diff --git a/02_functions/02_content.ipynb b/02_functions/02_content.ipynb
index 06f1765..5162e9c 100644
--- a/02_functions/02_content.ipynb
+++ b/02_functions/02_content.ipynb
@@ -1571,7 +1571,7 @@
}
},
"source": [
- "Packages are a generalization of modules, and we look at one in detail in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb). You may, however, already look at a [sample package ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/sample_package) in the repository, which is nothing but a folder with *.py* files in it.\n",
+ "Packages are a generalization of modules, and we look at one in detail in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb#Packages-vs.-Modules).\n",
"\n",
"As a further reading on modules and packages, we refer to the [official tutorial ](https://docs.python.org/3/tutorial/modules.html)."
]
diff --git a/11_classes/00_content.ipynb b/11_classes/00_content.ipynb
index 6852453..87a9bae 100644
--- a/11_classes/00_content.ipynb
+++ b/11_classes/00_content.ipynb
@@ -808,7 +808,7 @@
}
},
"source": [
- "Every instance comes with a special `.__class__` attribute that also references the corresponding class object."
+ "`v`'s semantic \"value\" is not so clear yet. We fix this in the next section."
]
},
{
@@ -819,41 +819,6 @@
"slide_type": "fragment"
}
},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "__main__.Vector"
- ]
- },
- "execution_count": 21,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "v.__class__"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "skip"
- }
- },
- "source": [
- "`v`'s semantic \"value\" is not so clear yet. We fix this in the next section."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {
- "slideshow": {
- "slide_type": "fragment"
- }
- },
"outputs": [
{
"data": {
@@ -861,7 +826,7 @@
"<__main__.Vector at 0x7f9b9416d760>"
]
},
- "execution_count": 22,
+ "execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
@@ -885,7 +850,7 @@
},
{
"cell_type": "code",
- "execution_count": 23,
+ "execution_count": 22,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -910,7 +875,7 @@
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": 23,
"metadata": {
"slideshow": {
"slide_type": "fragment"
@@ -946,7 +911,7 @@
},
{
"cell_type": "code",
- "execution_count": 25,
+ "execution_count": 24,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -983,7 +948,7 @@
},
{
"cell_type": "code",
- "execution_count": 26,
+ "execution_count": 25,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -996,7 +961,7 @@
"[1.0, 2.0, 3.0]"
]
},
- "execution_count": 26,
+ "execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
@@ -1031,7 +996,7 @@
},
{
"cell_type": "code",
- "execution_count": 27,
+ "execution_count": 26,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -1042,6 +1007,30 @@
"x = 1, 2, 3"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(1, 2, 3)"
+ ]
+ },
+ "execution_count": 27,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "x"
+ ]
+ },
{
"cell_type": "code",
"execution_count": 28,
@@ -1062,30 +1051,6 @@
"output_type": "execute_result"
}
],
- "source": [
- "x"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {
- "slideshow": {
- "slide_type": "fragment"
- }
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "(1, 2, 3)"
- ]
- },
- "execution_count": 29,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
"source": [
"(1, 2, 3)"
]
@@ -1103,7 +1068,7 @@
},
{
"cell_type": "code",
- "execution_count": 30,
+ "execution_count": 29,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -1144,7 +1109,7 @@
},
{
"cell_type": "code",
- "execution_count": 31,
+ "execution_count": 30,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -1157,7 +1122,7 @@
},
{
"cell_type": "code",
- "execution_count": 32,
+ "execution_count": 31,
"metadata": {
"slideshow": {
"slide_type": "fragment"
@@ -1170,7 +1135,7 @@
"Vector((1.000, 2.000, 3.000))"
]
},
- "execution_count": 32,
+ "execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
@@ -1192,7 +1157,7 @@
},
{
"cell_type": "code",
- "execution_count": 33,
+ "execution_count": 32,
"metadata": {
"slideshow": {
"slide_type": "skip"
@@ -1205,7 +1170,7 @@
"Vector((1.000, 2.000, 3.000))"
]
},
- "execution_count": 33,
+ "execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
@@ -1227,7 +1192,7 @@
},
{
"cell_type": "code",
- "execution_count": 34,
+ "execution_count": 33,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -1240,7 +1205,7 @@
"'Vector((1.000, 2.000, 3.000))'"
]
},
- "execution_count": 34,
+ "execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
@@ -1266,7 +1231,7 @@
},
{
"cell_type": "code",
- "execution_count": 35,
+ "execution_count": 34,
"metadata": {
"slideshow": {
"slide_type": "fragment"
@@ -1279,7 +1244,7 @@
"'Vector(1.0, ..., 3.0)[3]'"
]
},
- "execution_count": 35,
+ "execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
@@ -1301,7 +1266,7 @@
},
{
"cell_type": "code",
- "execution_count": 36,
+ "execution_count": 35,
"metadata": {
"slideshow": {
"slide_type": "fragment"
@@ -1342,14 +1307,14 @@
}
},
"source": [
- "Below is a first implementation of the `Matrix` class that stores the entries internally as a `list` of `list`s.\n",
+ "Below is a first implementation of the `Matrix` class that stores the `._entries` internally as a `list` of `list`s.\n",
"\n",
"The `.__init__()` method ensures that all the rows come with the same number of columns. Again, we do not allow `Matrix` instances without any entries."
]
},
{
"cell_type": "code",
- "execution_count": 37,
+ "execution_count": 36,
"metadata": {
"code_folding": [],
"slideshow": {
@@ -1391,7 +1356,7 @@
},
{
"cell_type": "code",
- "execution_count": 38,
+ "execution_count": 37,
"metadata": {
"slideshow": {
"slide_type": "skip"
@@ -1404,7 +1369,7 @@
"94113690738160"
]
},
- "execution_count": 38,
+ "execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
@@ -1413,6 +1378,30 @@
"id(Matrix)"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "type"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "type(Matrix)"
+ ]
+ },
{
"cell_type": "code",
"execution_count": 39,
@@ -1421,30 +1410,6 @@
"slide_type": "skip"
}
},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "type"
- ]
- },
- "execution_count": 39,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "type(Matrix)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {
- "slideshow": {
- "slide_type": "skip"
- }
- },
"outputs": [
{
"data": {
@@ -1452,7 +1417,7 @@
"__main__.Matrix"
]
},
- "execution_count": 40,
+ "execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
@@ -1474,7 +1439,7 @@
},
{
"cell_type": "code",
- "execution_count": 41,
+ "execution_count": 40,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -1487,7 +1452,7 @@
},
{
"cell_type": "code",
- "execution_count": 42,
+ "execution_count": 41,
"metadata": {
"slideshow": {
"slide_type": "skip"
@@ -1500,7 +1465,7 @@
"140306180401856"
]
},
- "execution_count": 42,
+ "execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
@@ -1511,7 +1476,7 @@
},
{
"cell_type": "code",
- "execution_count": 43,
+ "execution_count": 42,
"metadata": {
"slideshow": {
"slide_type": "fragment"
@@ -1524,7 +1489,7 @@
"__main__.Matrix"
]
},
- "execution_count": 43,
+ "execution_count": 42,
"metadata": {},
"output_type": "execute_result"
}
@@ -1546,7 +1511,7 @@
},
{
"cell_type": "code",
- "execution_count": 44,
+ "execution_count": 43,
"metadata": {
"slideshow": {
"slide_type": "fragment"
@@ -1559,7 +1524,7 @@
"Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
]
},
- "execution_count": 44,
+ "execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
@@ -1570,7 +1535,7 @@
},
{
"cell_type": "code",
- "execution_count": 45,
+ "execution_count": 44,
"metadata": {
"slideshow": {
"slide_type": "fragment"
@@ -1602,7 +1567,7 @@
},
{
"cell_type": "code",
- "execution_count": 46,
+ "execution_count": 45,
"metadata": {
"slideshow": {
"slide_type": "skip"
@@ -1628,7 +1593,7 @@
},
{
"cell_type": "code",
- "execution_count": 47,
+ "execution_count": 46,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -1673,12 +1638,14 @@
"source": [
"The methods we have seen so far are all **instance methods**. The characteristic idea behind an instance method is that the behavior it provides either depends on the state of a concrete instance or mutates it. In other words, an instance method *always* works with attributes on the `self` argument. If a method does *not* need access to `self` to do its job, it is conceptually *not* an instance method and we should probably convert it into another kind of method as shown below.\n",
"\n",
- "An example of an instance method from linear algebra is the `.transpose()` method below that switches the rows and columns of an *existing* `Matrix` instance and returns a *new* `Matrix` instance based off that. It is implemented by passing the *iterator* created with the [zip() ](https://docs.python.org/3/library/functions.html#zip) built-in as the `data` argument to the `Matrix` constructor: The expression `zip(*self._entries)` may be a bit hard to understand because of the involved unpacking but simply flips a `Vector`'s rows and columns. The built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor within the `.__init__()` method then materializes the iterator into the `._entries`. Without a concrete `Matrix`'s rows and columns, `.transpose()` does not make sense, conceptually speaking."
+ "An example of an instance method from linear algebra is the `.transpose()` method below that switches the rows and columns of an *existing* `Matrix` instance and returns a *new* `Matrix` instance based off that. It is implemented by passing the *iterator* created with the [zip() ](https://docs.python.org/3/library/functions.html#zip) built-in as the `data` argument to the `Matrix` constructor: The expression `zip(*self._entries)` may be a bit hard to understand because of the involved unpacking but simply flips a `Matrix`'s rows and columns. The built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor within the `.__init__()` method then materializes the iterator into the `._entries` attribute. Without a concrete `Matrix`'s rows and columns, `.transpose()` does not make sense, conceptually speaking.\n",
+ "\n",
+ "Also, we see that it is ok to reference a class from within one of its methods. While this seems trivial to some readers, others may find this confusing. The final versions of the `Vector` and `Matrix` classes in the [fourth part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb#The-final-Vector-and-Matrix-Classes) of this chapter show how this \"hard coded\" redundancy can be avoided."
]
},
{
"cell_type": "code",
- "execution_count": 48,
+ "execution_count": 47,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -1713,7 +1680,7 @@
},
{
"cell_type": "code",
- "execution_count": 49,
+ "execution_count": 48,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -1724,6 +1691,30 @@
"m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
+ ]
+ },
+ "execution_count": 49,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "m"
+ ]
+ },
{
"cell_type": "code",
"execution_count": 50,
@@ -1732,30 +1723,6 @@
"slide_type": "fragment"
}
},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
- ]
- },
- "execution_count": 50,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "m"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 51,
- "metadata": {
- "slideshow": {
- "slide_type": "fragment"
- }
- },
"outputs": [
{
"data": {
@@ -1763,7 +1730,7 @@
"Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))"
]
},
- "execution_count": 51,
+ "execution_count": 50,
"metadata": {},
"output_type": "execute_result"
}
@@ -1785,7 +1752,7 @@
},
{
"cell_type": "code",
- "execution_count": 52,
+ "execution_count": 51,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -1796,6 +1763,30 @@
"n = m.transpose().transpose()"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 52,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
+ ]
+ },
+ "execution_count": 52,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": 53,
@@ -1808,7 +1799,7 @@
{
"data": {
"text/plain": [
- "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
+ "False"
]
},
"execution_count": 53,
@@ -1817,7 +1808,18 @@
}
],
"source": [
- "n"
+ "m is n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "Unintuitively, the comparison operator `==` returns a wrong result as `m` and `n` have `_entries` attributes that compare equal. We fix this in the \"*Operator Overloading*\" section later in this chapter."
]
},
{
@@ -1840,41 +1842,6 @@
"output_type": "execute_result"
}
],
- "source": [
- "m is n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "skip"
- }
- },
- "source": [
- "Unintuitively, the comparison operator `==` returns a wrong result as `m` and `n` have `_entries` attributes that compare equal. We fix this in the \"*Operator Overloading*\" section later in this chapter."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "metadata": {
- "slideshow": {
- "slide_type": "fragment"
- }
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "False"
- ]
- },
- "execution_count": 55,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
"source": [
"m == n"
]
@@ -1894,7 +1861,7 @@
},
{
"cell_type": "code",
- "execution_count": 56,
+ "execution_count": 55,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -1933,7 +1900,7 @@
},
{
"cell_type": "code",
- "execution_count": 57,
+ "execution_count": 56,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -1946,7 +1913,7 @@
},
{
"cell_type": "code",
- "execution_count": 58,
+ "execution_count": 57,
"metadata": {
"slideshow": {
"slide_type": "fragment"
@@ -1959,7 +1926,7 @@
"Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
]
},
- "execution_count": 58,
+ "execution_count": 57,
"metadata": {},
"output_type": "execute_result"
}
@@ -2007,7 +1974,7 @@
},
{
"cell_type": "code",
- "execution_count": 59,
+ "execution_count": 58,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -2051,7 +2018,7 @@
},
{
"cell_type": "code",
- "execution_count": 60,
+ "execution_count": 59,
"metadata": {
"slideshow": {
"slide_type": "slide"
@@ -2064,7 +2031,7 @@
},
{
"cell_type": "code",
- "execution_count": 61,
+ "execution_count": 60,
"metadata": {
"slideshow": {
"slide_type": "fragment"
@@ -2077,7 +2044,7 @@
"(2, 3)"
]
},
- "execution_count": 61,
+ "execution_count": 60,
"metadata": {},
"output_type": "execute_result"
}
@@ -2099,7 +2066,7 @@
},
{
"cell_type": "code",
- "execution_count": 62,
+ "execution_count": 61,
"metadata": {
"slideshow": {
"slide_type": "fragment"
diff --git a/11_classes/02_content.ipynb b/11_classes/02_content.ipynb
index 520339f..644fad2 100644
--- a/11_classes/02_content.ipynb
+++ b/11_classes/02_content.ipynb
@@ -1577,7 +1577,7 @@
}
},
"source": [
- "After this discussion of mutable `Vector` and `Matrix` classes, we continue with immutable classes in the rest of this chapter."
+ "After this discussion of mutable `Vector` and `Matrix` classes, we continue with immutable implementations in the rest of this chapter. To lower the chance that we accidently design parts of our classes to be mutable, we replace the built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor with [tuple() ](https://docs.python.org/3/library/functions.html#func-tuple) in the `.__init__()` methods. As we learn in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Tuples-are-like-%22Immutable-Lists%22), `tuple`s are like immutable `list`s."
]
},
{
diff --git a/11_classes/03_content.ipynb b/11_classes/03_content.ipynb
index 9dc08a2..def4be5 100644
--- a/11_classes/03_content.ipynb
+++ b/11_classes/03_content.ipynb
@@ -82,7 +82,7 @@
"class Vector:\n",
"\n",
" def __init__(self, data):\n",
- " self._entries = list(float(x) for x in data)\n",
+ " self._entries = tuple(float(x) for x in data)\n",
" # ...\n",
"\n",
" def __repr__(self):\n",
@@ -111,7 +111,7 @@
"class Matrix:\n",
"\n",
" def __init__(self, data):\n",
- " self._entries = list(list(float(x) for x in r) for r in data)\n",
+ " self._entries = tuple(tuple(float(x) for x in r) for r in data)\n",
" # ...\n",
"\n",
" def __repr__(self):\n",
@@ -678,7 +678,7 @@
"class Vector:\n",
"\n",
" def __init__(self, data):\n",
- " self._entries = list(float(x) for x in data)\n",
+ " self._entries = tuple(float(x) for x in data)\n",
" # ...\n",
"\n",
" def __repr__(self):\n",
@@ -692,7 +692,7 @@
" return iter(self._entries)\n",
"\n",
" def __add__(self, other):\n",
- " if isinstance(other, self.__class__): # vector addition\n",
+ " if isinstance(other, Vector): # vector addition\n",
" if len(self) != len(other):\n",
" raise ValueError(\"vectors must be of the same length\")\n",
" return Vector(x + y for (x, y) in zip(self, other))\n",
@@ -704,7 +704,7 @@
" return self + other\n",
"\n",
" def __sub__(self, other):\n",
- " if isinstance(other, self.__class__): # vector subtraction\n",
+ " if isinstance(other, Vector): # vector subtraction\n",
" if len(self) != len(other):\n",
" raise ValueError(\"vectors must be of the same length\")\n",
" return Vector(x - y for (x, y) in zip(self, other))\n",
@@ -719,7 +719,7 @@
" return NotImplemented\n",
"\n",
" def __mul__(self, other):\n",
- " if isinstance(other, self.__class__): # dot product\n",
+ " if isinstance(other, Vector): # dot product\n",
" if len(self) != len(other):\n",
" raise ValueError(\"vectors must be of the same length\")\n",
" return sum(x * y for (x, y) in zip(self, other))\n",
@@ -1142,7 +1142,7 @@
"class Matrix:\n",
"\n",
" def __init__(self, data):\n",
- " self._entries = list(list(float(x) for x in r) for r in data)\n",
+ " self._entries = tuple(tuple(float(x) for x in r) for r in data)\n",
" # ...\n",
"\n",
" def __repr__(self):\n",
@@ -1169,7 +1169,7 @@
" return (self._entries[r][c] for r in range(self.n_rows) for c in range(self.n_cols))\n",
"\n",
" def __add__(self, other):\n",
- " if isinstance(other, self.__class__): # matrix addition\n",
+ " if isinstance(other, Matrix): # matrix addition\n",
" if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n",
" raise ValueError(\"matrices must have the same dimensions\")\n",
" return Matrix((s_col + o_col for (s_col, o_col) in zip(s_row, o_row))\n",
@@ -1184,7 +1184,7 @@
" return self + other\n",
"\n",
" def __sub__(self, other):\n",
- " if isinstance(other, self.__class__): # matrix subtraction\n",
+ " if isinstance(other, Matrix): # matrix subtraction\n",
" if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n",
" raise ValueError(\"matrices must have the same dimensions\")\n",
" return Matrix((s_col - o_col for (s_col, o_col) in zip(s_row, o_row))\n",
@@ -1211,7 +1211,7 @@
" return Matrix((x * other for x in r) for r in self._entries)\n",
" elif isinstance(other, Vector):\n",
" return self._matrix_multiply(other.as_matrix()).as_vector()\n",
- " elif isinstance(other, self.__class__):\n",
+ " elif isinstance(other, Matrix):\n",
" return self._matrix_multiply(other)\n",
" return NotImplemented\n",
"\n",
@@ -1720,7 +1720,7 @@
" zero_threshold = 1e-12\n",
"\n",
" def __init__(self, data):\n",
- " self._entries = list(float(x) for x in data)\n",
+ " self._entries = tuple(float(x) for x in data)\n",
" # ...\n",
"\n",
" def __repr__(self):\n",
@@ -1734,7 +1734,7 @@
" return iter(self._entries)\n",
"\n",
" def __eq__(self, other):\n",
- " if isinstance(other, self.__class__):\n",
+ " if isinstance(other, Vector):\n",
" if len(self) != len(other):\n",
" raise ValueError(\"vectors must be of the same length\")\n",
" for x, y in zip(self, other):\n",
@@ -1990,7 +1990,7 @@
"class Vector:\n",
"\n",
" def __init__(self, data):\n",
- " self._entries = list(float(x) for x in data)\n",
+ " self._entries = tuple(float(x) for x in data)\n",
" # ...\n",
"\n",
" def __repr__(self):\n",
diff --git a/11_classes/04_content.ipynb b/11_classes/04_content.ipynb
new file mode 100644
index 0000000..51b0818
--- /dev/null
+++ b/11_classes/04_content.ipynb
@@ -0,0 +1,2719 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/04_content.ipynb)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "# Chapter 11: Classes & Instances (continued)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "In this fourth part of the chapter, we finalize our `Vector` and `Matrix` classes. As both `class` definitions have become rather lengthy, we learn how we to organize them into a Python package and import them in this Jupyter notebook. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Packages vs. Modules"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb#Local-Modules-and-Packages), we introduce the concept of a Python module that is imported with the `import` statement. Essentially, a **module** is a single plain text \\*.py file on disk that contains Python code (e.g., [*sample_module.py* ](https://github.com/webartifex/intro-to-python/blob/develop/02_functions/sample_module.py) in [Chapter 2's folder ](https://github.com/webartifex/intro-to-python/tree/develop/02_functions)).\n",
+ "\n",
+ "Conceptually, a **package** is a generalization of a module whose code is split across several \\*.py to achieve a better organization of the individual parts. The \\*.py files are stored within a folder (e.g., [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/sample_package) in [Chapter 11's folder ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes)). In addition to that, a \"*\\_\\_init\\_\\_.py*\" file that may be empty must be put inside the folder. The latter is what the Python interpreter looks for to decide if a folder is a package or not.\n",
+ "\n",
+ "Let's look at an example with the final version of our `Vector` and `Matrix` classes.\n",
+ "\n",
+ "`!pwd` shows the location of this Jupyter notebook on the computer you are running [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) on: It is the local equivalent of [Chapter 11's folder ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes) in this book's [GitHub repository ](https://github.com/webartifex/intro-to-python)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "/home/webartifex/repos/intro-to-python/11_classes\n"
+ ]
+ }
+ ],
+ "source": [
+ "!pwd"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`!ls` lists all the files and folders in the current location: These are Chapter 11's Jupyter notebooks (i.e., the \\*.ipynb files) and the [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/sample_package) folder. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "00_content.ipynb 02_content.ipynb 04_content.ipynb\n",
+ "01_exercises.ipynb 03_content.ipynb sample_package\n"
+ ]
+ }
+ ],
+ "source": [
+ "!ls"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If we run `!ls` with the `sample_package` folder as the argument, we see the folder's contents: Four \\*.py files. Alternatively, you can use [JupyterLab' File Browser](https://jupyterlab.readthedocs.io/en/stable/user/interface.html?highlight=file%20browser#left-sidebar) on the left to navigate into the package."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "__init__.py matrix.py\tutils.py vector.py\n"
+ ]
+ }
+ ],
+ "source": [
+ "!ls sample_package"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The package is organized such that the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) modules each define just one class, `Matrix` and `Vector`. That is intentional as both classes consist of several hundred lines of code and comments.\n",
+ "\n",
+ "The [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py) module contains code that is shared by both classes. Such code snippets are commonly called \"utilities\" or \"helpers,\" which explains the module's name.\n",
+ "\n",
+ "Finally, the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file contains mostly meta information and defines what objects should be importable from the package's top level.\n",
+ "\n",
+ "With the `import` statement, we can import the entire package just as we would import a module from the [standard library ](https://docs.python.org/3/library/index.html)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import sample_package as pkg"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The above cell runs the code in the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file from top to bottom, which in turn runs the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py), [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py), and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) modules (cf., look at the `import` statements in the four \\*.py files to get the idea). As both [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) depend on each other (i.e., the `Matrix` class needs the `Vector` class to work and vice versa), understanding the order in that the modules are executed is not trivial. Without going into detail, we mention that Python guarantees that each \\*.py file is run only once and figures out the order on its own. If Python is unable to do that, for example, due to unresolvable cirular imports, it aborts with an `ImportError`.\n",
+ "\n",
+ "Below, `pkg` is an object of type `module` ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "pkg"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "module"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "type(pkg)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "... and we use the built-in [dir() ](https://docs.python.org/3/library/functions.html#dir) function to check what attributes `pkg` comes with."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['Matrix',\n",
+ " 'Vector',\n",
+ " '__all__',\n",
+ " '__author__',\n",
+ " '__builtins__',\n",
+ " '__cached__',\n",
+ " '__doc__',\n",
+ " '__file__',\n",
+ " '__loader__',\n",
+ " '__name__',\n",
+ " '__package__',\n",
+ " '__path__',\n",
+ " '__spec__',\n",
+ " '__version__',\n",
+ " 'matrix',\n",
+ " 'utils',\n",
+ " 'vector']"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "dir(pkg)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The package's meta information and documentation are automatically parsed from the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Help on package linear_algebra_tools:\n",
+ "\n",
+ "NAME\n",
+ " linear_algebra_tools - This package provides linear algebra functionalities.\n",
+ "\n",
+ "DESCRIPTION\n",
+ " The package is split into three modules:\n",
+ " - matrix: defines the Matrix class\n",
+ " - vector: defines the Vector class\n",
+ " - utils: defines the norm() function that is shared by Matrix and Vector\n",
+ " and package-wide constants\n",
+ " \n",
+ " The classes implement arithmetic operations involving vectors and matrices.\n",
+ " \n",
+ " See the docstrings in the modules and classes for further info.\n",
+ "\n",
+ "PACKAGE CONTENTS\n",
+ " matrix\n",
+ " utils\n",
+ " vector\n",
+ "\n",
+ "CLASSES\n",
+ " builtins.object\n",
+ " sample_package.matrix.Matrix\n",
+ " sample_package.vector.Vector\n",
+ " \n",
+ " class Matrix(builtins.object)\n",
+ " | Matrix(data)\n",
+ " | \n",
+ " | An m-by-n-dimensional matrix from linear algebra.\n",
+ " | \n",
+ " | All entries are converted to floats, or whatever is set in the typing attribute.\n",
+ " | \n",
+ " | Attributes:\n",
+ " | storage (callable): data type used to store the entries internally;\n",
+ " | defaults to tuple\n",
+ " | typing (callable): type casting applied to all entries upon creation;\n",
+ " | defaults to float\n",
+ " | vector_cls (vector.Vector): a reference to the Vector class to work with\n",
+ " | zero_threshold (float): max. tolerance when comparing an entry to zero;\n",
+ " | defaults to 1e-12\n",
+ " | \n",
+ " | Methods defined here:\n",
+ " | \n",
+ " | __abs__(self)\n",
+ " | The Frobenius norm of a Matrix.\n",
+ " | \n",
+ " | __add__(self, other)\n",
+ " | Handle `self + other` and `other + self`.\n",
+ " | \n",
+ " | This may be either matrix addition or broadcasting addition.\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Matrix([(1, 2), (3, 4)]) + Matrix([(2, 3), (4, 5)])\n",
+ " | Matrix(((3.000, 5.000,), (7.000, 9.000,)))\n",
+ " | \n",
+ " | >>> Matrix([(1, 2), (3, 4)]) + 5\n",
+ " | Matrix(((6.000, 7.000,), (8.000, 9.000,)))\n",
+ " | \n",
+ " | >>> 10 + Matrix([(1, 2), (3, 4)])\n",
+ " | Matrix(((11.000, 12.000,), (13.000, 14.000,)))\n",
+ " | \n",
+ " | __bool__(self)\n",
+ " | A Matrix is truthy if its Frobenius norm is strictly positive.\n",
+ " | \n",
+ " | __eq__(self, other)\n",
+ " | Handle `self == other`.\n",
+ " | \n",
+ " | Compare two Matrix instances for equality.\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Matrix([(1, 2), (3, 4)]) == Matrix([(1, 2), (3, 4)])\n",
+ " | True\n",
+ " | \n",
+ " | >>> Matrix([(1, 2), (3, 4)]) == Matrix([(5, 6), (7, 8)])\n",
+ " | False\n",
+ " | \n",
+ " | __float__(self)\n",
+ " | Cast a Matrix as a scalar.\n",
+ " | \n",
+ " | Returns:\n",
+ " | scalar (float)\n",
+ " | \n",
+ " | Raises:\n",
+ " | RuntimeError: if the Matrix has more than one entry\n",
+ " | \n",
+ " | __getitem__(self, index)\n",
+ " | Obtain an individual entry of a Matrix.\n",
+ " | \n",
+ " | Args:\n",
+ " | index (int / tuple of int's): if index is an integer,\n",
+ " | the Matrix is viewed as a sequence in row-major order;\n",
+ " | if index is a tuple of integers, the first one refers to\n",
+ " | the row and the second one to the column of the entry\n",
+ " | \n",
+ " | Returns:\n",
+ " | entry (Matrix.typing)\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> m = Matrix([(1, 2), (3, 4)])\n",
+ " | >>> m[0]\n",
+ " | 1.0\n",
+ " | >>> m[-1]\n",
+ " | 4.0\n",
+ " | >>> m[0, 1]\n",
+ " | 2.0\n",
+ " | \n",
+ " | __init__(self, data)\n",
+ " | Create a new matrix.\n",
+ " | \n",
+ " | Args:\n",
+ " | data (sequence of sequences): the matrix's entries;\n",
+ " | viewed as a sequence of the matrix's rows (i.e., row-major order);\n",
+ " | use the .from_columns() class method if the data come as a sequence\n",
+ " | of the matrix's columns (i.e., column-major order)\n",
+ " | \n",
+ " | Raises:\n",
+ " | ValueError:\n",
+ " | - if no entries are provided\n",
+ " | - if the number of columns is inconsistent across the rows\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Matrix([(1, 2), (3, 4)])\n",
+ " | Matrix(((1.000, 2.000,), (3.000, 4.000,)))\n",
+ " | \n",
+ " | __iter__(self)\n",
+ " | Loop over a Matrix's entries.\n",
+ " | \n",
+ " | See .entries() for more customization options.\n",
+ " | \n",
+ " | __len__(self)\n",
+ " | Number of entries in a Matrix.\n",
+ " | \n",
+ " | __mul__(self, other)\n",
+ " | Handle `self * other` and `other * self`.\n",
+ " | \n",
+ " | This may be either scalar multiplication, matrix-vector multiplication,\n",
+ " | vector-matrix multiplication, or matrix-matrix multiplication.\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Matrix([(1, 2), (3, 4)]) * Matrix([(1, 2), (3, 4)])\n",
+ " | Matrix(((7.000, 10.000,), (15.000, 22.000,)))\n",
+ " | \n",
+ " | >>> 2 * Matrix([(1, 2), (3, 4)])\n",
+ " | Matrix(((2.000, 4.000,), (6.000, 8.000,)))\n",
+ " | \n",
+ " | >>> Matrix([(1, 2), (3, 4)]) * 3\n",
+ " | Matrix(((3.000, 6.000,), (9.000, 12.000,)))\n",
+ " | \n",
+ " | Matrix-vector and vector-matrix multiplication are not commutative.\n",
+ " | \n",
+ " | >>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6])\n",
+ " | Vector((17.000, 39.000))\n",
+ " | \n",
+ " | >>> Vector([5, 6]) * Matrix([(1, 2), (3, 4)])\n",
+ " | Vector((23.000, 34.000))\n",
+ " | \n",
+ " | __neg__(self)\n",
+ " | Handle `-self`.\n",
+ " | \n",
+ " | Negate all entries of a Matrix.\n",
+ " | \n",
+ " | __pos__(self)\n",
+ " | Handle `+self`.\n",
+ " | \n",
+ " | This is simply an identity operator returning the Matrix itself.\n",
+ " | \n",
+ " | __radd__(self, other)\n",
+ " | See docstring for .__add__().\n",
+ " | \n",
+ " | __repr__(self)\n",
+ " | Text representation of a Matrix.\n",
+ " | \n",
+ " | __reversed__(self)\n",
+ " | Loop over a Matrix's entries in reverse order.\n",
+ " | \n",
+ " | See .entries() for more customization options.\n",
+ " | \n",
+ " | __rmul__(self, other)\n",
+ " | See docstring for .__mul__().\n",
+ " | \n",
+ " | __rsub__(self, other)\n",
+ " | See docstring for .__sub__().\n",
+ " | \n",
+ " | __str__(self)\n",
+ " | Human-readable text representation of a Matrix.\n",
+ " | \n",
+ " | __sub__(self, other)\n",
+ " | Handle `self - other` and `other - self`.\n",
+ " | \n",
+ " | This may be either matrix subtraction or broadcasting subtraction.\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Matrix([(2, 3), (4, 5)]) - Matrix([(1, 2), (3, 4)])\n",
+ " | Matrix(((1.000, 1.000,), (1.000, 1.000,)))\n",
+ " | \n",
+ " | >>> Matrix([(1, 2), (3, 4)]) - 1\n",
+ " | Matrix(((0.000, 1.000,), (2.000, 3.000,)))\n",
+ " | \n",
+ " | >>> 10 - Matrix([(1, 2), (3, 4)])\n",
+ " | Matrix(((9.000, 8.000,), (7.000, 6.000,)))\n",
+ " | \n",
+ " | __truediv__(self, other)\n",
+ " | Handle `self / other`.\n",
+ " | \n",
+ " | Divide a Matrix by a scalar.\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Matrix([(1, 2), (3, 4)]) / 4\n",
+ " | Matrix(((0.250, 0.500,), (0.750, 1.000,)))\n",
+ " | \n",
+ " | as_vector(self)\n",
+ " | Get a Vector representation of a Matrix.\n",
+ " | \n",
+ " | Returns:\n",
+ " | vector (vector.Vector)\n",
+ " | \n",
+ " | Raises:\n",
+ " | RuntimeError: if one of the two dimensions, .n_rows or .n_cols, is not 1\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Matrix([(1, 2, 3)]).as_vector()\n",
+ " | Vector((1.000, 2.000, 3.000))\n",
+ " | \n",
+ " | cols(self)\n",
+ " | Loop over a Matrix's columns.\n",
+ " | \n",
+ " | Returns:\n",
+ " | columns (generator): produces a Matrix's columns as Vectors\n",
+ " | \n",
+ " | entries(self, *, reverse=False, row_major=True)\n",
+ " | Loop over a Matrix's entries.\n",
+ " | \n",
+ " | Args:\n",
+ " | reverse (bool): flag to loop backwards; defaults to False\n",
+ " | row_major (bool): flag to loop in row-major order; defaults to True\n",
+ " | \n",
+ " | Returns:\n",
+ " | entries (generator): produces a Matrix's entries\n",
+ " | \n",
+ " | rows(self)\n",
+ " | Loop over a Matrix's rows.\n",
+ " | \n",
+ " | Returns:\n",
+ " | rows (generator): produces a Matrix's rows as Vectors\n",
+ " | \n",
+ " | transpose(self)\n",
+ " | Switch the rows and columns of a Matrix.\n",
+ " | \n",
+ " | Returns:\n",
+ " | matrix (Matrix)\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> m = Matrix([(1, 2), (3, 4)])\n",
+ " | >>> m\n",
+ " | Matrix(((1.000, 2.000,), (3.000, 4.000,)))\n",
+ " | >>> m.transpose()\n",
+ " | Matrix(((1.000, 3.000,), (2.000, 4.000,)))\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Class methods defined here:\n",
+ " | \n",
+ " | from_columns(data) from builtins.type\n",
+ " | Create a new matrix.\n",
+ " | \n",
+ " | This is an alternative constructor for data provided in column-major order.\n",
+ " | \n",
+ " | Args:\n",
+ " | data (sequence of sequences): the matrix's entries;\n",
+ " | viewed as a sequence of the matrix's columns (i.e., column-major order);\n",
+ " | use the normal constructor method if the data come as a sequence\n",
+ " | of the matrix's rows (i.e., row-major order)\n",
+ " | \n",
+ " | Raises:\n",
+ " | ValueError:\n",
+ " | - if no entries are provided\n",
+ " | - if the number of rows is inconsistent across the columns\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Matrix.from_columns([(1, 2), (3, 4)])\n",
+ " | Matrix(((1.000, 3.000,), (2.000, 4.000,)))\n",
+ " | \n",
+ " | from_rows(data) from builtins.type\n",
+ " | See docstring for .__init__().\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Readonly properties defined here:\n",
+ " | \n",
+ " | n_cols\n",
+ " | Number of columns in a Matrix.\n",
+ " | \n",
+ " | n_rows\n",
+ " | Number of rows in a Matrix.\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Data descriptors defined here:\n",
+ " | \n",
+ " | __dict__\n",
+ " | dictionary for instance variables (if defined)\n",
+ " | \n",
+ " | __weakref__\n",
+ " | list of weak references to the object (if defined)\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Data and other attributes defined here:\n",
+ " | \n",
+ " | __hash__ = None\n",
+ " | \n",
+ " | storage = \n",
+ " | Built-in immutable sequence.\n",
+ " | \n",
+ " | If no argument is given, the constructor returns an empty tuple.\n",
+ " | If iterable is specified the tuple is initialized from iterable's items.\n",
+ " | \n",
+ " | If the argument is a tuple, the return value is the same object.\n",
+ " | \n",
+ " | typing = \n",
+ " | Convert a string or number to a floating point number, if possible.\n",
+ " | \n",
+ " | vector_cls = \n",
+ " | A one-dimensional vector from linear algebra.\n",
+ " | \n",
+ " | All entries are converted to floats, or whatever is set in the typing attribute.\n",
+ " | \n",
+ " | Attributes:\n",
+ " | matrix_cls (matrix.Matrix): a reference to the Matrix class to work with\n",
+ " | storage (callable): data type used to store the entries internally;\n",
+ " | defaults to tuple\n",
+ " | typing (callable): type casting applied to all entries upon creation;\n",
+ " | defaults to float\n",
+ " | zero_threshold (float): max. tolerance when comparing an entry to zero;\n",
+ " | defaults to 1e-12\n",
+ " | \n",
+ " | zero_threshold = 1e-12\n",
+ " \n",
+ " class Vector(builtins.object)\n",
+ " | Vector(data)\n",
+ " | \n",
+ " | A one-dimensional vector from linear algebra.\n",
+ " | \n",
+ " | All entries are converted to floats, or whatever is set in the typing attribute.\n",
+ " | \n",
+ " | Attributes:\n",
+ " | matrix_cls (matrix.Matrix): a reference to the Matrix class to work with\n",
+ " | storage (callable): data type used to store the entries internally;\n",
+ " | defaults to tuple\n",
+ " | typing (callable): type casting applied to all entries upon creation;\n",
+ " | defaults to float\n",
+ " | zero_threshold (float): max. tolerance when comparing an entry to zero;\n",
+ " | defaults to 1e-12\n",
+ " | \n",
+ " | Methods defined here:\n",
+ " | \n",
+ " | __abs__(self)\n",
+ " | The Euclidean norm of a vector.\n",
+ " | \n",
+ " | __add__(self, other)\n",
+ " | Handle `self + other` and `other + self`.\n",
+ " | \n",
+ " | This may be either vector addition or broadcasting addition.\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Vector([1, 2, 3]) + Vector([2, 3, 4])\n",
+ " | Vector((3.000, 5.000, 7.000))\n",
+ " | \n",
+ " | >>> Vector([1, 2, 3]) + 4\n",
+ " | Vector((5.000, 6.000, 7.000))\n",
+ " | \n",
+ " | >>> 10 + Vector([1, 2, 3])\n",
+ " | Vector((11.000, 12.000, 13.000))\n",
+ " | \n",
+ " | __bool__(self)\n",
+ " | A Vector is truthy if its Euclidean norm is strictly positive.\n",
+ " | \n",
+ " | __eq__(self, other)\n",
+ " | Handle `self == other`.\n",
+ " | \n",
+ " | Compare two Vectors for equality.\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Vector([1, 2, 3]) == Vector([1, 2, 3])\n",
+ " | True\n",
+ " | \n",
+ " | >>> Vector([1, 2, 3]) == Vector([4, 5, 6])\n",
+ " | False\n",
+ " | \n",
+ " | __float__(self)\n",
+ " | Cast a Vector as a scalar.\n",
+ " | \n",
+ " | Returns:\n",
+ " | scalar (float)\n",
+ " | \n",
+ " | Raises:\n",
+ " | RuntimeError: if the Vector has more than one entry\n",
+ " | \n",
+ " | __getitem__(self, index)\n",
+ " | Obtain an individual entry of a Vector.\n",
+ " | \n",
+ " | __init__(self, data)\n",
+ " | Create a new vector.\n",
+ " | \n",
+ " | Args:\n",
+ " | data (sequence): the vector's entries\n",
+ " | \n",
+ " | Raises:\n",
+ " | ValueError: if no entries are provided\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Vector([1, 2, 3])\n",
+ " | Vector((1.000, 2.000, 3.000))\n",
+ " | \n",
+ " | >>> Vector(range(3))\n",
+ " | Vector((0.000, 1.000, 2.000))\n",
+ " | \n",
+ " | __iter__(self)\n",
+ " | Loop over a Vector's entries.\n",
+ " | \n",
+ " | __len__(self)\n",
+ " | Number of entries in a Vector.\n",
+ " | \n",
+ " | __mul__(self, other)\n",
+ " | Handle `self * other` and `other * self`.\n",
+ " | \n",
+ " | This may be either the dot product of two vectors or scalar multiplication.\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Vector([1, 2, 3]) * Vector([2, 3, 4])\n",
+ " | 20.0\n",
+ " | \n",
+ " | >>> 2 * Vector([1, 2, 3])\n",
+ " | Vector((2.000, 4.000, 6.000))\n",
+ " | \n",
+ " | >>> Vector([1, 2, 3]) * 3\n",
+ " | Vector((3.000, 6.000, 9.000))\n",
+ " | \n",
+ " | __neg__(self)\n",
+ " | Handle `-self`.\n",
+ " | \n",
+ " | Negate all entries of a Vector.\n",
+ " | \n",
+ " | __pos__(self)\n",
+ " | Handle `+self`.\n",
+ " | \n",
+ " | This is simply an identity operator returning the Vector itself.\n",
+ " | \n",
+ " | __radd__(self, other)\n",
+ " | See docstring for .__add__().\n",
+ " | \n",
+ " | __repr__(self)\n",
+ " | Text representation of a Vector.\n",
+ " | \n",
+ " | __reversed__(self)\n",
+ " | Loop over a Vector's entries in reverse order.\n",
+ " | \n",
+ " | __rmul__(self, other)\n",
+ " | See docstring for .__mul__().\n",
+ " | \n",
+ " | __rsub__(self, other)\n",
+ " | See docstring for .__sub__().\n",
+ " | \n",
+ " | __str__(self)\n",
+ " | Human-readable text representation of a Vector.\n",
+ " | \n",
+ " | __sub__(self, other)\n",
+ " | Handle `self - other` and `other - self`.\n",
+ " | \n",
+ " | This may be either vector subtraction or broadcasting subtraction.\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Vector([7, 8, 9]) - Vector([1, 2, 3])\n",
+ " | Vector((6.000, 6.000, 6.000))\n",
+ " | \n",
+ " | >>> Vector([1, 2, 3]) - 1\n",
+ " | Vector((0.000, 1.000, 2.000))\n",
+ " | \n",
+ " | >>> 10 - Vector([1, 2, 3])\n",
+ " | Vector((9.000, 8.000, 7.000))\n",
+ " | \n",
+ " | __truediv__(self, other)\n",
+ " | Handle `self / other`.\n",
+ " | \n",
+ " | Divide a Vector by a scalar.\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> Vector([9, 6, 12]) / 3\n",
+ " | Vector((3.000, 2.000, 4.000))\n",
+ " | \n",
+ " | as_matrix(self, *, column=True)\n",
+ " | Get a Matrix representation of a Vector.\n",
+ " | \n",
+ " | Args:\n",
+ " | column (bool): if the vector is interpreted as a\n",
+ " | column vector or a row vector; defaults to True\n",
+ " | \n",
+ " | Returns:\n",
+ " | matrix (matrix.Matrix)\n",
+ " | \n",
+ " | Example Usage:\n",
+ " | >>> v = Vector([1, 2, 3])\n",
+ " | >>> v.as_matrix()\n",
+ " | Matrix(((1.000,), (2.000,), (3.000,)))\n",
+ " | >>> v.as_matrix(column=False)\n",
+ " | Matrix(((1.000, 2.000, 3.000,)))\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Data descriptors defined here:\n",
+ " | \n",
+ " | __dict__\n",
+ " | dictionary for instance variables (if defined)\n",
+ " | \n",
+ " | __weakref__\n",
+ " | list of weak references to the object (if defined)\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Data and other attributes defined here:\n",
+ " | \n",
+ " | __hash__ = None\n",
+ " | \n",
+ " | matrix_cls = \n",
+ " | An m-by-n-dimensional matrix from linear algebra.\n",
+ " | \n",
+ " | All entries are converted to floats, or whatever is set in the typing attribute.\n",
+ " | \n",
+ " | Attributes:\n",
+ " | storage (callable): data type used to store the entries internally;\n",
+ " | defaults to tuple\n",
+ " | typing (callable): type casting applied to all entries upon creation;\n",
+ " | defaults to float\n",
+ " | vector_cls (vector.Vector): a reference to the Vector class to work with\n",
+ " | zero_threshold (float): max. tolerance when comparing an entry to zero;\n",
+ " | defaults to 1e-12\n",
+ " | \n",
+ " | storage = \n",
+ " | Built-in immutable sequence.\n",
+ " | \n",
+ " | If no argument is given, the constructor returns an empty tuple.\n",
+ " | If iterable is specified the tuple is initialized from iterable's items.\n",
+ " | \n",
+ " | If the argument is a tuple, the return value is the same object.\n",
+ " | \n",
+ " | typing = \n",
+ " | Convert a string or number to a floating point number, if possible.\n",
+ " | \n",
+ " | zero_threshold = 1e-12\n",
+ "\n",
+ "DATA\n",
+ " __all__ = ['Matrix', 'Vector']\n",
+ "\n",
+ "VERSION\n",
+ " 0.1.0\n",
+ "\n",
+ "AUTHOR\n",
+ " Alexander Hess\n",
+ "\n",
+ "FILE\n",
+ " /home/webartifex/repos/intro-to-python/11_classes/sample_package/__init__.py\n",
+ "\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "help(pkg)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The meta information could also be accessed separately and individually."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'linear_algebra_tools'"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "pkg.__name__"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'0.1.0'"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "pkg.__version__ # follows the semantic versioning format"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We create `Vector` and `Matrix` instances in the usual way by calling the `Vector` and `Matrix` classes from the package's top level."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Vector((1.000, 2.000, 3.000))"
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "pkg.Vector([1, 2, 3])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "pkg.Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A common practice by package authors is to put all the objects on the package's top level that they want the package users to work with directly. That is achieved via the `import` statements in the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file.\n",
+ "\n",
+ "However, users can always reach into a package and work with its internals.\n",
+ "\n",
+ "For example, the `Vector` and `Matrix` classes are also available via their **qualified name** (cf., [PEP 3155 ](https://www.python.org/dev/peps/pep-3155/)): First, we access the `vector` and `matrix` modules on `pkg`, and then the `Vector` and `Matrix` classes on the modules."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "sample_package.vector.Vector"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "pkg.vector.Vector"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "sample_package.matrix.Matrix"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "pkg.matrix.Matrix"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Also, let's import the [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py) module with the `norm()` function into the global scope. As this function is integrated into the `Vector.__abs__()` and `Matrix.__abs__()` methods, there is actually no need to work with it explicitly."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from sample_package import utils"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Help on function norm in module sample_package.utils:\n",
+ "\n",
+ "norm(vec_or_mat)\n",
+ " Calculate the Frobenius or Euclidean norm of a matrix or vector.\n",
+ " \n",
+ " Find more infos here: https://en.wikipedia.org/wiki/Matrix_norm#Frobenius_norm\n",
+ " \n",
+ " Args:\n",
+ " vec_or_mat (Vector / Matrix): object whose entries are squared and summed up\n",
+ " \n",
+ " Returns:\n",
+ " norm (float)\n",
+ " \n",
+ " Example Usage:\n",
+ " As Vector and Matrix objects are by design non-empty sequences,\n",
+ " norm() may be called, for example, with `[3, 4]` as the argument:\n",
+ " >>> norm([3, 4])\n",
+ " 5.0\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "help(utils.norm)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Many tutorials on the internet begin by importing \"everything\" from a package into the global scope with `from ... import *`.\n",
+ "\n",
+ "That is commonly considered a *bad* practice as it may overwrite already existing variables. However, if the package's [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file defines an `__all__` attribute, a `list` with all the names to be \"exported,\" the **star import** is safe to be used, in particular, in *interactive* sessions like Jupyter notebooks. We emphasize that the star import should *not* be used *within* packages and modules as then it is not directly evident from a name where the corresponding object is defined.\n",
+ "\n",
+ "For more best practices regarding importing we refer to, among others, [Google's Python Style Guide](https://google.github.io/styleguide/pyguide.html#22-imports).\n",
+ "\n",
+ "The following `import` statement makes the `Vector` and `Matrix` classes available in the global scope."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from sample_package import *"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "sample_package.vector.Vector"
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Vector"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "sample_package.matrix.Matrix"
+ ]
+ },
+ "execution_count": 19,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Matrix"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "For further information on modules and packages, we refer to the [official tutorial ](https://docs.python.org/3/tutorial/modules.html)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## The final `Vector` and `Matrix` Classes"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The final implementations of the `Vector` and `Matrix` classes are in the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) files: They integrate all of the functionalities introduced in this chapter. In addition, the code is cleaned up and fully documented, including examples of common usages.\n",
+ "\n",
+ "We strongly suggest the eager student go over the files in the [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/sample_package) in detail at some point to understand what well-written and (re-)usable code looks like."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "v = Vector([1, 2, 3])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Vector((1.000, 2.000, 3.000))"
+ ]
+ },
+ "execution_count": 21,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "v"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "sample_package.vector.Vector"
+ ]
+ },
+ "execution_count": 22,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "type(v)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "m"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "sample_package.matrix.Matrix"
+ ]
+ },
+ "execution_count": 25,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "type(m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "Furthermore, the classes are designed for easier maintenence in the long-run.\n",
+ "\n",
+ "For example, the `Matrix/Vector.storage` and `Matrix/Vector.typing` class attributes replace the \"hard coded\" [tuple() ](https://docs.python.org/3/library/functions.html#func-tuple) and [float() ](https://docs.python.org/3/library/functions.html#float) built-ins in the `.__init__()` methods: As `self.storage` and `self.typing` are not defined on the *instances*, Python automatically looks them up on the *classes*."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "tuple"
+ ]
+ },
+ "execution_count": 26,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Vector.storage"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "float"
+ ]
+ },
+ "execution_count": 27,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Vector.typing"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\u001b[0;31mSignature:\u001b[0m \u001b[0mVector\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mSource:\u001b[0m \n",
+ " \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Create a new vector.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Args:\u001b[0m\n",
+ "\u001b[0;34m data (sequence): the vector's entries\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Raises:\u001b[0m\n",
+ "\u001b[0;34m ValueError: if no entries are provided\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Example Usage:\u001b[0m\n",
+ "\u001b[0;34m >>> Vector([1, 2, 3])\u001b[0m\n",
+ "\u001b[0;34m Vector((1.000, 2.000, 3.000))\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m >>> Vector(range(3))\u001b[0m\n",
+ "\u001b[0;34m Vector((0.000, 1.000, 2.000))\u001b[0m\n",
+ "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstorage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtyping\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"a vector must have at least one entry\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/vector.py\n",
+ "\u001b[0;31mType:\u001b[0m function\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "Vector.__init__??"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Both `Matrix/Vector.storage` and `Matrix/Vector.typing` themselves reference the `DEFAULT_ENTRIES_STORAGE` and `DEFAULT_ENTRY_TYPE` constants in the [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py) module. This way, we could, for example, change only the constants and thereby also change how the `._entries` are stored internally in both classes. Also, this single **[single source of truth ](https://en.wikipedia.org/wiki/Single_source_of_truth)** ensures that both classes are consistent with each other at all times."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "tuple"
+ ]
+ },
+ "execution_count": 29,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "utils.DEFAULT_ENTRIES_STORAGE"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "float"
+ ]
+ },
+ "execution_count": 30,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "utils.DEFAULT_ENTRY_TYPE"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "For the same reasons, we also replace the \"hard coded\" references to the `Vector` and `Matrix` classes within the various methods.\n",
+ "\n",
+ "Every instance object has an automatically set `.__class__` attribute referencing its class."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "sample_package.matrix.Matrix"
+ ]
+ },
+ "execution_count": 31,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "m.__class__"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Of course, we could also use the [type() ](https://docs.python.org/3/library/functions.html#type) built-in instead."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "sample_package.matrix.Matrix"
+ ]
+ },
+ "execution_count": 32,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "type(m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "So, for example, the `Matrix.transpose()` method makes a `self.__class__(...)` instead of a `Matrix(...)` call."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\u001b[0;31mSignature:\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtranspose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mSource:\u001b[0m \n",
+ " \u001b[0;32mdef\u001b[0m \u001b[0mtranspose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Switch the rows and columns of a Matrix.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Returns:\u001b[0m\n",
+ "\u001b[0;34m matrix (Matrix)\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Example Usage:\u001b[0m\n",
+ "\u001b[0;34m >>> m = Matrix([(1, 2), (3, 4)])\u001b[0m\n",
+ "\u001b[0;34m >>> m\u001b[0m\n",
+ "\u001b[0;34m Matrix(((1.000, 2.000,), (3.000, 4.000,)))\u001b[0m\n",
+ "\u001b[0;34m >>> m.transpose()\u001b[0m\n",
+ "\u001b[0;34m Matrix(((1.000, 3.000,), (2.000, 4.000,)))\u001b[0m\n",
+ "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/matrix.py\n",
+ "\u001b[0;31mType:\u001b[0m function\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "Matrix.transpose??"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Whenever we need a `str` representation of a class's name, we use the `.__name__` attribute on the class, ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'Matrix'"
+ ]
+ },
+ "execution_count": 34,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Matrix.__name__"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "... or access it via the `.__class__` attribute on an instance."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'Matrix'"
+ ]
+ },
+ "execution_count": 35,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "m.__class__.__name__"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "For example, the `.__repr__()` and `.__str__()` methods make use of that."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\u001b[0;31mSignature:\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__repr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mSource:\u001b[0m \n",
+ " \u001b[0;32mdef\u001b[0m \u001b[0m__repr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Text representation of a Matrix.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mname\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\", \"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"(\"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\", \"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"{c:.3f}\"\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mc\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\",)\"\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mr\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34mf\"{name}(({args}))\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/matrix.py\n",
+ "\u001b[0;31mType:\u001b[0m function\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "Matrix.__repr__??"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In order to not have to \"hard code\" the name of *another* class (e.g., the `Vector.as_matrix()` method references the `Matrix` class), we apply the following \"hack:\" First, we store a reference to the other class as a class attribute (e.g., `Matrix.vector_cls` and `Vector.matrix_cls`), and then reference that attribute within the methods, just like `.storage` and `.typing` above."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "sample_package.vector.Vector"
+ ]
+ },
+ "execution_count": 37,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Matrix.vector_cls"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "sample_package.matrix.Matrix"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Vector.matrix_cls"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As an example, the `Vector.as_matrix()` method makes a `self.matrix_cls(...)` instead of a `Matrix(...)` call."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\u001b[0;31mSignature:\u001b[0m \u001b[0mVector\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolumn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mSource:\u001b[0m \n",
+ " \u001b[0;32mdef\u001b[0m \u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolumn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Get a Matrix representation of a Vector.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Args:\u001b[0m\n",
+ "\u001b[0;34m column (bool): if the vector is interpreted as a\u001b[0m\n",
+ "\u001b[0;34m column vector or a row vector; defaults to True\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Returns:\u001b[0m\n",
+ "\u001b[0;34m matrix (matrix.Matrix)\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Example Usage:\u001b[0m\n",
+ "\u001b[0;34m >>> v = Vector([1, 2, 3])\u001b[0m\n",
+ "\u001b[0;34m >>> v.as_matrix()\u001b[0m\n",
+ "\u001b[0;34m Matrix(((1.000,), (2.000,), (3.000,)))\u001b[0m\n",
+ "\u001b[0;34m >>> v.as_matrix(column=False)\u001b[0m\n",
+ "\u001b[0;34m Matrix(((1.000, 2.000, 3.000,)))\u001b[0m\n",
+ "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcolumn\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmatrix_cls\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmatrix_cls\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/vector.py\n",
+ "\u001b[0;31mType:\u001b[0m function\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "Vector.as_matrix??"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "For completeness sake, we mention that in the final `Vector` and `Matrix` classes, the `.__sub__()` and `.__rsub__()` methods use the negation operator implemented in `.__neg__()` and then dispatch to `.__add__()` instead of implementing the subtraction logic themselves."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## \"Real-life\" Experiment"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "Let's do some math with bigger `Matrix` and `Vector` instances."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import random"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "random.seed(42)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "We initialize `m` as a $100x50$ dimensional `Matrix` with random numbers in the range between `0` and `1_000`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "m = Matrix((1_000 * random.random() for _ in range(50)) for _ in range(100))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "We quickly lose track with all the numbers in the `Matrix`, which is why we implemented the `__str__()` method as a summary representation."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Matrix(((639.427, 25.011, 275.029, 223.211, 736.471, 676.699, 892.180, 86.939, 421.922, 29.797, 218.638, 505.355, 26.536, 198.838, 649.884, 544.941, 220.441, 589.266, 809.430, 6.499, 805.819, 698.139, 340.251, 155.479, 957.213, 336.595, 92.746, 96.716, 847.494, 603.726, 807.128, 729.732, 536.228, 973.116, 378.534, 552.041, 829.405, 618.520, 861.707, 577.352, 704.572, 45.824, 227.898, 289.388, 79.792, 232.791, 101.001, 277.974, 635.684, 364.832,), (370.181, 209.507, 266.978, 936.655, 648.035, 609.131, 171.139, 729.127, 163.402, 379.455, 989.523, 640.000, 556.950, 684.614, 842.852, 776.000, 229.048, 32.100, 315.453, 267.741, 210.983, 942.910, 876.368, 314.678, 655.439, 395.632, 914.548, 458.852, 264.880, 246.628, 561.368, 262.742, 584.586, 897.823, 399.401, 219.321, 997.538, 509.526, 90.909, 47.116, 109.649, 627.446, 792.079, 422.160, 63.528, 381.619, 996.121, 529.114, 971.078, 860.780,), (11.481, 720.722, 681.710, 536.970, 266.825, 640.962, 111.552, 434.765, 453.724, 953.816, 875.853, 263.389, 500.586, 178.652, 912.628, 870.519, 298.445, 638.949, 608.970, 152.839, 762.511, 539.379, 778.626, 530.354, 0.572, 324.156, 19.477, 929.099, 878.722, 831.666, 307.514, 57.925, 878.010, 946.949, 85.653, 485.990, 69.213, 760.602, 765.834, 128.391, 475.282, 549.804, 265.057, 872.433, 423.138, 211.798, 539.296, 729.931, 201.151, 311.716,), (995.149, 649.878, 438.100, 517.576, 121.004, 224.697, 338.086, 588.309, 230.115, 220.217, 70.993, 631.103, 228.942, 905.420, 859.635, 70.857, 238.005, 668.978, 214.237, 132.312, 935.514, 571.043, 472.671, 784.619, 807.497, 190.410, 96.931, 431.051, 423.579, 467.025, 729.076, 673.365, 984.165, 98.418, 402.621, 339.303, 861.673, 248.656, 190.209, 448.614, 421.882, 278.545, 249.806, 923.266, 443.131, 861.349, 550.325, 50.588, 999.282, 836.028,), (968.996, 926.367, 848.696, 166.311, 485.641, 213.747, 401.040, 58.635, 378.973, 985.309, 265.203, 784.071, 455.008, 423.007, 957.318, 995.423, 555.768, 718.408, 154.797, 296.708, 968.709, 579.180, 542.195, 747.976, 57.165, 584.178, 502.850, 852.720, 157.433, 960.779, 80.111, 185.825, 595.035, 675.213, 235.204, 119.887, 890.287, 246.215, 594.519, 619.382, 419.225, 583.672, 522.783, 934.706, 204.259, 716.192, 238.686, 395.786, 671.690, 299.997,), (316.177, 751.864, 72.543, 458.286, 998.454, 996.096, 73.261, 213.154, 265.200, 933.259, 880.864, 879.270, 369.527, 157.747, 833.745, 703.540, 611.678, 987.233, 653.976, 7.823, 817.104, 299.379, 663.389, 938.930, 134.291, 115.429, 107.036, 553.224, 272.348, 604.830, 717.612, 203.597, 634.238, 263.984, 488.532, 905.336, 846.104, 92.298, 423.576, 276.680, 3.546, 771.119, 637.113, 261.955, 741.231, 551.680, 427.687, 9.670, 75.244, 883.106,), (903.929, 545.590, 834.595, 582.510, 148.094, 127.446, 308.258, 898.981, 796.122, 860.703, 898.925, 210.077, 249.530, 102.794, 780.116, 884.135, 406.377, 620.662, 154.553, 929.881, 864.606, 976.206, 810.772, 881.416, 24.786, 736.564, 332.185, 930.816, 802.235, 864.064, 810.749, 266.806, 787.375, 108.096, 872.167, 858.593, 222.434, 816.587, 460.303, 305.191, 795.345, 227.595, 23.664, 193.130, 328.262, 864.353, 966.889, 279.125, 641.482, 399.678,), (981.150, 536.216, 939.237, 115.342, 970.401, 178.568, 962.534, 265.466, 108.403, 434.564, 728.545, 313.677, 606.209, 511.423, 385.195, 576.588, 254.723, 708.785, 1.691, 925.575, 538.452, 719.430, 741.950, 670.629, 364.221, 69.974, 664.238, 330.200, 313.916, 848.015, 719.754, 300.322, 309.285, 408.393, 402.400, 295.655, 127.288, 420.446, 940.364, 677.318, 902.806, 615.515, 300.950, 547.937, 0.406, 286.914, 429.888, 579.985, 654.706, 464.988,), (442.160, 213.701, 473.186, 901.181, 796.025, 169.691, 84.796, 515.452, 632.941, 335.188, 818.423, 751.138, 672.796, 224.641, 199.130, 24.425, 244.843, 475.136, 849.738, 72.828, 414.441, 629.765, 194.435, 696.354, 494.377, 243.984, 656.058, 5.545, 750.964, 770.046, 106.587, 425.146, 175.887, 957.966, 517.958, 50.218, 249.198, 848.336, 456.462, 801.417, 667.578, 987.892, 595.452, 950.040, 891.426, 612.652, 719.274, 504.778, 830.569, 547.872,), (897.208, 743.655, 474.674, 259.192, 247.240, 637.661, 765.814, 521.300, 626.748, 274.597, 77.483, 285.728, 271.715, 319.710, 540.152, 138.374, 231.261, 693.950, 706.419, 64.229, 407.599, 542.611, 415.774, 206.834, 420.144, 904.838, 584.079, 695.523, 856.732, 765.595, 380.381, 5.896, 351.759, 753.475, 853.448, 953.430, 419.021, 747.516, 546.132, 603.253, 220.539, 219.422, 435.836, 29.025, 336.130, 679.142, 404.317, 165.045, 467.390, 127.628,), (622.257, 26.966, 394.020, 564.392, 27.102, 642.750, 135.699, 461.698, 50.285, 379.104, 211.660, 326.846, 761.230, 379.126, 752.010, 831.924, 252.272, 81.906, 19.383, 539.419, 999.908, 349.960, 650.144, 781.233, 651.755, 754.233, 949.612, 199.361, 20.380, 152.382, 126.221, 669.459, 563.970, 217.965, 699.465, 766.898, 167.789, 607.247, 747.926, 114.533, 819.301, 964.721, 108.099, 25.678, 311.957, 677.347, 958.173, 396.654, 715.015, 75.996,), (690.614, 627.242, 101.901, 772.481, 850.293, 600.412, 121.055, 983.844, 782.635, 347.204, 428.378, 370.571, 505.961, 341.231, 849.576, 822.331, 105.539, 960.788, 635.585, 828.707, 707.309, 435.487, 733.795, 965.474, 270.082, 808.199, 538.173, 483.498, 435.574, 731.026, 268.396, 851.713, 830.731, 86.663, 881.631, 243.863, 464.708, 610.332, 378.989, 28.700, 850.953, 181.840, 212.120, 797.832, 340.339, 880.320, 701.184, 276.269, 10.151, 948.063,), (85.613, 720.075, 488.578, 758.165, 690.609, 645.903, 490.821, 792.933, 93.053, 221.596, 691.787, 306.206, 581.556, 473.260, 530.922, 425.504, 745.935, 330.791, 702.855, 270.916, 251.404, 120.656, 192.584, 119.555, 535.864, 762.190, 185.150, 216.385, 484.199, 724.585, 976.607, 524.637, 282.999, 100.526, 194.118, 227.483, 179.442, 14.148, 534.135, 274.311, 974.295, 553.359, 697.417, 126.279, 868.461, 490.879, 872.720, 574.064, 469.397, 440.469,), (184.364, 51.377, 941.064, 477.729, 822.116, 400.707, 74.082, 629.446, 53.609, 149.198, 562.840, 303.836, 993.918, 118.452, 764.443, 606.318, 790.741, 225.687, 522.573, 450.514, 442.721, 860.167, 990.031, 305.380, 621.027, 609.631, 740.089, 947.590, 207.788, 211.025, 660.428, 157.057, 173.814, 75.065, 2.676, 450.504, 593.811, 291.259, 231.476, 706.956, 702.988, 454.031, 687.385, 923.911, 787.828, 625.058, 661.183, 933.668, 425.139, 544.562,), (647.635, 908.411, 826.631, 71.410, 165.923, 307.612, 748.958, 569.207, 288.611, 124.354, 688.678, 699.734, 942.676, 500.472, 493.795, 80.442, 39.861, 432.029, 322.322, 250.368, 91.327, 961.911, 835.959, 575.199, 950.786, 999.572, 672.282, 269.511, 40.232, 756.269, 470.501, 651.509, 916.073, 181.489, 585.330, 634.785, 491.726, 91.242, 347.961, 333.308, 670.134, 857.733, 329.804, 693.674, 288.218, 945.194, 813.566, 550.097, 454.826, 314.517,), (323.274, 970.185, 404.175, 514.596, 988.119, 657.660, 542.594, 413.248, 187.583, 361.779, 756.443, 625.409, 759.991, 203.558, 549.220, 927.673, 438.116, 698.250, 121.426, 973.147, 608.872, 239.297, 158.378, 550.839, 552.251, 93.209, 992.257, 912.930, 461.448, 117.466, 832.143, 498.376, 716.603, 508.872, 273.425, 834.724, 980.245, 243.731, 551.265, 383.586, 921.868, 508.241, 879.326, 864.027, 276.247, 790.006, 414.942, 934.248, 507.738, 820.549,), (282.839, 298.556, 586.938, 998.902, 489.640, 148.595, 538.581, 345.124, 551.917, 543.430, 455.345, 321.777, 188.652, 697.498, 571.798, 233.562, 775.544, 43.647, 744.705, 705.228, 811.409, 386.079, 663.689, 820.748, 980.818, 495.329, 37.020, 502.291, 590.180, 869.700, 874.190, 440.306, 525.951, 456.928, 722.444, 409.979, 654.781, 154.361, 469.491, 969.204, 338.561, 692.705, 649.837, 851.765, 852.341, 859.342, 380.009, 316.661, 718.717, 759.402,), (872.383, 35.899, 68.421, 631.161, 920.929, 997.426, 746.766, 433.971, 98.443, 633.748, 872.579, 443.679, 694.001, 903.424, 45.991, 796.143, 293.368, 374.841, 145.570, 531.166, 565.928, 792.519, 169.984, 78.968, 870.840, 619.710, 240.830, 912.829, 143.118, 461.150, 253.977, 255.327, 9.397, 804.633, 901.209, 677.611, 157.976, 441.730, 345.566, 587.572, 638.939, 424.309, 250.098, 845.304, 199.217, 384.693, 483.208, 237.206, 571.923, 574.812,), (992.692, 295.231, 977.944, 658.230, 274.480, 565.929, 685.799, 744.669, 49.044, 606.406, 496.727, 904.155, 286.194, 798.860, 607.065, 352.321, 636.618, 620.891, 677.764, 720.928, 659.182, 838.337, 628.248, 903.404, 646.341, 308.933, 440.823, 579.574, 732.360, 90.133, 295.110, 747.481, 175.640, 132.160, 539.408, 971.490, 530.852, 913.487, 830.473, 256.970, 824.690, 481.848, 806.488, 746.559, 338.715, 115.170, 962.893, 140.757, 966.500, 860.141,), (724.217, 979.942, 967.270, 804.588, 365.775, 790.682, 13.919, 536.572, 454.786, 672.828, 672.341, 584.560, 822.417, 940.292, 108.346, 233.822, 25.025, 884.235, 561.407, 915.256, 221.367, 63.217, 823.855, 909.388, 302.190, 408.296, 139.777, 946.262, 304.365, 492.625, 97.192, 887.259, 135.664, 453.644, 670.486, 743.140, 945.974, 419.127, 742.269, 154.523, 414.885, 99.022, 489.347, 408.116, 951.522, 32.716, 370.530, 443.383, 950.555, 855.450,), (99.355, 685.680, 544.466, 977.843, 358.674, 398.140, 189.809, 122.160, 848.033, 454.717, 662.769, 641.704, 597.146, 21.357, 786.795, 243.569, 125.924, 564.578, 68.610, 765.157, 207.157, 215.951, 869.695, 328.560, 147.554, 900.531, 2.836, 858.406, 144.688, 129.992, 250.654, 174.497, 661.058, 25.780, 14.860, 789.985, 237.932, 323.771, 174.246, 52.399, 741.718, 526.086, 745.665, 476.246, 778.017, 513.238, 109.054, 503.839, 945.416, 43.365,), (783.227, 866.981, 521.451, 458.043, 964.026, 60.825, 478.982, 401.617, 686.097, 490.269, 909.701, 73.491, 80.790, 608.297, 65.682, 275.016, 633.077, 548.356, 325.185, 994.628, 530.557, 453.715, 605.427, 99.178, 701.779, 852.793, 650.917, 768.963, 720.840, 215.023, 451.555, 228.494, 338.932, 453.499, 415.990, 95.086, 426.764, 665.108, 374.301, 152.639, 922.985, 67.133, 831.772, 93.230, 96.564, 738.796, 811.769, 556.371, 586.465, 561.586,), (329.646, 122.231, 353.598, 665.341, 750.284, 868.092, 721.061, 968.399, 600.410, 351.646, 577.919, 212.739, 656.736, 224.245, 108.218, 845.373, 367.561, 762.606, 574.100, 807.221, 845.155, 974.547, 818.427, 613.573, 642.699, 26.254, 929.084, 829.461, 267.448, 180.416, 702.699, 308.985, 339.825, 6.106, 869.863, 566.321, 400.784, 141.875, 633.172, 30.657, 746.112, 215.133, 419.832, 340.896, 370.053, 721.596, 776.836, 567.594, 84.957, 52.609,), (157.410, 617.838, 673.969, 272.103, 661.939, 485.662, 442.044, 273.167, 754.943, 113.818, 429.914, 283.246, 678.486, 486.633, 667.133, 45.417, 395.263, 599.325, 7.687, 301.419, 211.234, 137.235, 255.520, 328.122, 7.730, 747.014, 175.695, 380.207, 703.671, 500.262, 833.354, 806.200, 72.075, 861.764, 42.302, 18.742, 921.162, 862.110, 575.759, 573.400, 709.499, 417.694, 115.173, 20.857, 324.768, 801.322, 618.125, 832.026, 919.770, 88.130,), (844.484, 243.316, 588.871, 523.963, 395.767, 310.275, 339.513, 333.069, 168.133, 510.483, 114.027, 509.952, 905.923, 349.375, 727.379, 818.949, 815.037, 236.269, 146.444, 197.272, 602.399, 760.215, 655.509, 177.146, 772.848, 494.117, 754.446, 759.877, 448.905, 924.154, 564.492, 635.298, 624.522, 864.247, 627.217, 150.957, 68.286, 442.208, 302.820, 274.674, 56.172, 507.337, 310.408, 451.914, 56.890, 831.697, 76.731, 864.250, 855.293, 615.008,), (507.068, 462.712, 554.316, 791.818, 895.877, 449.734, 809.816, 651.837, 321.527, 475.629, 150.861, 61.874, 103.502, 899.127, 343.438, 714.316, 504.549, 172.559, 247.744, 437.758, 439.422, 522.748, 158.746, 372.852, 282.894, 408.769, 338.367, 597.886, 789.227, 647.305, 65.912, 94.506, 678.379, 284.147, 723.734, 656.564, 906.343, 873.280, 333.362, 582.740, 141.428, 349.821, 967.697, 698.480, 391.958, 595.041, 938.002, 309.582, 376.679, 791.662,), (813.185, 670.116, 828.959, 738.775, 685.414, 526.393, 646.025, 423.406, 361.828, 362.598, 180.263, 214.193, 947.668, 486.271, 226.543, 137.565, 77.165, 844.428, 101.141, 770.875, 835.120, 883.682, 37.747, 336.764, 766.308, 131.049, 376.720, 162.247, 831.345, 771.098, 809.044, 165.539, 437.673, 410.859, 676.363, 237.530, 444.199, 284.928, 748.537, 448.928, 534.011, 309.468, 808.624, 469.016, 835.113, 367.841, 947.130, 984.440, 461.680, 281.772,), (381.872, 527.460, 966.268, 816.891, 801.259, 138.399, 250.003, 641.179, 874.117, 554.541, 102.590, 845.892, 851.166, 285.063, 763.117, 272.791, 905.306, 147.349, 437.473, 946.413, 222.038, 451.128, 349.585, 26.670, 53.257, 502.007, 235.778, 994.525, 374.913, 28.188, 930.826, 839.176, 649.961, 791.381, 137.600, 286.879, 829.762, 696.072, 138.793, 705.536, 448.601, 5.251, 79.226, 255.924, 834.963, 548.804, 727.235, 527.772, 111.187, 288.102,), (301.151, 47.749, 419.826, 793.899, 457.114, 110.858, 905.147, 596.739, 16.435, 515.376, 241.938, 143.577, 429.239, 614.810, 240.564, 416.568, 664.371, 85.614, 974.654, 67.679, 526.059, 507.328, 988.331, 554.152, 390.454, 470.135, 635.671, 981.039, 253.650, 16.242, 788.520, 344.802, 732.941, 628.257, 771.501, 735.187, 332.519, 44.336, 546.014, 813.509, 175.089, 779.143, 464.623, 695.389, 631.736, 811.498, 63.101, 776.190, 457.680, 293.443,), (43.806, 199.470, 41.906, 933.371, 515.384, 989.123, 543.031, 253.314, 753.291, 191.103, 356.974, 780.842, 865.798, 331.925, 124.475, 368.019, 889.487, 743.308, 894.637, 386.645, 973.724, 496.203, 497.523, 924.310, 519.276, 801.148, 727.081, 78.927, 602.453, 822.341, 545.474, 321.211, 80.069, 660.919, 306.496, 602.622, 426.116, 689.765, 351.547, 42.355, 870.037, 352.559, 998.151, 274.555, 980.027, 947.904, 75.041, 637.513, 363.311, 801.096,), (679.411, 952.789, 142.779, 607.573, 781.312, 34.799, 67.233, 778.515, 366.328, 382.854, 567.245, 605.095, 679.062, 948.824, 372.013, 763.084, 573.922, 529.460, 398.034, 649.561, 249.612, 113.449, 735.675, 499.044, 386.987, 561.673, 261.777, 260.290, 446.273, 996.365, 285.577, 916.479, 491.200, 122.637, 852.826, 452.043, 898.679, 445.111, 87.791, 681.929, 845.521, 319.588, 347.425, 64.939, 542.171, 891.332, 851.362, 711.809, 927.324, 637.700,), (793.696, 508.756, 121.362, 200.980, 138.877, 790.373, 26.284, 554.021, 368.911, 803.662, 551.647, 611.948, 86.215, 309.291, 999.595, 718.870, 525.696, 769.165, 823.339, 73.751, 972.380, 642.339, 449.974, 680.109, 344.515, 877.960, 780.263, 639.794, 181.963, 966.265, 432.618, 910.712, 55.413, 124.161, 153.015, 164.657, 322.661, 709.332, 346.023, 940.904, 894.926, 845.934, 250.605, 635.057, 550.841, 125.170, 302.825, 533.478, 502.573, 168.636,), (941.607, 154.194, 658.733, 720.633, 605.139, 842.530, 563.618, 825.236, 28.373, 45.462, 641.454, 576.771, 651.130, 766.959, 416.587, 638.991, 498.038, 627.164, 289.672, 956.650, 482.945, 804.688, 684.991, 297.434, 72.973, 59.913, 439.605, 484.251, 204.023, 606.660, 312.582, 718.363, 734.200, 860.777, 975.374, 130.766, 370.540, 561.651, 319.116, 466.473, 267.472, 247.919, 96.812, 290.212, 384.150, 615.377, 248.270, 865.308, 159.700, 327.436,), (577.687, 312.715, 763.121, 498.266, 514.725, 498.760, 308.540, 23.176, 945.233, 505.444, 966.687, 215.144, 352.895, 50.540, 494.894, 882.339, 654.260, 470.587, 536.691, 847.172, 430.928, 882.456, 727.508, 763.857, 365.937, 400.582, 570.282, 194.655, 553.223, 73.532, 504.256, 764.404, 279.721, 989.091, 680.399, 118.811, 975.083, 393.904, 794.897, 339.085, 938.949, 754.965, 199.058, 509.123, 500.078, 45.303, 137.036, 333.041, 473.744, 456.989,), (606.261, 515.506, 327.966, 613.068, 162.502, 990.616, 739.319, 299.234, 336.373, 828.289, 532.340, 708.740, 299.791, 815.749, 368.358, 673.806, 979.898, 583.702, 796.755, 725.324, 688.044, 26.647, 474.590, 967.071, 782.904, 776.162, 577.634, 721.400, 583.523, 170.512, 629.025, 619.736, 841.167, 147.776, 680.727, 31.571, 948.205, 109.896, 18.937, 313.692, 151.431, 690.500, 410.377, 774.972, 920.521, 872.818, 735.837, 62.281, 138.082, 207.342,), (325.050, 662.227, 525.477, 313.753, 173.182, 912.124, 342.327, 354.287, 771.990, 720.925, 643.309, 693.313, 610.077, 192.264, 246.519, 558.087, 224.867, 972.911, 297.615, 289.004, 207.278, 704.988, 317.041, 348.803, 933.700, 795.405, 273.458, 121.874, 676.622, 379.694, 980.161, 818.377, 954.609, 804.616, 290.453, 287.630, 714.141, 346.364, 442.376, 256.444, 479.079, 202.068, 538.578, 933.024, 696.171, 137.273, 615.677, 586.830, 242.458, 669.834,), (531.041, 637.945, 52.491, 413.301, 717.358, 100.545, 770.766, 5.181, 550.353, 929.100, 406.907, 935.032, 878.400, 477.449, 199.456, 963.914, 321.168, 645.898, 907.937, 89.461, 574.133, 535.152, 723.118, 936.669, 913.230, 175.065, 882.245, 175.789, 919.635, 997.172, 396.995, 495.384, 936.609, 962.131, 926.040, 876.743, 9.267, 567.962, 107.301, 982.994, 284.562, 989.099, 543.300, 493.912, 938.561, 851.060, 468.021, 192.811, 112.647, 162.494,), (458.914, 257.265, 186.199, 736.618, 790.768, 567.781, 757.283, 175.495, 856.147, 897.043, 826.990, 515.281, 86.738, 669.256, 184.781, 140.612, 323.602, 248.047, 260.785, 235.521, 753.757, 954.035, 301.946, 722.883, 11.436, 653.683, 692.769, 62.124, 118.225, 306.806, 405.417, 502.520, 895.118, 703.557, 310.978, 117.416, 916.130, 295.038, 614.625, 219.129, 133.569, 153.186, 747.735, 605.739, 415.846, 549.235, 470.828, 537.518, 664.094, 218.412,), (247.465, 754.740, 873.135, 81.870, 446.748, 703.766, 78.103, 564.169, 61.758, 547.649, 505.487, 572.702, 149.852, 328.118, 520.342, 116.240, 205.401, 583.148, 90.942, 510.375, 808.692, 453.432, 513.248, 456.798, 57.737, 462.378, 806.915, 723.280, 395.949, 816.453, 745.804, 578.311, 45.290, 344.529, 63.760, 994.124, 934.583, 69.019, 933.776, 31.735, 408.867, 768.972, 765.828, 978.333, 645.881, 420.362, 992.857, 382.480, 869.620, 906.767,), (375.646, 682.730, 661.793, 539.300, 653.534, 347.770, 178.474, 537.258, 528.843, 727.858, 222.690, 3.473, 22.735, 298.363, 673.500, 544.445, 531.934, 823.360, 247.512, 346.160, 275.650, 937.410, 725.024, 112.845, 809.478, 419.241, 766.053, 883.757, 15.646, 206.082, 100.897, 33.576, 597.785, 703.286, 48.676, 740.541, 402.265, 234.339, 217.269, 863.730, 56.444, 503.896, 289.263, 815.786, 731.517, 318.904, 597.918, 672.532, 320.665, 301.764,), (143.260, 660.212, 221.043, 300.501, 60.958, 948.520, 879.714, 911.578, 625.993, 427.201, 495.621, 972.290, 941.586, 671.343, 785.805, 318.734, 416.325, 149.218, 376.460, 754.416, 473.519, 849.341, 300.736, 707.577, 805.776, 914.741, 562.386, 967.786, 557.287, 134.093, 242.859, 203.337, 646.706, 922.226, 847.133, 92.464, 724.585, 190.482, 268.462, 673.672, 602.922, 873.620, 188.163, 761.696, 724.305, 558.850, 479.394, 869.474, 332.964, 957.020,), (15.334, 937.160, 962.078, 117.316, 999.572, 478.921, 242.593, 604.402, 204.513, 915.126, 552.079, 775.514, 380.662, 533.650, 359.260, 261.562, 512.817, 497.277, 98.608, 981.318, 469.490, 839.731, 914.330, 370.705, 413.930, 562.525, 221.274, 145.923, 260.774, 934.758, 579.143, 417.578, 152.411, 329.865, 379.840, 833.363, 499.301, 654.608, 684.847, 257.327, 821.592, 966.508, 641.694, 490.596, 168.234, 794.976, 169.266, 720.314, 488.316, 916.899,), (542.137, 641.809, 58.732, 33.824, 846.697, 945.188, 668.216, 764.339, 412.392, 842.545, 231.433, 707.170, 9.141, 505.733, 373.201, 617.835, 666.755, 616.519, 483.204, 487.854, 6.612, 551.644, 11.851, 529.418, 274.741, 977.479, 17.143, 813.157, 674.033, 806.168, 909.773, 107.016, 96.314, 148.897, 191.932, 526.456, 815.214, 267.325, 396.896, 373.052, 406.027, 565.002, 990.233, 225.857, 684.042, 847.867, 653.736, 858.219, 759.586, 93.501,), (379.264, 552.701, 56.115, 9.450, 171.384, 499.858, 433.910, 784.376, 565.857, 857.960, 95.362, 528.159, 42.552, 211.417, 868.117, 887.554, 475.500, 46.562, 74.348, 925.585, 899.312, 563.510, 32.902, 928.766, 314.485, 961.469, 587.036, 752.254, 712.711, 398.296, 76.937, 162.450, 240.472, 834.651, 389.157, 896.526, 331.730, 755.609, 139.951, 988.478, 724.164, 500.793, 974.323, 53.696, 437.088, 838.675, 340.593, 769.006, 954.858, 396.703,), (773.555, 29.626, 273.327, 992.586, 490.603, 355.811, 941.143, 431.848, 679.695, 660.672, 85.694, 618.616, 798.055, 713.109, 82.038, 154.221, 711.677, 633.901, 739.655, 316.678, 106.551, 5.195, 308.267, 359.917, 269.766, 132.507, 187.392, 448.844, 554.740, 408.044, 26.262, 353.914, 93.064, 598.044, 324.430, 385.238, 291.847, 387.800, 84.700, 901.136, 905.208, 978.173, 571.960, 169.583, 380.732, 138.840, 301.131, 493.124, 63.267, 434.676,), (421.102, 484.231, 76.921, 251.700, 246.590, 625.034, 593.806, 195.548, 106.972, 304.658, 948.823, 332.217, 620.192, 804.076, 329.542, 334.736, 815.475, 859.508, 974.225, 136.124, 320.665, 947.279, 200.851, 314.183, 964.575, 968.725, 291.448, 694.958, 491.007, 575.879, 242.424, 376.055, 816.495, 392.935, 113.888, 563.851, 592.227, 545.629, 681.713, 550.099, 953.005, 461.622, 708.367, 438.455, 291.331, 692.835, 818.966, 795.657, 409.142, 499.303,), (633.336, 242.021, 658.663, 715.236, 789.077, 73.965, 990.701, 479.235, 400.805, 506.613, 920.392, 691.709, 543.645, 790.721, 359.529, 895.502, 536.906, 638.180, 84.982, 768.954, 657.602, 355.009, 647.000, 44.297, 983.608, 677.472, 399.618, 752.683, 965.717, 430.456, 10.548, 258.738, 510.676, 518.798, 580.518, 575.235, 445.779, 391.134, 772.342, 588.590, 500.466, 344.967, 24.563, 104.549, 415.975, 961.728, 116.069, 940.676, 141.675, 311.890,), (455.333, 206.867, 482.926, 476.163, 438.166, 696.763, 318.909, 300.264, 810.186, 115.085, 849.180, 647.970, 677.139, 164.354, 983.900, 243.913, 174.453, 160.136, 559.849, 958.463, 231.856, 405.047, 184.452, 640.479, 432.134, 29.192, 614.107, 197.324, 592.203, 388.836, 704.736, 205.784, 752.325, 808.730, 62.564, 101.752, 871.979, 186.960, 325.985, 457.550, 262.353, 862.637, 527.715, 639.109, 596.971, 611.308, 587.005, 347.925, 845.518, 617.363,), (813.738, 705.988, 297.445, 614.485, 84.752, 133.948, 117.862, 305.380, 183.045, 693.437, 510.825, 418.239, 137.867, 383.710, 185.754, 635.502, 693.433, 645.260, 999.900, 554.913, 489.642, 140.297, 314.580, 451.001, 53.611, 359.039, 9.583, 136.535, 815.216, 963.829, 505.438, 494.970, 684.697, 415.630, 839.892, 488.700, 82.671, 30.861, 761.057, 292.090, 274.853, 537.609, 168.209, 457.321, 742.518, 765.920, 549.726, 113.211, 114.207, 775.113,), (823.283, 366.862, 822.611, 41.611, 718.980, 546.353, 989.776, 102.416, 830.071, 751.345, 297.709, 999.313, 449.732, 348.577, 816.729, 439.070, 993.958, 775.632, 236.946, 810.703, 587.924, 350.631, 710.754, 632.771, 165.982, 139.235, 206.620, 206.943, 59.358, 350.815, 281.085, 538.769, 323.654, 704.054, 289.333, 267.343, 858.017, 985.488, 679.299, 95.225, 962.772, 785.691, 918.769, 992.486, 867.048, 126.888, 866.079, 249.677, 711.395, 828.482,), (761.474, 676.235, 489.459, 577.426, 268.717, 414.225, 451.992, 633.628, 880.125, 93.095, 515.613, 278.226, 936.336, 369.071, 950.254, 327.289, 2.473, 774.135, 732.724, 730.932, 458.449, 664.144, 358.223, 63.331, 534.424, 217.830, 429.643, 211.851, 268.537, 828.344, 337.755, 577.934, 566.142, 485.338, 343.740, 682.552, 48.409, 99.575, 783.890, 459.582, 124.237, 857.652, 441.286, 0.676, 958.032, 202.318, 688.592, 131.913, 649.997, 158.977,), (932.726, 274.019, 654.588, 250.389, 371.844, 903.800, 165.525, 396.342, 305.509, 699.441, 234.144, 655.485, 703.698, 1.086, 476.807, 132.700, 226.191, 679.983, 9.287, 695.597, 817.109, 988.155, 422.314, 132.175, 70.828, 383.070, 730.763, 102.427, 313.351, 880.989, 137.129, 773.460, 753.158, 133.146, 992.940, 142.853, 530.508, 8.475, 650.020, 440.099, 722.432, 628.080, 151.374, 411.710, 686.566, 859.963, 86.688, 100.465, 752.446, 589.574,), (384.032, 963.249, 314.504, 139.830, 276.968, 84.249, 553.397, 600.008, 607.593, 778.970, 690.476, 847.892, 658.405, 301.649, 517.749, 509.523, 747.844, 295.542, 54.569, 897.913, 954.672, 494.888, 112.744, 499.583, 593.930, 528.287, 977.697, 986.883, 933.924, 131.983, 860.814, 568.380, 365.412, 682.942, 762.726, 954.453, 770.367, 16.689, 67.533, 262.185, 39.827, 60.469, 789.290, 506.611, 628.571, 501.049, 415.432, 701.811, 82.428, 536.565,), (616.047, 277.468, 309.907, 511.305, 203.197, 808.060, 536.390, 390.732, 634.294, 834.526, 681.056, 66.115, 698.676, 729.958, 846.468, 57.906, 86.213, 434.484, 453.372, 608.832, 309.290, 741.694, 740.658, 119.443, 707.901, 701.500, 163.833, 953.016, 523.005, 782.987, 720.766, 166.963, 126.931, 781.124, 268.764, 886.415, 771.430, 29.356, 807.108, 271.981, 63.872, 712.323, 576.652, 77.070, 455.198, 360.124, 499.610, 566.886, 367.688, 255.116,), (102.905, 573.912, 722.778, 228.404, 508.825, 44.038, 862.928, 244.551, 471.765, 382.979, 150.085, 931.160, 857.485, 552.865, 913.948, 740.666, 419.369, 321.802, 416.257, 720.288, 271.258, 77.887, 372.808, 502.041, 901.941, 179.344, 804.338, 981.414, 954.079, 68.926, 465.094, 282.307, 844.847, 327.301, 553.091, 7.969, 200.671, 563.807, 303.909, 622.718, 463.927, 591.691, 493.361, 772.613, 195.422, 900.443, 760.482, 245.127, 6.378, 410.036,), (232.998, 346.424, 839.574, 877.199, 950.990, 1.462, 657.304, 849.006, 727.215, 103.949, 529.814, 238.168, 492.029, 59.895, 996.959, 711.652, 93.027, 921.274, 897.287, 519.759, 700.847, 372.492, 974.555, 84.902, 95.577, 133.514, 819.963, 74.830, 567.821, 434.983, 964.218, 236.772, 260.991, 315.009, 800.817, 700.726, 735.353, 318.058, 271.956, 74.687, 202.713, 779.937, 584.708, 155.411, 164.375, 466.055, 406.513, 535.925, 964.635, 207.636,), (308.308, 265.008, 119.914, 157.615, 686.055, 826.387, 696.878, 40.330, 835.925, 327.794, 91.213, 248.219, 355.743, 513.460, 677.183, 260.167, 990.677, 31.082, 404.392, 452.201, 748.078, 249.853, 462.043, 803.897, 139.793, 11.957, 830.372, 982.567, 130.715, 823.673, 372.240, 630.298, 644.685, 582.324, 258.821, 812.747, 21.800, 64.472, 902.496, 443.431, 128.792, 905.078, 829.356, 331.550, 42.696, 460.995, 167.989, 573.884, 821.685, 394.999,), (29.482, 683.213, 172.807, 214.716, 187.151, 279.855, 883.424, 34.649, 619.177, 245.785, 295.105, 411.992, 550.689, 60.979, 279.776, 137.236, 199.458, 884.653, 525.814, 630.754, 802.168, 794.846, 989.404, 781.916, 359.112, 544.518, 484.679, 912.677, 502.393, 388.381, 179.816, 318.878, 219.020, 895.765, 778.538, 58.591, 991.531, 529.432, 766.842, 999.606, 973.980, 100.134, 656.864, 266.527, 816.285, 917.259, 55.909, 996.392, 219.412, 846.505,), (797.391, 354.805, 839.222, 845.221, 176.100, 592.520, 806.210, 697.627, 913.980, 28.207, 700.561, 947.559, 563.606, 563.109, 188.234, 988.006, 881.626, 492.227, 309.053, 490.436, 90.257, 232.623, 218.809, 526.449, 0.683, 917.896, 201.464, 130.490, 716.938, 918.781, 844.284, 323.589, 21.913, 586.609, 917.224, 774.366, 846.481, 860.669, 960.559, 373.591, 941.923, 395.596, 101.032, 301.765, 136.452, 157.504, 948.694, 791.843, 960.662, 649.180,), (174.203, 968.743, 693.560, 928.845, 786.992, 223.237, 588.950, 175.352, 306.823, 688.498, 127.348, 728.831, 948.788, 948.698, 391.601, 994.283, 965.184, 32.383, 602.389, 921.038, 967.532, 220.895, 565.550, 936.688, 140.643, 745.343, 237.996, 982.380, 167.887, 885.320, 88.726, 708.966, 639.103, 886.654, 446.630, 265.212, 249.541, 67.795, 256.658, 108.035, 1.281, 385.937, 732.584, 969.102, 884.552, 493.082, 378.713, 546.024, 101.429, 479.489,), (864.051, 650.970, 687.160, 162.657, 73.709, 845.762, 294.909, 318.717, 951.662, 74.200, 170.105, 375.458, 731.985, 547.046, 898.124, 93.105, 594.031, 613.645, 482.737, 30.993, 942.442, 164.997, 889.753, 157.104, 101.258, 205.550, 189.984, 697.197, 722.038, 729.974, 265.265, 281.523, 237.548, 49.635, 571.770, 839.855, 153.590, 360.829, 427.606, 294.549, 662.020, 600.216, 199.675, 25.736, 170.832, 291.811, 81.936, 843.769, 308.353, 397.467,), (489.097, 661.040, 91.137, 544.126, 184.881, 885.493, 369.402, 445.775, 263.296, 465.058, 226.242, 268.562, 61.639, 752.235, 667.468, 85.707, 343.791, 541.449, 970.706, 589.726, 553.602, 840.797, 818.405, 418.632, 535.514, 866.894, 474.824, 881.654, 476.262, 78.955, 902.777, 714.306, 502.101, 900.455, 800.450, 677.535, 620.273, 120.282, 757.189, 172.879, 984.368, 972.165, 808.461, 126.186, 423.375, 988.280, 435.393, 997.269, 627.226, 834.108,), (257.886, 910.804, 914.174, 67.012, 388.099, 397.355, 326.127, 276.201, 458.157, 873.466, 786.551, 623.058, 522.562, 419.594, 414.442, 148.200, 587.934, 758.377, 939.650, 924.928, 562.684, 100.990, 286.093, 535.633, 343.869, 410.890, 383.043, 485.585, 608.962, 37.466, 275.414, 143.853, 608.655, 693.661, 38.783, 889.574, 331.494, 237.579, 745.750, 920.835, 897.031, 20.233, 817.386, 303.055, 280.347, 491.619, 696.183, 98.217, 868.900, 134.386,), (974.219, 443.111, 825.823, 269.420, 416.773, 645.555, 187.936, 211.391, 823.969, 740.945, 759.493, 867.189, 821.017, 515.270, 158.972, 311.123, 506.795, 135.650, 851.295, 879.333, 28.948, 192.763, 832.930, 836.977, 249.490, 456.449, 918.031, 704.634, 273.977, 823.506, 505.126, 635.410, 123.873, 30.563, 372.467, 594.044, 177.584, 870.481, 586.880, 349.760, 163.514, 894.492, 748.961, 688.851, 285.069, 386.566, 162.907, 572.258, 964.918, 857.111,), (647.380, 677.695, 269.084, 409.507, 20.052, 780.304, 767.573, 8.898, 911.515, 647.372, 601.142, 8.464, 252.390, 805.086, 305.460, 967.024, 642.740, 423.807, 376.475, 348.709, 251.999, 466.698, 677.196, 824.311, 397.153, 102.312, 511.514, 662.355, 843.284, 374.230, 647.342, 609.050, 298.476, 108.105, 63.837, 988.361, 640.595, 861.492, 261.147, 711.094, 892.374, 298.791, 149.929, 765.474, 899.687, 805.430, 802.364, 600.033, 660.530, 680.756,), (721.283, 655.401, 997.469, 259.426, 418.566, 388.275, 35.319, 708.069, 572.042, 189.915, 726.550, 222.363, 534.635, 784.897, 906.527, 671.868, 507.315, 845.419, 840.639, 876.495, 181.136, 97.603, 127.944, 258.652, 808.344, 762.918, 183.068, 679.712, 335.632, 89.300, 355.283, 744.210, 307.085, 788.090, 331.317, 260.559, 294.051, 851.214, 470.537, 866.393, 583.575, 944.301, 71.216, 889.426, 500.477, 867.498, 381.669, 298.356, 54.062, 854.240,), (137.365, 200.297, 409.192, 569.404, 906.621, 457.571, 316.383, 715.668, 778.941, 487.581, 631.033, 176.824, 634.499, 4.714, 273.528, 761.193, 168.606, 764.484, 489.577, 763.569, 88.041, 614.480, 633.488, 403.393, 965.328, 383.316, 37.725, 199.414, 373.101, 14.077, 322.213, 833.229, 190.577, 676.748, 626.687, 248.823, 693.525, 344.350, 128.931, 383.550, 588.671, 167.020, 823.844, 298.202, 290.828, 727.832, 596.370, 337.835, 887.974, 995.472,), (342.733, 901.384, 359.251, 188.426, 948.084, 918.205, 403.392, 228.418, 727.169, 131.206, 734.077, 589.693, 168.985, 366.591, 650.540, 37.363, 876.544, 255.789, 534.733, 48.587, 994.680, 661.970, 653.568, 19.742, 689.750, 416.776, 380.254, 547.282, 474.396, 153.127, 695.192, 630.311, 301.116, 661.665, 662.483, 269.977, 605.634, 137.164, 830.653, 104.914, 718.766, 117.735, 114.013, 106.264, 198.647, 199.749, 262.994, 523.146, 201.673, 703.437,), (295.351, 39.406, 496.355, 207.694, 933.124, 330.604, 2.738, 671.633, 906.880, 835.232, 669.022, 149.144, 90.083, 511.705, 723.564, 101.290, 255.892, 231.160, 988.666, 296.022, 464.277, 99.809, 174.702, 39.445, 290.567, 801.596, 312.707, 738.540, 94.998, 758.204, 45.889, 851.999, 663.355, 170.512, 357.527, 437.715, 621.807, 878.476, 92.951, 814.964, 182.869, 400.778, 962.310, 271.833, 385.715, 850.672, 799.899, 648.846, 796.910, 113.057,), (696.170, 58.646, 942.467, 159.395, 416.028, 590.750, 802.265, 678.393, 181.261, 379.751, 358.599, 28.816, 684.464, 838.536, 973.445, 130.655, 920.398, 112.937, 411.284, 45.972, 261.613, 314.238, 704.568, 677.929, 767.529, 576.649, 565.012, 977.896, 669.844, 338.301, 523.103, 700.580, 95.242, 661.713, 248.577, 345.749, 676.296, 384.877, 839.033, 558.344, 987.792, 54.566, 643.399, 156.927, 848.846, 851.871, 869.416, 74.865, 491.648, 240.892,), (970.145, 50.351, 222.707, 643.317, 403.277, 235.000, 459.095, 801.261, 448.099, 856.589, 447.068, 118.707, 497.373, 653.373, 102.643, 412.339, 557.131, 0.170, 90.896, 604.203, 619.066, 304.598, 508.338, 206.851, 671.474, 950.355, 363.342, 54.275, 222.918, 454.460, 560.151, 619.758, 473.134, 657.167, 715.914, 114.023, 759.320, 221.747, 341.357, 829.884, 964.434, 291.984, 521.985, 702.096, 45.778, 164.155, 140.449, 716.856, 721.559, 106.962,), (610.852, 187.543, 930.216, 392.896, 457.046, 781.420, 716.788, 107.766, 414.470, 926.627, 837.466, 588.811, 772.145, 450.505, 658.457, 956.190, 134.636, 498.829, 530.364, 48.572, 935.308, 838.816, 482.932, 508.333, 921.162, 177.202, 578.554, 730.495, 128.382, 387.406, 600.546, 882.696, 504.110, 384.663, 979.501, 915.910, 762.375, 273.596, 963.594, 970.492, 452.846, 133.372, 412.745, 700.014, 748.427, 298.913, 701.494, 860.708, 711.874, 935.512,), (632.583, 200.889, 624.168, 290.579, 345.294, 672.374, 981.335, 650.086, 958.326, 503.893, 694.312, 322.381, 115.405, 352.238, 480.370, 570.638, 667.157, 417.478, 747.869, 841.389, 285.928, 847.301, 808.305, 522.730, 25.272, 145.327, 670.254, 199.902, 750.185, 160.935, 286.616, 250.561, 839.382, 690.595, 295.141, 753.594, 32.306, 814.026, 102.480, 868.035, 739.492, 864.841, 742.413, 561.271, 237.595, 784.307, 798.822, 287.639, 664.494, 926.485,), (387.555, 956.702, 975.823, 312.654, 552.125, 12.965, 251.341, 620.562, 780.925, 870.118, 829.983, 911.486, 704.629, 647.632, 755.108, 546.615, 603.387, 776.147, 964.290, 294.178, 177.426, 682.659, 186.977, 173.795, 513.847, 377.198, 428.508, 556.599, 130.067, 583.986, 255.016, 330.563, 709.689, 154.806, 153.703, 322.630, 50.855, 932.382, 615.609, 661.890, 490.551, 572.677, 358.086, 784.031, 317.859, 219.962, 183.869, 68.176, 505.140, 415.931,), (537.036, 91.946, 221.198, 213.354, 331.884, 360.798, 218.445, 752.656, 530.499, 996.631, 823.688, 981.133, 8.804, 668.938, 445.667, 904.496, 613.701, 621.132, 958.897, 682.551, 320.937, 915.932, 944.988, 385.876, 540.237, 282.829, 911.336, 822.109, 374.958, 802.817, 445.557, 43.852, 898.282, 192.512, 513.895, 948.212, 167.395, 956.276, 537.975, 7.331, 65.472, 670.335, 773.582, 864.951, 424.209, 103.927, 537.754, 702.743, 976.224, 775.207,), (646.561, 939.751, 746.891, 153.755, 459.225, 331.715, 87.521, 54.268, 794.215, 558.084, 574.962, 227.646, 258.648, 388.033, 630.217, 433.025, 16.891, 673.299, 534.834, 641.423, 618.349, 755.961, 585.608, 700.882, 72.589, 928.048, 106.136, 786.892, 301.351, 86.505, 765.274, 437.676, 395.113, 660.723, 473.988, 533.468, 136.381, 390.993, 797.485, 545.724, 960.134, 144.076, 677.289, 914.169, 795.017, 729.325, 373.131, 949.465, 553.524, 555.070,), (123.235, 5.030, 596.655, 537.152, 946.979, 304.691, 748.297, 903.546, 343.852, 412.654, 645.932, 512.553, 160.954, 220.788, 834.748, 194.326, 181.358, 799.660, 852.251, 851.388, 932.370, 994.313, 461.265, 549.665, 290.653, 67.363, 98.202, 725.589, 487.190, 330.851, 128.215, 655.473, 100.045, 619.516, 900.119, 317.786, 450.646, 616.342, 305.675, 584.170, 565.552, 364.493, 316.333, 428.307, 4.832, 246.175, 221.526, 739.816, 436.124, 840.110,), (134.306, 732.973, 877.886, 462.848, 358.742, 305.472, 551.672, 175.770, 606.628, 841.793, 858.714, 140.002, 538.618, 263.235, 886.336, 76.462, 75.400, 18.630, 507.178, 31.194, 581.890, 405.134, 590.036, 906.304, 551.594, 543.469, 998.728, 472.104, 775.197, 365.819, 223.307, 772.145, 733.227, 290.999, 464.701, 510.412, 396.785, 502.167, 662.678, 847.982, 806.540, 614.036, 165.844, 514.137, 446.220, 179.145, 948.712, 659.413, 967.736, 735.920,), (483.503, 358.796, 218.819, 487.642, 62.861, 369.435, 42.808, 206.774, 907.726, 361.251, 469.674, 454.643, 46.437, 980.590, 324.071, 703.736, 521.373, 829.647, 834.872, 263.316, 544.350, 173.850, 653.675, 365.297, 653.337, 835.469, 516.663, 376.329, 907.242, 516.372, 352.898, 867.719, 486.862, 482.110, 604.211, 501.837, 138.968, 165.606, 77.088, 643.477, 211.517, 186.906, 362.223, 717.303, 118.330, 230.346, 811.173, 720.256, 481.517, 478.815,), (210.704, 161.246, 833.365, 22.446, 43.144, 573.487, 161.129, 629.540, 39.191, 572.269, 55.912, 258.260, 180.038, 958.238, 599.358, 563.605, 18.620, 719.547, 661.745, 283.455, 85.749, 449.203, 992.776, 867.302, 170.567, 830.364, 600.839, 794.616, 820.453, 181.884, 659.649, 264.521, 724.187, 342.671, 453.470, 590.596, 229.814, 385.462, 108.570, 202.349, 859.592, 504.360, 419.795, 149.561, 96.299, 475.869, 614.748, 39.037, 783.868, 503.329,), (117.516, 482.241, 130.329, 603.475, 827.522, 896.969, 774.870, 649.232, 522.044, 370.377, 43.438, 529.817, 229.486, 802.300, 789.556, 381.236, 589.048, 741.394, 761.539, 696.708, 97.717, 133.883, 477.141, 232.513, 859.053, 283.267, 876.766, 408.647, 189.021, 709.149, 789.422, 577.987, 117.829, 7.194, 654.546, 687.767, 315.902, 362.270, 154.779, 651.576, 253.633, 854.977, 423.562, 365.644, 275.561, 680.606, 754.972, 413.228, 783.788, 482.468,), (370.213, 554.911, 253.778, 306.653, 344.356, 705.495, 735.782, 854.999, 659.283, 747.814, 446.595, 699.311, 161.795, 214.458, 400.561, 338.438, 550.576, 697.057, 706.790, 160.738, 964.570, 5.291, 91.089, 145.328, 925.862, 435.377, 64.080, 221.770, 80.040, 37.755, 384.326, 984.448, 613.966, 521.006, 711.606, 597.473, 945.716, 820.229, 640.291, 438.972, 201.707, 656.884, 803.910, 286.007, 33.609, 599.515, 515.814, 231.671, 169.483, 36.061,), (239.257, 0.287, 157.445, 996.817, 779.673, 353.128, 395.143, 586.080, 517.743, 736.926, 68.617, 90.166, 284.494, 829.765, 915.009, 348.095, 963.288, 276.508, 606.363, 194.525, 929.714, 574.907, 261.108, 407.135, 106.465, 72.386, 294.252, 947.015, 802.942, 956.709, 876.113, 813.147, 574.894, 694.192, 966.055, 563.169, 769.967, 757.473, 961.239, 458.848, 460.720, 586.827, 27.351, 116.268, 67.554, 633.662, 994.198, 676.913, 229.930, 315.731,), (955.446, 516.504, 9.723, 832.177, 248.258, 93.009, 673.698, 821.075, 76.029, 931.396, 476.555, 353.538, 894.316, 269.076, 947.118, 683.107, 909.927, 498.999, 199.667, 735.429, 872.742, 206.779, 202.767, 249.695, 618.597, 155.996, 106.689, 920.188, 675.812, 663.424, 613.827, 764.244, 541.474, 42.263, 482.292, 620.667, 492.340, 985.361, 897.000, 868.769, 490.805, 984.395, 916.007, 280.267, 222.093, 576.665, 54.148, 799.270, 478.320, 540.768,), (502.475, 393.717, 686.339, 174.800, 976.507, 698.245, 460.071, 689.211, 11.820, 210.752, 581.008, 325.436, 612.778, 259.701, 548.563, 237.139, 471.345, 613.084, 365.924, 498.770, 210.532, 700.713, 372.310, 854.326, 279.630, 179.889, 131.139, 575.893, 228.567, 99.870, 269.949, 235.805, 432.219, 380.883, 145.844, 959.148, 149.548, 805.668, 176.607, 499.598, 995.562, 849.390, 517.007, 720.560, 785.333, 300.034, 562.138, 567.835, 398.402, 690.543,), (60.036, 813.818, 476.539, 629.862, 449.855, 334.657, 360.962, 560.350, 931.845, 257.715, 19.835, 121.237, 867.369, 963.117, 198.958, 575.894, 649.131, 174.539, 780.309, 355.109, 673.193, 487.494, 736.526, 889.632, 381.071, 286.524, 631.712, 144.848, 167.576, 807.717, 337.388, 631.273, 571.638, 848.901, 71.344, 161.999, 228.218, 316.878, 291.356, 267.472, 644.644, 271.248, 444.898, 862.806, 363.165, 586.920, 965.526, 413.989, 183.886, 23.101,), (727.772, 662.023, 940.437, 700.697, 80.065, 166.910, 103.009, 63.792, 878.691, 548.434, 26.365, 397.013, 764.996, 83.251, 262.452, 155.274, 654.833, 885.471, 309.371, 247.397, 284.175, 626.481, 131.277, 838.518, 28.571, 663.549, 860.369, 325.201, 476.155, 974.982, 540.584, 272.414, 444.845, 969.542, 697.623, 177.037, 597.584, 631.048, 635.851, 563.984, 523.460, 639.528, 309.661, 347.168, 539.668, 804.708, 441.860, 365.783, 259.807, 303.573,), (0.092, 815.380, 847.946, 343.885, 469.198, 8.317, 922.088, 946.958, 477.067, 9.133, 430.435, 292.923, 231.215, 7.218, 373.644, 411.736, 560.554, 394.752, 163.269, 737.118, 389.723, 378.358, 262.980, 422.239, 239.562, 764.538, 909.954, 807.575, 684.640, 284.677, 742.927, 808.809, 412.904, 853.482, 182.407, 289.595, 636.893, 617.729, 272.134, 622.754, 187.789, 19.395, 49.632, 534.971, 185.938, 102.273, 269.195, 715.196, 727.107, 232.810,), (150.684, 493.580, 341.913, 311.577, 799.462, 997.963, 463.684, 791.425, 330.249, 843.546, 951.644, 56.038, 775.653, 71.385, 470.147, 192.192, 841.539, 817.251, 828.252, 121.962, 768.146, 248.962, 771.225, 442.961, 737.619, 33.510, 460.835, 770.996, 521.139, 982.116, 472.826, 681.442, 312.109, 323.063, 629.164, 42.186, 937.421, 520.926, 253.313, 638.520, 198.724, 887.520, 863.658, 217.788, 112.928, 632.909, 324.513, 167.360, 276.100, 119.650,), (789.144, 8.930, 41.993, 783.475, 475.271, 596.212, 371.399, 89.480, 157.698, 91.402, 618.645, 930.849, 996.973, 625.132, 59.775, 644.575, 701.328, 791.466, 125.827, 232.675, 981.676, 788.562, 756.698, 805.525, 438.719, 192.965, 689.250, 358.167, 134.732, 898.663, 485.302, 437.500, 294.715, 697.038, 189.776, 186.627, 347.665, 732.402, 275.324, 831.350, 914.474, 554.520, 68.877, 155.025, 295.673, 263.349, 367.113, 0.720, 638.026, 379.044,), (185.534, 18.720, 856.829, 776.017, 238.787, 721.004, 658.289, 538.957, 387.491, 524.070, 497.549, 551.468, 613.039, 322.600, 640.904, 110.464, 523.201, 66.680, 835.193, 129.774, 873.493, 202.079, 485.370, 98.754, 571.012, 836.300, 657.921, 525.821, 701.314, 255.939, 366.354, 605.954, 70.874, 930.211, 208.738, 491.406, 889.767, 79.957, 801.020, 60.700, 558.020, 938.244, 415.953, 369.069, 708.434, 823.643, 265.816, 40.653, 70.388, 303.905,), (944.603, 960.587, 97.119, 725.172, 531.661, 189.460, 526.192, 248.763, 625.325, 178.898, 749.194, 415.185, 105.514, 638.624, 357.963, 458.721, 665.242, 882.824, 166.809, 180.397, 401.557, 337.513, 158.176, 998.434, 443.012, 352.324, 315.155, 991.463, 324.654, 371.720, 764.184, 430.588, 725.868, 608.413, 563.145, 213.928, 765.708, 926.147, 254.089, 961.652, 447.500, 397.091, 726.313, 982.956, 595.763, 517.609, 993.412, 301.842, 300.334, 221.887,), (855.720, 21.701, 821.940, 689.719, 275.957, 553.663, 556.323, 925.922, 154.715, 37.736, 355.656, 138.408, 367.083, 582.156, 232.994, 811.074, 91.905, 399.798, 917.882, 734.266, 723.581, 792.904, 172.902, 825.705, 689.595, 576.232, 907.656, 595.231, 300.393, 730.789, 576.284, 78.477, 55.923, 770.902, 347.930, 817.142, 416.522, 867.831, 869.787, 226.718, 652.779, 602.302, 11.434, 777.419, 382.487, 304.783, 41.182, 539.930, 149.580, 502.402,), (220.797, 50.520, 731.579, 392.883, 445.616, 595.132, 504.729, 222.086, 289.783, 394.322, 132.189, 82.545, 571.441, 49.311, 399.191, 85.079, 501.823, 773.825, 130.375, 134.871, 559.296, 487.861, 652.248, 196.099, 615.997, 735.668, 246.246, 71.644, 776.772, 323.412, 924.138, 89.595, 671.748, 423.541, 348.308, 320.738, 593.877, 24.207, 304.819, 987.652, 616.221, 990.159, 442.210, 145.818, 44.878, 818.172, 199.685, 373.821, 757.734, 852.764,), (112.372, 54.538, 948.941, 926.730, 868.752, 820.134, 13.733, 693.795, 111.278, 450.062, 22.748, 209.010, 538.005, 203.801, 523.266, 258.658, 483.026, 729.924, 141.347, 698.755, 18.389, 583.005, 663.528, 43.482, 170.320, 284.014, 789.188, 617.965, 53.085, 654.758, 8.334, 388.645, 271.310, 852.083, 660.100, 864.282, 19.078, 867.410, 649.411, 231.169, 380.701, 976.612, 99.604, 315.456, 866.773, 531.561, 186.417, 500.651, 457.986, 926.350,), (21.488, 247.392, 529.415, 333.570, 393.400, 157.005, 346.780, 351.913, 625.231, 236.205, 978.244, 501.251, 811.893, 625.792, 878.677, 890.402, 814.859, 29.466, 554.939, 280.194, 151.708, 897.175, 656.932, 87.795, 382.207, 960.606, 612.448, 625.525, 227.428, 240.361, 152.749, 970.537, 909.735, 329.285, 542.234, 206.757, 138.577, 541.354, 800.105, 862.588, 308.998, 705.136, 523.805, 135.252, 995.686, 975.777, 145.152, 933.030, 917.112, 317.496,), (557.340, 948.602, 118.387, 317.598, 879.638, 727.080, 765.435, 880.132, 414.040, 411.252, 443.004, 933.769, 894.130, 933.250, 273.796, 779.108, 106.771, 184.746, 762.447, 611.982, 266.863, 567.043, 230.911, 232.183, 687.378, 359.261, 688.141, 476.602, 502.325, 604.712, 712.019, 373.959, 852.129, 491.446, 137.513, 193.260, 32.230, 764.539, 15.014, 269.859, 413.044, 742.367, 988.243, 757.777, 66.136, 927.103, 985.628, 867.129, 489.943, 324.896,), (457.545, 246.784, 404.867, 41.826, 735.332, 380.374, 312.897, 611.505, 742.468, 593.806, 525.231, 875.783, 786.920, 520.649, 451.610, 827.010, 42.416, 995.836, 518.703, 395.611, 735.170, 557.701, 516.127, 630.649, 49.295, 291.179, 398.040, 304.554, 827.621, 461.347, 422.462, 613.134, 54.547, 516.970, 142.240, 829.894, 451.698, 722.647, 113.172, 778.731, 937.842, 696.114, 135.307, 413.559, 450.886, 178.879, 590.315, 712.634, 201.932, 454.683,), (250.082, 691.717, 907.162, 796.486, 718.375, 123.544, 114.165, 449.398, 362.943, 523.817, 384.087, 791.067, 511.666, 949.754, 378.679, 380.607, 768.260, 912.253, 565.492, 659.530, 150.090, 868.819, 178.867, 712.047, 419.582, 309.518, 768.630, 446.258, 626.453, 117.554, 131.571, 202.852, 622.560, 253.111, 458.643, 855.411, 543.501, 5.751, 882.703, 237.878, 588.862, 475.712, 410.151, 79.405, 600.102, 244.713, 547.342, 619.766, 557.549, 825.253,), (48.919, 148.256, 653.277, 36.721, 853.829, 666.997, 838.014, 298.442, 920.385, 49.031, 416.957, 178.248, 672.090, 611.090, 691.514, 594.969, 787.308, 177.419, 455.338, 578.966, 931.480, 93.126, 299.294, 368.569, 378.939, 67.667, 427.324, 550.471, 292.833, 134.546, 694.619, 274.419, 527.053, 524.646, 695.829, 612.054, 109.302, 729.729, 628.979, 987.189, 483.663, 688.654, 933.892, 986.278, 287.187, 608.845, 316.489, 525.391, 995.024, 353.853,)))"
+ ]
+ },
+ "execution_count": 43,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "m"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Matrix((639.4, ...), ..., (..., 353.9))[100x50]\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Similarily, `v` is now a `Vector` with $50$ entries."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "v = Vector(1_000 * random.random() for _ in range(50))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Vector((129.713, 562.634, 519.706, 631.858, 492.504, 179.907, 609.406, 708.587, 979.258, 1.581, 23.987, 625.461, 117.926, 848.070, 799.564, 998.987, 414.041, 333.792, 560.416, 637.504, 11.297, 201.187, 281.627, 790.196, 307.773, 506.690, 323.924, 6.131, 685.836, 341.362, 724.397, 615.993, 29.117, 175.629, 330.515, 337.937, 672.473, 916.163, 797.254, 645.652, 481.496, 627.200, 892.058, 536.968, 335.110, 783.989, 413.953, 742.585, 835.106, 299.344))"
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "v"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Vector(129.7, ..., 299.3)[50]\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(v)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "The arithmetic works as before."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "w = m * v"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Vector(11378937.3, ..., 13593029.3)[100]\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(w)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "We can multiply `m` with its transpose or the other way round."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "n = m * m.transpose()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Matrix((14370711.3, ...), ..., (..., 16545418.2))[100x100]\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(n)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 52,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "o = m.transpose() * m"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 53,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Matrix((32618511.5, ...), ..., (..., 32339164.8))[50x50]\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(o)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## Comparison with [numpy](https://www.numpy.org/)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "We started out in this chapter by realizing that Python provides us no good data type to model a vector $\\vec{x}$ or a matrix $\\bf{A}$. Then, we built up two custom data types, `Vector` and `Matrix`, that wrap a simple `tuple` object for $\\vec{x}$ and a `tuple` of `tuple`s for $\\bf{A}$ so that we can interact with their `._entries` in a \"natural\" way, which is similar to how we write linear algebra tasks by hand. By doing this, we extend Python with our own little \"dialect\" or **[domain-specific language ](https://en.wikipedia.org/wiki/Domain-specific_language)** (DSL).\n",
+ "\n",
+ "If we feel like sharing our linear algebra library with the world, we could easily do so on either [GitHub ](https://github.com) or [PyPI](https://pypi.org). However, for the domain of linear algebra this would be rather pointless as there is already a widely adopted library with [numpy](https://www.numpy.org/) that not only has a lot more features than ours but also is implemented in C, which makes it a lot faster with big data.\n",
+ "\n",
+ "Let's model the example in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb#Example:-Vectors-&-Matrices) with both [numpy](https://www.numpy.org/) and our own DSL and compare them."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 54,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "x = (1, 2, 3)\n",
+ "A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "ename": "TypeError",
+ "evalue": "can't multiply sequence by non-int of type 'tuple'",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mA\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;31mTypeError\u001b[0m: can't multiply sequence by non-int of type 'tuple'"
+ ]
+ }
+ ],
+ "source": [
+ "A * x"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "The creation of vectors and matrices is similar to our DSL. However, numpy uses the more general concept of an **n-dimensional array** (i.e., the `ndarray` type) where a vector is only a special case of a matrix and a matrix is yet another special case of an even higher dimensional structure."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 56,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import numpy as np"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "x_arr = np.array(x)\n",
+ "A_arr = np.array(A)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 58,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "x_vec = Vector(x)\n",
+ "A_mat = Matrix(A)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "The text representations are very similar. However, [numpy](https://www.numpy.org/)'s `ndarray`s keep the entries as `int`s while our `Vector` and `Matrix` objects contain `float`s."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 59,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([1, 2, 3])"
+ ]
+ },
+ "execution_count": 59,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "x_arr"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 60,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Vector((1.000, 2.000, 3.000))"
+ ]
+ },
+ "execution_count": 60,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "x_vec"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 61,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([[1, 2, 3],\n",
+ " [4, 5, 6],\n",
+ " [7, 8, 9]])"
+ ]
+ },
+ "execution_count": 61,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "A_arr"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 62,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
+ ]
+ },
+ "execution_count": 62,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "A_mat"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "[numpy](https://www.numpy.org/)'s `ndarray`s come with a `.shape` instance attribute that returns a `tuple` with the dimensions ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 63,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(3,)"
+ ]
+ },
+ "execution_count": 63,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "x_arr.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 64,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(3, 3)"
+ ]
+ },
+ "execution_count": 64,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "A_arr.shape"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "... while `Matrix` objects come with `.n_rows` and `.n_cols` properties."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 65,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(3, 3)"
+ ]
+ },
+ "execution_count": 65,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "A_mat.n_rows, A_mat.n_cols"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "The built-in [len() ](https://docs.python.org/3/library/functions.html#len) function does not return the number of entries in an `ndarray` but the number of the rows instead. This is equivalent to the first element in the `.shape` attribute."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "3"
+ ]
+ },
+ "execution_count": 66,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "len(x_arr)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 67,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "3"
+ ]
+ },
+ "execution_count": 67,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "len(x_vec)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "3"
+ ]
+ },
+ "execution_count": 68,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "len(A_arr)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 69,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "9"
+ ]
+ },
+ "execution_count": 69,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "len(A_mat)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "The `.transpose()` method also exists for `ndarray`s."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 70,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([[1, 4, 7],\n",
+ " [2, 5, 8],\n",
+ " [3, 6, 9]])"
+ ]
+ },
+ "execution_count": 70,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "A_arr.transpose()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 71,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))"
+ ]
+ },
+ "execution_count": 71,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "A_mat.transpose()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "To perform matrix-matrix, matrix-vector, or vector-matrix multiplication in [numpy](https://www.numpy.org/), we use the `.dot()` method. If we use the `*` operator with `ndarray`s, an *entry-wise* multiplication is performed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 72,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([14, 32, 50])"
+ ]
+ },
+ "execution_count": 72,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "A_arr.dot(x_arr)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 73,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([[ 1, 4, 9],\n",
+ " [ 4, 10, 18],\n",
+ " [ 7, 16, 27]])"
+ ]
+ },
+ "execution_count": 73,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "A_arr * x_arr"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 74,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Vector((14.000, 32.000, 50.000))"
+ ]
+ },
+ "execution_count": 74,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "A_mat * x_vec"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "Scalar multiplication, however, works as expected."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([10, 20, 30])"
+ ]
+ },
+ "execution_count": 75,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "10 * x_arr"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 76,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Vector((10.000, 20.000, 30.000))"
+ ]
+ },
+ "execution_count": 76,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "10 * x_vec"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "Because we implemented our classes to support the sequence protocol, [numpy](https://www.numpy.org/)'s *one*-dimensional `ndarray`s are actually able to work with them: The `*` operator is applied on a per-entry basis."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 77,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([2., 4., 6.])"
+ ]
+ },
+ "execution_count": 77,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "x_arr + x_vec"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 78,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([1., 4., 9.])"
+ ]
+ },
+ "execution_count": 78,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "x_arr * x_vec"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 79,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "ename": "ValueError",
+ "evalue": "operands could not be broadcast together with shapes (3,3) (9,) ",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mA_arr\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mA_mat\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;31mValueError\u001b[0m: operands could not be broadcast together with shapes (3,3) (9,) "
+ ]
+ }
+ ],
+ "source": [
+ "A_arr + A_mat"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "We conclude that it is rather easy to extend Python in a way that makes the resulting application code read like core Python again. As there are many well established third-party packages out there, it is unlikely that we have to implement a fundamental library ourselves. Yet, we can apply the concepts introduced in this chapter to organize the code in the applications we write."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.6"
+ },
+ "livereveal": {
+ "auto_select": "code",
+ "auto_select_fragment": true,
+ "scroll": true,
+ "theme": "serif"
+ },
+ "toc": {
+ "base_numbering": 1,
+ "nav_menu": {},
+ "number_sections": false,
+ "sideBar": true,
+ "skip_h1_title": true,
+ "title_cell": "Table of Contents",
+ "title_sidebar": "Contents",
+ "toc_cell": false,
+ "toc_position": {
+ "height": "calc(100% - 180px)",
+ "left": "10px",
+ "top": "150px",
+ "width": "384px"
+ },
+ "toc_section_display": false,
+ "toc_window_display": false
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/11_classes/sample_package/__init__.py b/11_classes/sample_package/__init__.py
index 1e8d12c..8e87577 100644
--- a/11_classes/sample_package/__init__.py
+++ b/11_classes/sample_package/__init__.py
@@ -28,3 +28,7 @@ from sample_package.vector import Vector
__name__ = "linear_algebra_tools"
__version__ = "0.1.0" # see https://semver.org/ for how the format works
__author__ = "Alexander Hess"
+
+# Define what is imported with the "star import"
+# (i.e., with `from sample_package import *`).
+__all__ = ["Matrix", "Vector"]
diff --git a/11_classes/sample_package/matrix.py b/11_classes/sample_package/matrix.py
index 65f5fc1..3c82d26 100644
--- a/11_classes/sample_package/matrix.py
+++ b/11_classes/sample_package/matrix.py
@@ -17,12 +17,14 @@ class Matrix:
defaults to tuple
typing (callable): type casting applied to all entries upon creation;
defaults to float
+ vector_cls (vector.Vector): a reference to the Vector class to work with
zero_threshold (float): max. tolerance when comparing an entry to zero;
defaults to 1e-12
"""
storage = utils.DEFAULT_ENTRIES_STORAGE
typing = utils.DEFAULT_ENTRY_TYPE
+ # the `vector_cls` attribute is set at the bottom of this file
zero_threshold = utils.ZERO_THRESHOLD
def __init__(self, data):
@@ -156,7 +158,7 @@ class Matrix:
Returns:
rows (generator): produces a Matrix's rows as Vectors
"""
- return (Vector(r) for r in self._entries)
+ return (self.vector_cls(r) for r in self._entries)
def cols(self):
"""Loop over a Matrix's columns.
@@ -165,7 +167,7 @@ class Matrix:
columns (generator): produces a Matrix's columns as Vectors
"""
return (
- Vector(self._entries[r][c] for r in range(self.n_rows))
+ self.vector_cls(self._entries[r][c] for r in range(self.n_rows))
for c in range(self.n_cols)
)
@@ -232,7 +234,7 @@ class Matrix:
def __radd__(self, other):
"""See docstring for .__add__()."""
- if isinstance(other, Vector):
+ if isinstance(other, self.vector_cls):
raise TypeError("vectors and matrices cannot be added")
# As both matrix and broadcasting addition are commutative,
# we dispatch to .__add__().
@@ -260,7 +262,7 @@ class Matrix:
def __rsub__(self, other):
"""See docstring for .__sub__()."""
- if isinstance(other, Vector):
+ if isinstance(other, self.vector_cls):
raise TypeError("vectors and matrices cannot be subtracted")
# Same comments as in .__sub__() apply
# with the roles of self and other swapped.
@@ -294,6 +296,8 @@ class Matrix:
Matrix-vector and vector-matrix multiplication are not commutative.
+ >>> from sample_package import Vector
+
>>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6])
Vector((17.000, 39.000))
@@ -304,7 +308,7 @@ class Matrix:
if isinstance(other, numbers.Number):
return self.__class__((x * other for x in r) for r in self._entries)
# Matrix-vector multiplication: Vector is a column Vector
- elif isinstance(other, Vector):
+ elif isinstance(other, self.vector_cls):
# First, cast the other Vector as a Matrix, then do matrix-matrix
# multiplication, and lastly return the result as a Vector again.
return self._matrix_multiply(other.as_matrix()).as_vector()
@@ -319,7 +323,7 @@ class Matrix:
if isinstance(other, numbers.Number):
return self * other
# Vector-matrix multiplication: Vector is a row Vector
- elif isinstance(other, Vector):
+ elif isinstance(other, self.vector_cls):
return other.as_matrix(column=False)._matrix_multiply(self).as_vector()
return NotImplemented
@@ -375,7 +379,7 @@ class Matrix:
def __abs__(self):
"""The Frobenius norm of a Matrix."""
- return utils.norm(self) # use the norm() function shared with the Vector class
+ return utils.norm(self) # uses the norm() function shared vector.Vector
def __bool__(self):
"""A Matrix is truthy if its Frobenius norm is strictly positive."""
@@ -398,7 +402,7 @@ class Matrix:
"""Get a Vector representation of a Matrix.
Returns:
- vector (Vector)
+ vector (vector.Vector)
Raises:
RuntimeError: if one of the two dimensions, .n_rows or .n_cols, is not 1
@@ -409,7 +413,7 @@ class Matrix:
"""
if not (self.n_rows == 1 or self.n_cols == 1):
raise RuntimeError("one dimension (m or n) must be 1")
- return Vector(x for x in self)
+ return self.vector_cls(x for x in self)
def transpose(self):
"""Switch the rows and columns of a Matrix.
@@ -432,4 +436,8 @@ class Matrix:
# We call that a circular import. Whereas Python handles "circular" references
# (e.g., both the Matrix and Vector classes have methods that reference the
# respective other class), that is forbidden for imports.
-from sample_package.vector import Vector
+from sample_package import vector
+
+# This attribute cannot be set in the class definition
+# as the vector module is only imported down here.
+Matrix.vector_cls = vector.Vector
diff --git a/11_classes/sample_package/vector.py b/11_classes/sample_package/vector.py
index 874d29a..21e3729 100644
--- a/11_classes/sample_package/vector.py
+++ b/11_classes/sample_package/vector.py
@@ -7,8 +7,8 @@ import numbers
# If third-party libraries are needed, they are
# put into a group on their own in between.
# Within a group, imports are sorted lexicographically.
+from sample_package import matrix
from sample_package import utils
-from sample_package.matrix import Matrix
class Vector:
@@ -17,6 +17,7 @@ class Vector:
All entries are converted to floats, or whatever is set in the typing attribute.
Attributes:
+ matrix_cls (matrix.Matrix): a reference to the Matrix class to work with
storage (callable): data type used to store the entries internally;
defaults to tuple
typing (callable): type casting applied to all entries upon creation;
@@ -25,6 +26,7 @@ class Vector:
defaults to 1e-12
"""
+ matrix_cls = matrix.Matrix
storage = utils.DEFAULT_ENTRIES_STORAGE
typing = utils.DEFAULT_ENTRY_TYPE
zero_threshold = utils.ZERO_THRESHOLD
@@ -219,7 +221,7 @@ class Vector:
def __abs__(self):
"""The Euclidean norm of a vector."""
- return utils.norm(self) # use the norm() function shared with the Matrix class
+ return utils.norm(self) # uses the norm() function shared matrix.Matrix
def __bool__(self):
"""A Vector is truthy if its Euclidean norm is strictly positive."""
@@ -246,7 +248,7 @@ class Vector:
column vector or a row vector; defaults to True
Returns:
- matrix (Matrix)
+ matrix (matrix.Matrix)
Example Usage:
>>> v = Vector([1, 2, 3])
@@ -256,5 +258,5 @@ class Vector:
Matrix(((1.000, 2.000, 3.000,)))
"""
if column:
- return Matrix([x] for x in self)
- return Matrix([(x for x in self)])
+ return self.matrix_cls([x] for x in self)
+ return self.matrix_cls([(x for x in self)])
diff --git a/CONTENTS.md b/CONTENTS.md
index fed0b64..be3d163 100644
--- a/CONTENTS.md
+++ b/CONTENTS.md
@@ -284,3 +284,8 @@ If this is not possible,
[](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/03_content.ipynb)
(Operator Overloading: Arithmetic & Relational Operators;
Number Emulation)
+ - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb)
+ [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/04_content.ipynb)
+ (Writing one's own Packages;
+ The final `Vector` & `Matrix` Classes;
+ Comparison with `numpy`)