Add sample_package with linear algebra tools

- add sample_package:
  + __init__.py => high-level description and imports
  + matrix.py => define a Matrix class
  + vector.py => define a Vector class
  + utils.py => package-wide utilities
- streamline the code snippets in the chapter 11 notebooks
  to align with the sample_package
This commit is contained in:
Alexander Hess 2020-10-27 16:41:22 +01:00
parent 2c83a883c5
commit 6bcfff481c
Signed by: alexander
GPG key ID: 344EA5AB10D868E0
7 changed files with 770 additions and 40 deletions

View file

@ -272,7 +272,7 @@
"outputs": [], "outputs": [],
"source": [ "source": [
"class Vector:\n", "class Vector:\n",
" \"\"\"A standard one-dimensional vector from linear algebra.\"\"\"\n", " \"\"\"A one-dimensional vector from linear algebra.\"\"\"\n",
"\n", "\n",
" dummy_variable = \"I am a vector\"\n", " dummy_variable = \"I am a vector\"\n",
"\n", "\n",
@ -403,7 +403,7 @@
"data": { "data": {
"text/plain": [ "text/plain": [
"\u001b[0;31mInit signature:\u001b[0m \u001b[0mVector\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;31mInit signature:\u001b[0m \u001b[0mVector\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;31mDocstring:\u001b[0m A standard one-dimensional vector from linear algebra.\n", "\u001b[0;31mDocstring:\u001b[0m A one-dimensional vector from linear algebra.\n",
"\u001b[0;31mType:\u001b[0m type\n", "\u001b[0;31mType:\u001b[0m type\n",
"\u001b[0;31mSubclasses:\u001b[0m \n" "\u001b[0;31mSubclasses:\u001b[0m \n"
] ]
@ -432,7 +432,7 @@
"Help on class Vector in module __main__:\n", "Help on class Vector in module __main__:\n",
"\n", "\n",
"class Vector(builtins.object)\n", "class Vector(builtins.object)\n",
" | A standard one-dimensional vector from linear algebra.\n", " | A one-dimensional vector from linear algebra.\n",
" | \n", " | \n",
" | Methods defined here:\n", " | Methods defined here:\n",
" | \n", " | \n",
@ -484,7 +484,7 @@
"data": { "data": {
"text/plain": [ "text/plain": [
"mappingproxy({'__module__': '__main__',\n", "mappingproxy({'__module__': '__main__',\n",
" '__doc__': 'A standard one-dimensional vector from linear algebra.',\n", " '__doc__': 'A one-dimensional vector from linear algebra.',\n",
" 'dummy_variable': 'I am a vector',\n", " 'dummy_variable': 'I am a vector',\n",
" 'dummy_method': <function __main__.Vector.dummy_method(self)>,\n", " 'dummy_method': <function __main__.Vector.dummy_method(self)>,\n",
" '__dict__': <attribute '__dict__' of 'Vector' objects>,\n", " '__dict__': <attribute '__dict__' of 'Vector' objects>,\n",
@ -687,7 +687,7 @@
"outputs": [], "outputs": [],
"source": [ "source": [
"class Vector:\n", "class Vector:\n",
" \"\"\"A standard one-dimensional vector from linear algebra.\n", " \"\"\"A one-dimensional vector from linear algebra.\n",
"\n", "\n",
" All entries are converted to floats.\n", " All entries are converted to floats.\n",
" \"\"\"\n", " \"\"\"\n",

View file

@ -1815,17 +1815,16 @@
}, },
"outputs": [], "outputs": [],
"source": [ "source": [
"def norm(vector_or_matrix):\n", "def norm(vec_or_mat):\n",
" \"\"\"Calculate the Frobenius or Euclidean norm of a matrix or vector.\n", " \"\"\"Calculate the Frobenius or Euclidean norm of a matrix or vector.\n",
"\n", "\n",
" Args:\n", " Args:\n",
" vector_or_matrix (Vector/Matrix): the entries whose squares\n", " vec_or_mat (Vector / Matrix): object whose entries are squared and summed up\n",
" are summed up\n",
"\n", "\n",
" Returns:\n", " Returns:\n",
" norm (float)\n", " norm (float)\n",
" \"\"\"\n", " \"\"\"\n",
" return math.sqrt(sum(x ** 2 for x in vector_or_matrix))" " return math.sqrt(sum(x ** 2 for x in vec_or_mat))"
] ]
}, },
{ {

View file

@ -694,7 +694,7 @@
" def __add__(self, other):\n", " def __add__(self, other):\n",
" if isinstance(other, self.__class__): # vector addition\n", " if isinstance(other, self.__class__): # vector addition\n",
" if len(self) != len(other):\n", " if len(self) != len(other):\n",
" raise ValueError(\"vectors need to be of the same length\")\n", " raise ValueError(\"vectors must be of the same length\")\n",
" return Vector(x + y for (x, y) in zip(self, other))\n", " return Vector(x + y for (x, y) in zip(self, other))\n",
" elif isinstance(other, numbers.Number): # broadcasting addition\n", " elif isinstance(other, numbers.Number): # broadcasting addition\n",
" return Vector(x + other for x in self)\n", " return Vector(x + other for x in self)\n",
@ -706,7 +706,7 @@
" def __sub__(self, other):\n", " def __sub__(self, other):\n",
" if isinstance(other, self.__class__): # vector subtraction\n", " if isinstance(other, self.__class__): # vector subtraction\n",
" if len(self) != len(other):\n", " if len(self) != len(other):\n",
" raise ValueError(\"vectors need to be of the same length\")\n", " raise ValueError(\"vectors must be of the same length\")\n",
" return Vector(x - y for (x, y) in zip(self, other))\n", " return Vector(x - y for (x, y) in zip(self, other))\n",
" elif isinstance(other, numbers.Number): # broadcasting subtraction\n", " elif isinstance(other, numbers.Number): # broadcasting subtraction\n",
" return Vector(x - other for x in self)\n", " return Vector(x - other for x in self)\n",
@ -721,7 +721,7 @@
" def __mul__(self, other):\n", " def __mul__(self, other):\n",
" if isinstance(other, self.__class__): # dot product\n", " if isinstance(other, self.__class__): # dot product\n",
" if len(self) != len(other):\n", " if len(self) != len(other):\n",
" raise ValueError(\"vectors need to be of the same length\")\n", " raise ValueError(\"vectors must be of the same length\")\n",
" return sum(x * y for (x, y) in zip(self, other))\n", " return sum(x * y for (x, y) in zip(self, other))\n",
" elif isinstance(other, numbers.Number): # scalar multiplication\n", " elif isinstance(other, numbers.Number): # scalar multiplication\n",
" return Vector(x * other for x in self)\n", " return Vector(x * other for x in self)\n",
@ -872,14 +872,14 @@
"outputs": [ "outputs": [
{ {
"ename": "ValueError", "ename": "ValueError",
"evalue": "vectors need to be of the same length", "evalue": "vectors must be of the same length",
"output_type": "error", "output_type": "error",
"traceback": [ "traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-27-21fab2d5f12e>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mw\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m<ipython-input-27-21fab2d5f12e>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mw\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-21-12852bd26164>\u001b[0m in \u001b[0;36m__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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[0;34m:\u001b[0m \u001b[0;31m# dot product\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 46\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[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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;32m---> 47\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors need to be of the same length\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 48\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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[1;32m 49\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# scalar multiplication\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m<ipython-input-21-e7b5dd8e219a>\u001b[0m in \u001b[0;36m__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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[0;34m:\u001b[0m \u001b[0;31m# dot product\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 46\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[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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;32m---> 47\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors must be of the same length\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 48\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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[1;32m 49\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# scalar multiplication\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mValueError\u001b[0m: vectors need to be of the same length" "\u001b[0;31mValueError\u001b[0m: vectors must be of the same length"
] ]
} }
], ],
@ -992,14 +992,14 @@
"outputs": [ "outputs": [
{ {
"ename": "ValueError", "ename": "ValueError",
"evalue": "vectors need to be of the same length", "evalue": "vectors must be of the same length",
"output_type": "error", "output_type": "error",
"traceback": [ "traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-31-490ee3f2b9e8>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mv\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mw\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m<ipython-input-31-490ee3f2b9e8>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mv\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mw\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-21-12852bd26164>\u001b[0m in \u001b[0;36m__add__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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[0;34m:\u001b[0m \u001b[0;31m# vector addition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\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[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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;32m---> 20\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors need to be of the same length\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 21\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mVector\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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[1;32m 22\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# broadcasting addition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m<ipython-input-21-e7b5dd8e219a>\u001b[0m in \u001b[0;36m__add__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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[0;34m:\u001b[0m \u001b[0;31m# vector addition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\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[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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;32m---> 20\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors must be of the same length\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 21\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mVector\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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[1;32m 22\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# broadcasting addition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mValueError\u001b[0m: vectors need to be of the same length" "\u001b[0;31mValueError\u001b[0m: vectors must be of the same length"
] ]
} }
], ],
@ -1171,7 +1171,7 @@
" def __add__(self, other):\n", " def __add__(self, other):\n",
" if isinstance(other, self.__class__): # matrix addition\n", " if isinstance(other, self.__class__): # matrix addition\n",
" if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n", " if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n",
" raise ValueError(\"matrices need to be of the same dimensions\")\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", " return Matrix((s_col + o_col for (s_col, o_col) in zip(s_row, o_row))\n",
" for (s_row, o_row) in zip(self._entries, other._entries))\n", " for (s_row, o_row) in zip(self._entries, other._entries))\n",
" elif isinstance(other, numbers.Number): # broadcasting addition\n", " elif isinstance(other, numbers.Number): # broadcasting addition\n",
@ -1186,7 +1186,7 @@
" def __sub__(self, other):\n", " def __sub__(self, other):\n",
" if isinstance(other, self.__class__): # matrix subtraction\n", " if isinstance(other, self.__class__): # matrix subtraction\n",
" if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n", " if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n",
" raise ValueError(\"matrices need to be of the same dimensions\")\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", " return Matrix((s_col - o_col for (s_col, o_col) in zip(s_row, o_row))\n",
" for (s_row, o_row) in zip(self._entries, other._entries))\n", " for (s_row, o_row) in zip(self._entries, other._entries))\n",
" elif isinstance(other, numbers.Number): # broadcasting subtraction\n", " elif isinstance(other, numbers.Number): # broadcasting subtraction\n",
@ -1203,7 +1203,7 @@
" \n", " \n",
" def _matrix_multiply(self, other):\n", " def _matrix_multiply(self, other):\n",
" if self.n_cols != other.n_rows:\n", " if self.n_cols != other.n_rows:\n",
" raise ValueError(\"matrices need to have compatible dimensions\")\n", " raise ValueError(\"matrices must have compatible dimensions\")\n",
" return Matrix((rv * cv for cv in other.cols()) for rv in self.rows())\n", " return Matrix((rv * cv for cv in other.cols()) for rv in self.rows())\n",
"\n", "\n",
" def __mul__(self, other):\n", " def __mul__(self, other):\n",
@ -1387,15 +1387,15 @@
"outputs": [ "outputs": [
{ {
"ename": "ValueError", "ename": "ValueError",
"evalue": "matrices need to have compatible dimensions", "evalue": "matrices must have compatible dimensions",
"output_type": "error", "output_type": "error",
"traceback": [ "traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-41-b57a49d097c9>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mm\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mn\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m<ipython-input-41-b57a49d097c9>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mm\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mn\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-35-f4e0bb37caf6>\u001b[0m in \u001b[0;36m__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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[1;32m 73\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 74\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 75\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mNotImplemented\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m<ipython-input-35-def82d7b791b>\u001b[0m in \u001b[0;36m__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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[1;32m 73\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 74\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 75\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mNotImplemented\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m<ipython-input-35-f4e0bb37caf6>\u001b[0m in \u001b[0;36m_matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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[1;32m 64\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"matrices need to have compatible dimensions\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 66\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrows\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[1;32m 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m<ipython-input-35-def82d7b791b>\u001b[0m in \u001b[0;36m_matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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[1;32m 64\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"matrices must have compatible dimensions\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 66\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrows\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[1;32m 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mValueError\u001b[0m: matrices need to have compatible dimensions" "\u001b[0;31mValueError\u001b[0m: matrices must have compatible dimensions"
] ]
} }
], ],
@ -1497,15 +1497,15 @@
"outputs": [ "outputs": [
{ {
"ename": "ValueError", "ename": "ValueError",
"evalue": "matrices need to have compatible dimensions", "evalue": "matrices must have compatible dimensions",
"output_type": "error", "output_type": "error",
"traceback": [ "traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-45-9faf2ee0ae54>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mn\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m<ipython-input-45-9faf2ee0ae54>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mn\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-35-f4e0bb37caf6>\u001b[0m in \u001b[0;36m__rmul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 79\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mVector\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;32m---> 81\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcolumn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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[0m\u001b[1;32m 82\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mNotImplemented\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m<ipython-input-35-def82d7b791b>\u001b[0m in \u001b[0;36m__rmul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 79\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mVector\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;32m---> 81\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcolumn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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[0m\u001b[1;32m 82\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mNotImplemented\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m<ipython-input-35-f4e0bb37caf6>\u001b[0m in \u001b[0;36m_matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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[1;32m 64\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"matrices need to have compatible dimensions\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 66\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrows\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[1;32m 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m<ipython-input-35-def82d7b791b>\u001b[0m in \u001b[0;36m_matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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[1;32m 64\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"matrices must have compatible dimensions\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 66\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrows\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[1;32m 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mValueError\u001b[0m: matrices need to have compatible dimensions" "\u001b[0;31mValueError\u001b[0m: matrices must have compatible dimensions"
] ]
} }
], ],
@ -1736,7 +1736,7 @@
" def __eq__(self, other):\n", " def __eq__(self, other):\n",
" if isinstance(other, self.__class__):\n", " if isinstance(other, self.__class__):\n",
" if len(self) != len(other):\n", " if len(self) != len(other):\n",
" raise ValueError(\"vectors need to be of the same length\")\n", " raise ValueError(\"vectors must be of the same length\")\n",
" for x, y in zip(self, other):\n", " for x, y in zip(self, other):\n",
" if abs(x - y) > self.zero_threshold:\n", " if abs(x - y) > self.zero_threshold:\n",
" return False # exit early if two corresponding entries differ\n", " return False # exit early if two corresponding entries differ\n",
@ -1972,12 +1972,9 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"def norm(vector_or_matrix):\n", "def norm(vec_or_mat):\n",
" \"\"\"Calculate the Frobenius or Euclidean norm of a matrix or vector.\n", " \"\"\"Calculate the Frobenius or Euclidean norm of a matrix or vector.\"\"\"\n",
" \n", " return math.sqrt(sum(x ** 2 for x in vec_or_mat))"
" ...\n",
" \"\"\"\n",
" return math.sqrt(sum(x ** 2 for x in vector_or_matrix))"
] ]
}, },
{ {
@ -2020,7 +2017,7 @@
"\n", "\n",
" def __float__(self):\n", " def __float__(self):\n",
" if len(self) != 1:\n", " if len(self) != 1:\n",
" raise RuntimeError(\"vector must have one entry to become a scalar\")\n", " raise RuntimeError(\"vector must have exactly one entry to become a scalar\")\n",
" return self._entries[0]" " return self._entries[0]"
] ]
}, },
@ -2303,14 +2300,14 @@
"outputs": [ "outputs": [
{ {
"ename": "RuntimeError", "ename": "RuntimeError",
"evalue": "vector must have one entry to become a scalar", "evalue": "vector must have exactly one entry to become a scalar",
"output_type": "error", "output_type": "error",
"traceback": [ "traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)", "\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-75-8369cec552f3>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m<ipython-input-75-8369cec552f3>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-63-01438fec4aa0>\u001b[0m in \u001b[0;36m__float__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__float__\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[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 30\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;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 31\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vector must have one entry to become a scalar\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 32\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m<ipython-input-63-67b90fc89e7c>\u001b[0m in \u001b[0;36m__float__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__float__\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[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 30\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;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 31\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vector must have exactly one entry to become a scalar\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 32\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mRuntimeError\u001b[0m: vector must have one entry to become a scalar" "\u001b[0;31mRuntimeError\u001b[0m: vector must have exactly one entry to become a scalar"
] ]
} }
], ],

View file

@ -0,0 +1,30 @@
"""This package provides linear algebra functionalities.
The package is split into three modules:
- matrix: defines the Matrix class
- vector: defines the Vector class
- utils: defines the norm() function that is shared by Matrix and Vector
and package-wide constants
The classes implement arithmetic operations involving vectors and matrices.
See the docstrings in the modules and classes for further info.
"""
# Import the classes here so that they are available
# from the package's top level. That means that a user
# who imports this package with `import sample_package`
# may then refer to, for example, the Matrix class with
# simply `sample_package.Matrix` instead of the longer
# `sample_package.matrix.Matrix`.
from sample_package.matrix import Matrix
from sample_package.vector import Vector
# Define meta information for the package.
# There are other (and more modern) ways of
# doing this, but specifying the following
# dunder variables here is the traditional way.
__name__ = "linear_algebra_tools"
__version__ = "0.1.0" # see https://semver.org/ for how the format works
__author__ = "Alexander Hess"

View file

@ -0,0 +1,416 @@
"""This module defines a Matrix class."""
import numbers
# Note the import at the bottom of this file, and
# see the comments about imports in the matrix module.
from sample_package import utils
class Matrix:
"""An m-by-n-dimensional matrix from linear algebra.
All entries are converted to floats, or whatever is set in the typing attribute.
Attributes:
storage (callable): data type used to store the entries internally;
defaults to tuple
typing (callable): type casting applied to all entries upon creation;
defaults to float
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
zero_threshold = utils.ZERO_THRESHOLD
def __init__(self, data):
"""Create a new matrix.
Args:
data (sequence of sequences): the matrix's entries;
viewed as a sequence of the matrix's rows (i.e., row-major order);
use the .from_columns() class method if the data come as a sequence
of the matrix's columns (i.e., column-major order)
Raises:
ValueError:
- if no entries are provided
- if the number of columns is inconsistent across the rows
"""
self._entries = self.storage(
self.storage(self.typing(x) for x in r) for r in data
)
for row in self._entries[1:]:
if len(row) != self.n_cols:
raise ValueError("rows must have the same number of entries")
if len(self) == 0:
raise ValueError("a matrix must have at least one entry")
@classmethod
def from_columns(cls, data):
"""Create a new matrix.
This is an alternative constructor for data provided in column-major order.
Args:
data (sequence of sequences): the matrix's entries;
viewed as a sequence of the matrix's columns (i.e., column-major order);
use the normal constructor method if the data come as a sequence
of the matrix's rows (i.e., row-major order)
Raises:
ValueError:
- if no entries are provided
- if the number of rows is inconsistent across the columns
"""
return cls(data).transpose()
@classmethod
def from_rows(cls, data):
"""See docstring for .__init__()."""
# Some users may want to use this .from_rows() constructor
# to explicitly communicate that the data are in row-major order.
# Otherwise, this method is redundant.
return cls(data)
def __repr__(self):
"""Text representation of a Matrix."""
name = self.__class__.__name__
args = ", ".join(
"(" + ", ".join(f"{c:.3f}" for c in r) + ",)" for r in self._entries
)
return f"{name}(({args}))"
def __str__(self):
"""Human-readable text representation of a Matrix."""
name = self.__class__.__name__
first, last, m, n = self[0], self[-1], self.n_rows, self.n_cols
return f"{name}(({first:.1f}, ...), ..., (..., {last:.1f}))[{m:d}x{n:d}]"
@property
def n_rows(self):
"""Number of rows in a Matrix."""
return len(self._entries)
@property
def n_cols(self):
"""Number of columns in a Matrix."""
return len(self._entries[0])
def __len__(self):
"""Number of entries in a Matrix."""
return self.n_rows * self.n_cols
def __getitem__(self, index):
"""Obtain an individual entry of a Matrix.
Args:
index (int / tuple of int's): if index is an integer,
the Matrix is viewed as a sequence in row-major order;
if index is a tuple of integers, the first one refers to
the row and the second one to the column of the entry
Returns:
entry (Matrix.typing)
Example Usage:
>>> m = Matrix([(1, 2), (3, 4)])
>>> m[0]
1.0
>>> m[-1]
4.0
>>> m[0, 1]
3.0
"""
# Sequence-like indexing (one-dimensional)
if isinstance(index, int):
if index < 0:
index += len(self)
if not (0 <= index < len(self)):
raise IndexError("integer index out of range")
row, col = divmod(index, self.n_cols)
return self._entries[row][col]
# Mathematical-like indexing (two-dimensional)
elif (
isinstance(index, tuple)
and len(index) == 2
and isinstance(index[0], int)
and isinstance(index[1], int)
):
return self._entries[index[0]][index[1]]
raise TypeError("index must be either an int or a tuple of two int's")
def rows(self):
"""Loop over a Matrix's rows.
Returns:
rows (generator): produces a Matrix's rows as Vectors
"""
return (Vector(r) for r in self._entries)
def cols(self):
"""Loop over a Matrix's columns.
Returns:
columns (generator): produces a Matrix's columns as Vectors
"""
return (
Vector(self._entries[r][c] for r in range(self.n_rows))
for c in range(self.n_cols)
)
def entries(self, *, reverse=False, row_major=True):
"""Loop over a Matrix's entries.
Args:
reverse (bool): flag to loop backwards; defaults to False
row_major (bool): flag to loop in row-major order; defaults to True
Returns:
entries (generator): produces a Matrix's entries
"""
if reverse:
rows = range(self.n_rows - 1, -1, -1)
cols = range(self.n_cols - 1, -1, -1)
else:
rows, cols = range(self.n_rows), range(self.n_cols)
if row_major:
return (self._entries[r][c] for r in rows for c in cols)
return (self._entries[r][c] for c in cols for r in rows)
def __iter__(self):
"""Loop over a Matrix's entries.
See .entries() for more customization options.
"""
return self.entries()
def __reversed__(self):
"""Loop over a Matrix's entries in reverse order.
See .entries() for more customization options.
"""
return self.entries(reverse=True)
def __add__(self, other):
"""Handle `self + other` and `other + self`.
This may be either matrix addition or broadcasting addition.
Example Usage:
>>> Matrix([(1, 2), (3, 4)]) + Matrix([(2, 3), (4, 5)])
Matrix(((3.0, 5.0), (7.0, 9.0)))
>>> Matrix([(1, 2), (3, 4)]) + 5
Matrix(((6.0, 7.0), (8.0, 9.0)))
>>> 10 + Matrix([(1, 2), (3, 4)])
Matrix(((11.0, 12.0), (13.0, 14.0)))
"""
# Matrix addition
if isinstance(other, self.__class__):
if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):
raise ValueError("matrices must have the same dimensions")
return self.__class__(
(s_col + o_col for (s_col, o_col) in zip(s_row, o_row))
for (s_row, o_row) in zip(self._entries, other._entries)
)
# Broadcasting addition
elif isinstance(other, numbers.Number):
return self.__class__((c + other for c in r) for r in self._entries)
return NotImplemented
def __radd__(self, other):
"""See docstring for .__add__()."""
if isinstance(other, Vector):
raise TypeError("vectors and matrices cannot be added")
# As both matrix and broadcasting addition are commutative,
# we dispatch to .__add__().
return self + other
def __sub__(self, other):
"""Handle `self - other` and `other - self`.
This may be either matrix subtraction or broadcasting subtraction.
Example Usage:
>>> Matrix([(2, 3), (4, 5)]) - Matrix([(1, 2), (3, 4)])
Matrix(((1.0, 1.0), (1.0, 1.0)))
>>> Matrix([(1, 2), (3, 4)]) - 1
Matrix(((0.0, 1.0), (2.0, 3.0)))
>>> 10 - Matrix([(1, 2), (3, 4)])
Matrix(((9.0, 8.0), (7.0, 6.0)))
"""
# As subtraction is the inverse of addition,
# we first dispatch to .__neg__() to invert the signs of
# all entries in other and then dispatch to .__add__().
return self + (-other)
def __rsub__(self, other):
"""See docstring for .__sub__()."""
if isinstance(other, Vector):
raise TypeError("vectors and matrices cannot be subtracted")
# Same comments as in .__sub__() apply
# with the roles of self and other swapped.
return (-self) + other
def _matrix_multiply(self, other):
"""Internal utility method to multiply to Matrix instances."""
if self.n_cols != other.n_rows:
raise ValueError("matrices must have compatible dimensions")
# Matrix-matrix multiplication means that each entry of the resulting
# Matrix is the dot product of the respective row of the "left" Matrix
# and column of the "right" Matrix. So, the rows/columns are represented
# by the Vector instances provided by the .cols() and .rows() methods.
return self.__class__((rv * cv for cv in other.cols()) for rv in self.rows())
def __mul__(self, other):
"""Handle `self * other` and `other * self`.
This may be either scalar multiplication, matrix-vector multiplication,
vector-matrix multiplication, or matrix-matrix multiplication.
Example Usage:
>>> Matrix([(1, 2), (3, 4)]) * Matrix([(1, 2), (3, 4)])
Matrix(((7.0, 10.0), (15.0, 22.0)))
>>> 2 * Matrix([(1, 2), (3, 4)])
Matrix(((2.0, 4.0), (6.0, 8.0)))
>>> Matrix([(1, 2), (3, 4)]) * 3
Matrix(((3.0, 6.0), (9.0, 12.0)))
Matrix-vector and vector-matrix multiplication are not commutative.
>>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6])
Vector((17.0, 39.0))
>>> Vector([5, 6]) * Matrix([(1, 2), (3, 4)])
Vector((23.0, 34.0))
"""
# Scalar multiplication
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):
# 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()
# Matrix-matrix multiplication
elif isinstance(other, self.__class__):
return self._matrix_multiply(other)
return NotImplemented
def __rmul__(self, other):
"""See docstring for .__mul__()."""
# As scalar multiplication is commutative, we dispatch to .__mul__().
if isinstance(other, numbers.Number):
return self * other
# Vector-matrix multiplication: Vector is a row Vector
elif isinstance(other, Vector):
return other.as_matrix(column=False)._matrix_multiply(self).as_vector()
return NotImplemented
def __truediv__(self, other):
"""Handle `self / other`.
Divide a Matrix by a scalar.
Example Usage:
>>> Matrix([(1, 2), (3, 4)]) / 4
Matrix([(0.25, 0.5), (0.75, 1.0)])
"""
# As scalar division division is the same as multiplication
# with the inverse, we dispatch to .__mul__().
if isinstance(other, numbers.Number):
return self * (1 / other)
return NotImplemented
def __eq__(self, other):
"""Handle `self == other`.
Compare two Matrix instances for equality.
Example Usage:
>>> Matrix([(1, 2), (3, 4)]) == Matrix([(1, 2), (3, 4)])
True
>>> Matrix([(1, 2), (3, 4)]) == Matrix([(5, 6), (7, 8)])
False
"""
if isinstance(other, self.__class__):
if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):
raise ValueError("matrices must have the same dimensions")
for x, y in zip(self, other):
if abs(x - y) > self.zero_threshold:
return False # exit early if two corresponding entries differ
return True
return NotImplemented
def __pos__(self):
"""Handle `+self`.
This is simply an identity operator returning the Matrix itself.
"""
return self
def __neg__(self):
"""Handle `-self`.
Negate all entries of a Matrix.
"""
return self.__class__((-x for x in r) for r in self._entries)
def __abs__(self):
"""The Frobenius norm of a Matrix."""
return utils.norm(self) # use the norm() function shared with the Vector class
def __bool__(self):
"""A Matrix is truthy if its Frobenius norm is strictly positive."""
return bool(abs(self))
def __float__(self):
"""Cast a Matrix as a scalar.
Returns:
scalar (float)
Raises:
RuntimeError: if the Matrix has more than one entry
"""
if not (self.n_rows == 1 and self.n_cols == 1):
raise RuntimeError("matrix must have exactly one entry to become a scalar")
return self[0]
def as_vector(self):
"""Get a Vector representation of a Matrix.
Returns:
vector (Vector)
Raises:
RuntimeError: if one of the two dimensions, .n_rows or .n_cols, is not 1
"""
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)
def transpose(self):
"""Switch the rows and columns of a Matrix.
Returns:
matrix (Matrix)
"""
return self.__class__(zip(*self._entries))
# This import needs to be made here as otherwise an ImportError is raised.
# That is so as both the matrix and vector modules import a class from each other.
# 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

View file

@ -0,0 +1,35 @@
"""This module provides utilities for the whole package.
The defined constants are used as defaults in the Vector and Matrix classes.
The norm() function is shared by Vector.__abs__() and Matrix.__abs__().
"""
import math
# Define constants (i.e., normal variables that are, by convention, named in UPPERCASE)
# that are used as the defaults for class attributes within Vector and Matrix.
DEFAULT_ENTRIES_STORAGE = tuple
DEFAULT_ENTRY_TYPE = float
ZERO_THRESHOLD = 1e-12
def norm(vec_or_mat):
"""Calculate the Frobenius or Euclidean norm of a matrix or vector.
Find more infos here: https://en.wikipedia.org/wiki/Matrix_norm#Frobenius_norm
Args:
vec_or_mat (Vector / Matrix): object whose entries are squared and summed up
Returns:
norm (float)
Example Usage:
As Vector and Matrix objects are by design non-empty sequences,
norm() may be called, for example, with `[3, 4]` as the argument:
>>> norm([3, 4])
5.0
"""
return math.sqrt(sum(x ** 2 for x in vec_or_mat))

View file

@ -0,0 +1,253 @@
"""This module defines a Vector class."""
# Imports from the standard library go first ...
import numbers
# ... and are followed by project-internal ones.
# 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 utils
from sample_package.matrix import Matrix
class Vector:
"""A one-dimensional vector from linear algebra.
All entries are converted to floats, or whatever is set in the typing attribute.
Attributes:
storage (callable): data type used to store the entries internally;
defaults to tuple
typing (callable): type casting applied to all entries upon creation;
defaults to float
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
zero_threshold = utils.ZERO_THRESHOLD
def __init__(self, data):
"""Create a new vector.
Args:
data (sequence): the vector's entries
Raises:
ValueError: if no entries are provided
Example Usage:
>>> Vector([1, 2, 3])
Vector((1.0, 2.0, 3.0))
>>> Vector(range(3))
Vector((0.0, 1.0, 2.0))
"""
self._entries = self.storage(self.typing(x) for x in data)
if len(self) == 0:
raise ValueError("a vector must have at least one entry")
def __repr__(self):
"""Text representation of a Vector."""
name = self.__class__.__name__
args = ", ".join(f"{x:.3f}" for x in self)
return f"{name}(({args}))"
def __str__(self):
"""Human-readable text representation of a Vector."""
name = self.__class__.__name__
first, last, n_entries = self[0], self[-1], len(self)
return f"{name}({first:.1f}, ..., {last:.1f})[{n_entries:d}]"
def __len__(self):
"""Number of entries in a Vector."""
return len(self._entries)
def __getitem__(self, index):
"""Obtain an individual entry of a Vector."""
if not isinstance(index, int):
raise TypeError("index must be an integer")
return self._entries[index]
def __iter__(self):
"""Loop over a Vector's entries."""
return iter(self._entries)
def __reversed__(self):
"""Loop over a Vector's entries in reverse order."""
return reversed(self._entries)
def __add__(self, other):
"""Handle `self + other` and `other + self`.
This may be either vector addition or broadcasting addition.
Example Usage:
>>> Vector([1, 2, 3]) + Vector([2, 3, 4])
Vector((3, 5, 7))
>>> Vector([1, 2, 3]) + 4
Vector((5, 6, 7))
>>> 10 + Vector([1, 2, 3])
Vector((11, 12, 13))
"""
# Vector addition
if isinstance(other, self.__class__):
if len(self) != len(other):
raise ValueError("vectors must be of the same length")
return self.__class__(x + y for (x, y) in zip(self, other))
# Broadcasting addition
elif isinstance(other, numbers.Number):
return self.__class__(x + other for x in self)
return NotImplemented
def __radd__(self, other):
"""See docstring for .__add__()."""
# As both vector and broadcasting addition are commutative,
# we dispatch to .__add__().
return self + other
def __sub__(self, other):
"""Handle `self - other` and `other - self`.
This may be either vector subtraction or broadcasting subtraction.
Example Usage:
>>> Vector([7, 8, 9]) - Vector([1, 2, 3])
Vector((6, 6, 6))
>>> Vector([1, 2, 3]) - 1
Vector((0, 1, 2))
>>> 10 - Vector([1, 2, 3])
Vector((9, 8, 7))
"""
# As subtraction is the inverse of addition,
# we first dispatch to .__neg__() to invert the signs of
# all entries in other and then dispatch to .__add__().
return self + (-other)
def __rsub__(self, other):
"""See docstring for .__sub__()."""
# Same comments as in .__sub__() apply
# with the roles of self and other swapped.
return (-self) + other
def __mul__(self, other):
"""Handle `self * other` and `other * self`.
This may be either the dot product of two vectors or scalar multiplication.
Example Usage:
>>> Vector([1, 2, 3]) * Vector([2, 3, 4])
14
>>> 2 * Vector([1, 2, 3])
Vector((2.0, 4.0, 6.0))
>>> Vector([1, 2, 3]) * 3
Vector((3.0, 6.0, 9.0))
"""
# Dot product
if isinstance(other, self.__class__):
if len(self) != len(other):
raise ValueError("vectors must be of the same length")
return sum(x * y for (x, y) in zip(self, other))
# Scalar multiplication
elif isinstance(other, numbers.Number):
return self.__class__(x * other for x in self)
return NotImplemented
def __rmul__(self, other):
"""See docstring for .__mul__()."""
# As both dot product and scalar multiplication are commutative,
# we dispatch to .__mul__().
return self * other
def __truediv__(self, other):
"""Handle `self / other`.
Divide a Vector by a scalar.
Example Usage:
>>> Vector([9, 6, 12]) / 3
Vector((3.0, 2.0, 4.0))
"""
# As scalar division division is the same as multiplication
# with the inverse, we dispatch to .__mul__().
if isinstance(other, numbers.Number):
return self * (1 / other)
return NotImplemented
def __eq__(self, other):
"""Handle `self == other`.
Compare two Vectors for equality.
Example Usage:
>>> Vector([1, 2, 3]) == Vector([1, 2, 3])
True
>>> Vector([1, 2, 3]) == Vector([4, 5, 6])
False
"""
if isinstance(other, self.__class__):
if len(self) != len(other):
raise ValueError("vectors must be of the same length")
for x, y in zip(self, other):
if abs(x - y) > self.zero_threshold:
return False # exit early if two corresponding entries differ
return True
return NotImplemented
def __pos__(self):
"""Handle `+self`.
This is simply an identity operator returning the Vector itself.
"""
return self
def __neg__(self):
"""Handle `-self`.
Negate all entries of a Vector.
"""
return self.__class__(-x for x in self)
def __abs__(self):
"""The Euclidean norm of a vector."""
return utils.norm(self) # use the norm() function shared with the Matrix class
def __bool__(self):
"""A Vector is truthy if its Euclidean norm is strictly positive."""
return bool(abs(self))
def __float__(self):
"""Cast a Vector as a scalar.
Returns:
scalar (float)
Raises:
RuntimeError: if the Vector has more than one entry
"""
if len(self) != 1:
raise RuntimeError("vector must have exactly one entry to become a scalar")
return self[0]
def as_matrix(self, *, column=True):
"""Get a Matrix representation of a Vector.
Args:
column (bool): if the vector is interpreted as a
column vector or a row vector; defaults to True
Returns:
matrix (Matrix)
"""
if column:
return Matrix([x] for x in self)
return Matrix([(x for x in self)])