diff --git a/docs/manual/source/conf.py b/docs/manual/source/conf.py index 5ce0432..23df79c 100644 --- a/docs/manual/source/conf.py +++ b/docs/manual/source/conf.py @@ -27,7 +27,7 @@ copyright = '2019-2021, Zoltán Vörös and contributors' author = 'Zoltán Vörös' # The full version, including alpha/beta/rc tags -release = '2.6.0' +release = '2.7.0' # -- General configuration --------------------------------------------------- diff --git a/docs/manual/source/index.rst b/docs/manual/source/index.rst index 90af07f..96d45b2 100644 --- a/docs/manual/source/index.rst +++ b/docs/manual/source/index.rst @@ -22,6 +22,7 @@ Welcome to the ulab book! numpy-universal numpy-fft numpy-linalg + scipy-linalg scipy-optimize scipy-signal scipy-special diff --git a/docs/manual/source/scipy-linalg.rst b/docs/manual/source/scipy-linalg.rst new file mode 100644 index 0000000..9e47bfa --- /dev/null +++ b/docs/manual/source/scipy-linalg.rst @@ -0,0 +1,120 @@ + +linalg +====== + +``scipy``\ ’s ``linalg`` module contains a single function, +``solve_triangular``, which can be called by prepending it by +``scipy.linalg.``. + +1. `scipy.linalg.solve_triangular <#solve_triangular>`__ + +solve_triangular +---------------- + +``scipy``: +https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.solve_triangular.html + +Solve the linear equation + +:raw-latex:`\begin{equation} +\mathbf{a}\cdot\mathbf{x} = \mathbf{b} +\end{equation}` + +with the assumption that :math:`\mathbf{a}` is a triangular matrix. The +two position arguments are :math:`\mathbf{a}`, and :math:`\mathbf{b}`, +and the optional keyword argument is ``lower`` with a default value of +``False``. ``lower`` determines, whether data are taken from the lower, +or upper triangle of :math:`\mathbf{a}`. + +Note that :math:`\mathbf{a}` itself does not have to be a triangular +matrix: if it is not, then the values are simply taken to be 0 in the +upper or lower triangle, as dictated by ``lower``. However, +:math:`\mathbf{a}\cdot\mathbf{x}` will yield :math:`\mathbf{b}` only, +when :math:`\mathbf{a}` is triangular. You should keep this in mind, +when trying to establish the validity of the solution by back +substitution. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + from ulab import scipy as spy + + a = np.array([[3, 0, 0, 0], [2, 1, 0, 0], [1, 0, 1, 0], [1, 2, 1, 8]]) + b = np.array([4, 2, 4, 2]) + + print('a:\n') + print(a) + print('\nb: ', b) + + x = spy.linalg.solve_triangular(a, b, lower=True) + + print('='*20) + print('x: ', x) + print('\ndot(a, x): ', np.dot(a, x)) + +.. parsed-literal:: + + a: + + array([[3.0, 0.0, 0.0, 0.0], + [2.0, 1.0, 0.0, 0.0], + [1.0, 0.0, 1.0, 0.0], + [1.0, 2.0, 1.0, 8.0]], dtype=float64) + + b: array([4.0, 2.0, 4.0, 2.0], dtype=float64) + ==================== + x: array([1.333333333333333, -0.6666666666666665, 2.666666666666667, -0.08333333333333337], dtype=float64) + + dot(a, x): array([4.0, 2.0, 4.0, 2.0], dtype=float64) + + + + +With get the same solution, :math:`\mathbf{x}`, with the following +matrix, but the dot product of :math:`\mathbf{a}`, and +:math:`\mathbf{x}` is no longer :math:`\mathbf{b}`: + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + from ulab import scipy as spy + + a = np.array([[3, 2, 1, 0], [2, 1, 0, 1], [1, 0, 1, 4], [1, 2, 1, 8]]) + b = np.array([4, 2, 4, 2]) + + print('a:\n') + print(a) + print('\nb: ', b) + + x = spy.linalg.solve_triangular(a, b, lower=True) + + print('='*20) + print('x: ', x) + print('\ndot(a, x): ', np.dot(a, x)) + +.. parsed-literal:: + + a: + + array([[3.0, 2.0, 1.0, 0.0], + [2.0, 1.0, 0.0, 1.0], + [1.0, 0.0, 1.0, 4.0], + [1.0, 2.0, 1.0, 8.0]], dtype=float64) + + b: array([4.0, 2.0, 4.0, 2.0], dtype=float64) + ==================== + x: array([1.333333333333333, -0.6666666666666665, 2.666666666666667, -0.08333333333333337], dtype=float64) + + dot(a, x): array([5.333333333333334, 1.916666666666666, 3.666666666666667, 2.0], dtype=float64) + + + + +.. code:: + + # code to be run in CPython + diff --git a/docs/scipy-linalg.ipynb b/docs/scipy-linalg.ipynb new file mode 100644 index 0000000..5d2b944 --- /dev/null +++ b/docs/scipy-linalg.ipynb @@ -0,0 +1,437 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2021-01-13T18:54:58.722373Z", + "start_time": "2021-01-13T18:54:57.178438Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Populating the interactive namespace from numpy and matplotlib\n" + ] + } + ], + "source": [ + "%pylab inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Notebook magic" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2021-05-09T05:37:22.600510Z", + "start_time": "2021-05-09T05:37:22.595924Z" + } + }, + "outputs": [], + "source": [ + "from IPython.core.magic import Magics, magics_class, line_cell_magic\n", + "from IPython.core.magic import cell_magic, register_cell_magic, register_line_magic\n", + "from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring\n", + "import subprocess\n", + "import os" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2021-05-09T05:37:26.429136Z", + "start_time": "2021-05-09T05:37:26.403191Z" + } + }, + "outputs": [], + "source": [ + "@magics_class\n", + "class PyboardMagic(Magics):\n", + " @cell_magic\n", + " @magic_arguments()\n", + " @argument('-skip')\n", + " @argument('-unix')\n", + " @argument('-pyboard')\n", + " @argument('-file')\n", + " @argument('-data')\n", + " @argument('-time')\n", + " @argument('-memory')\n", + " def micropython(self, line='', cell=None):\n", + " args = parse_argstring(self.micropython, line)\n", + " if args.skip: # doesn't care about the cell's content\n", + " print('skipped execution')\n", + " return None # do not parse the rest\n", + " if args.unix: # tests the code on the unix port. Note that this works on unix only\n", + " with open('/dev/shm/micropython.py', 'w') as fout:\n", + " fout.write(cell)\n", + " proc = subprocess.Popen([\"../../micropython/ports/unix/micropython\", \"/dev/shm/micropython.py\"], \n", + " stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n", + " print(proc.stdout.read().decode(\"utf-8\"))\n", + " print(proc.stderr.read().decode(\"utf-8\"))\n", + " return None\n", + " if args.file: # can be used to copy the cell content onto the pyboard's flash\n", + " spaces = \" \"\n", + " try:\n", + " with open(args.file, 'w') as fout:\n", + " fout.write(cell.replace('\\t', spaces))\n", + " printf('written cell to {}'.format(args.file))\n", + " except:\n", + " print('Failed to write to disc!')\n", + " return None # do not parse the rest\n", + " if args.data: # can be used to load data from the pyboard directly into kernel space\n", + " message = pyb.exec(cell)\n", + " if len(message) == 0:\n", + " print('pyboard >>>')\n", + " else:\n", + " print(message.decode('utf-8'))\n", + " # register new variable in user namespace\n", + " self.shell.user_ns[args.data] = string_to_matrix(message.decode(\"utf-8\"))\n", + " \n", + " if args.time: # measures the time of executions\n", + " pyb.exec('import utime')\n", + " message = pyb.exec('t = utime.ticks_us()\\n' + cell + '\\ndelta = utime.ticks_diff(utime.ticks_us(), t)' + \n", + " \"\\nprint('execution time: {:d} us'.format(delta))\")\n", + " print(message.decode('utf-8'))\n", + " \n", + " if args.memory: # prints out memory information \n", + " message = pyb.exec('from micropython import mem_info\\nprint(mem_info())\\n')\n", + " print(\"memory before execution:\\n========================\\n\", message.decode('utf-8'))\n", + " message = pyb.exec(cell)\n", + " print(\">>> \", message.decode('utf-8'))\n", + " message = pyb.exec('print(mem_info())')\n", + " print(\"memory after execution:\\n========================\\n\", message.decode('utf-8'))\n", + "\n", + " if args.pyboard:\n", + " message = pyb.exec(cell)\n", + " print(message.decode('utf-8'))\n", + "\n", + "ip = get_ipython()\n", + "ip.register_magics(PyboardMagic)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## pyboard" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-07T07:35:35.126401Z", + "start_time": "2020-05-07T07:35:35.105824Z" + } + }, + "outputs": [], + "source": [ + "import pyboard\n", + "pyb = pyboard.Pyboard('/dev/ttyACM0')\n", + "pyb.enter_raw_repl()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-19T19:11:18.145548Z", + "start_time": "2020-05-19T19:11:18.137468Z" + } + }, + "outputs": [], + "source": [ + "pyb.exit_raw_repl()\n", + "pyb.close()" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-07T07:35:38.725924Z", + "start_time": "2020-05-07T07:35:38.645488Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "%%micropython -pyboard 1\n", + "\n", + "import utime\n", + "import ulab as np\n", + "\n", + "def timeit(n=1000):\n", + " def wrapper(f, *args, **kwargs):\n", + " func_name = str(f).split(' ')[1]\n", + " def new_func(*args, **kwargs):\n", + " run_times = np.zeros(n, dtype=np.uint16)\n", + " for i in range(n):\n", + " t = utime.ticks_us()\n", + " result = f(*args, **kwargs)\n", + " run_times[i] = utime.ticks_diff(utime.ticks_us(), t)\n", + " print('{}() execution times based on {} cycles'.format(func_name, n, (delta2-delta1)/n))\n", + " print('\\tbest: %d us'%np.min(run_times))\n", + " print('\\tworst: %d us'%np.max(run_times))\n", + " print('\\taverage: %d us'%np.mean(run_times))\n", + " print('\\tdeviation: +/-%.3f us'%np.std(run_times)) \n", + " return result\n", + " return new_func\n", + " return wrapper\n", + "\n", + "def timeit(f, *args, **kwargs):\n", + " func_name = str(f).split(' ')[1]\n", + " def new_func(*args, **kwargs):\n", + " t = utime.ticks_us()\n", + " result = f(*args, **kwargs)\n", + " print('execution time: ', utime.ticks_diff(utime.ticks_us(), t), ' us')\n", + " return result\n", + " return new_func" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__END_OF_DEFS__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# linalg\n", + "\n", + "`scipy`'s `linalg` module contains a single function, `solve_triangular`, which can be called by prepending it by `scipy.linalg.`.\n", + "\n", + "1. [scipy.linalg.solve_triangular](#solve_triangular)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## solve_triangular\n", + "\n", + "`scipy`: https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.solve_triangular.html \n", + "\n", + "Solve the linear equation \n", + "\n", + "\\begin{equation}\n", + "\\mathbf{a}\\cdot\\mathbf{x} = \\mathbf{b}\n", + "\\end{equation}\n", + "\n", + "with the assumption that $\\mathbf{a}$ is a triangular matrix. The two position arguments are $\\mathbf{a}$, and $\\mathbf{b}$, and the optional keyword argument is `lower` with a default value of `False`. `lower` determines, whether data are taken from the lower, or upper triangle of $\\mathbf{a}$. \n", + "\n", + "Note that $\\mathbf{a}$ itself does not have to be a triangular matrix: if it is not, then the values are simply taken to be 0 in the upper or lower triangle, as dictated by `lower`. However, $\\mathbf{a}\\cdot\\mathbf{x}$ will yield $\\mathbf{b}$ only, when $\\mathbf{a}$ is triangular. You should keep this in mind, when trying to establish the validity of the solution by back substitution." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "ExecuteTime": { + "end_time": "2021-05-09T05:56:57.449996Z", + "start_time": "2021-05-09T05:56:57.422515Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a:\n", + "\n", + "array([[3.0, 0.0, 0.0, 0.0],\n", + " [2.0, 1.0, 0.0, 0.0],\n", + " [1.0, 0.0, 1.0, 0.0],\n", + " [1.0, 2.0, 1.0, 8.0]], dtype=float64)\n", + "\n", + "b: array([4.0, 2.0, 4.0, 2.0], dtype=float64)\n", + "====================\n", + "x: array([1.333333333333333, -0.6666666666666665, 2.666666666666667, -0.08333333333333337], dtype=float64)\n", + "\n", + "dot(a, x): array([4.0, 2.0, 4.0, 2.0], dtype=float64)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "from ulab import scipy as spy\n", + "\n", + "a = np.array([[3, 0, 0, 0], [2, 1, 0, 0], [1, 0, 1, 0], [1, 2, 1, 8]])\n", + "b = np.array([4, 2, 4, 2])\n", + "\n", + "print('a:\\n')\n", + "print(a)\n", + "print('\\nb: ', b)\n", + "\n", + "x = spy.linalg.solve_triangular(a, b, lower=True)\n", + "\n", + "print('='*20)\n", + "print('x: ', x)\n", + "print('\\ndot(a, x): ', np.dot(a, x))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With get the same solution, $\\mathbf{x}$, with the following matrix, but the dot product of $\\mathbf{a}$, and $\\mathbf{x}$ is no longer $\\mathbf{b}$:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "ExecuteTime": { + "end_time": "2021-05-09T06:03:30.853054Z", + "start_time": "2021-05-09T06:03:30.841500Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a:\n", + "\n", + "array([[3.0, 2.0, 1.0, 0.0],\n", + " [2.0, 1.0, 0.0, 1.0],\n", + " [1.0, 0.0, 1.0, 4.0],\n", + " [1.0, 2.0, 1.0, 8.0]], dtype=float64)\n", + "\n", + "b: array([4.0, 2.0, 4.0, 2.0], dtype=float64)\n", + "====================\n", + "x: array([1.333333333333333, -0.6666666666666665, 2.666666666666667, -0.08333333333333337], dtype=float64)\n", + "\n", + "dot(a, x): array([5.333333333333334, 1.916666666666666, 3.666666666666667, 2.0], dtype=float64)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "from ulab import scipy as spy\n", + "\n", + "a = np.array([[3, 2, 1, 0], [2, 1, 0, 1], [1, 0, 1, 4], [1, 2, 1, 8]])\n", + "b = np.array([4, 2, 4, 2])\n", + "\n", + "print('a:\\n')\n", + "print(a)\n", + "print('\\nb: ', b)\n", + "\n", + "x = spy.linalg.solve_triangular(a, b, lower=True)\n", + "\n", + "print('='*20)\n", + "print('x: ', x)\n", + "print('\\ndot(a, x): ', np.dot(a, x))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "382.797px" + }, + "toc_section_display": true, + "toc_window_display": true + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/ulab-convert.ipynb b/docs/ulab-convert.ipynb index 6f1110f..f3f60ab 100644 --- a/docs/ulab-convert.ipynb +++ b/docs/ulab-convert.ipynb @@ -14,11 +14,11 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 1, "metadata": { "ExecuteTime": { - "end_time": "2021-03-23T16:27:42.273017Z", - "start_time": "2021-03-23T16:27:42.261057Z" + "end_time": "2021-05-09T06:05:50.855683Z", + "start_time": "2021-05-09T06:05:50.838482Z" } }, "outputs": [ @@ -61,7 +61,7 @@ "author = 'Zoltán Vörös'\n", "\n", "# The full version, including alpha/beta/rc tags\n", - "release = '2.6.0'\n", + "release = '2.7.0'\n", "\n", "\n", "# -- General configuration ---------------------------------------------------\n", @@ -151,8 +151,8 @@ "execution_count": 2, "metadata": { "ExecuteTime": { - "end_time": "2021-03-04T18:23:33.312441Z", - "start_time": "2021-03-04T18:23:33.298784Z" + "end_time": "2021-05-09T06:06:28.491158Z", + "start_time": "2021-05-09T06:06:28.477127Z" } }, "outputs": [ @@ -190,10 +190,11 @@ " numpy-universal\n", " numpy-fft\n", " numpy-linalg\n", + " scipy-linalg\n", " scipy-optimize\n", " scipy-signal\n", " scipy-special\n", - " ulab-utils\n", + " ulab-utils\n", " ulab-programming\n", "\n", "Indices and tables\n", @@ -213,11 +214,11 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": { "ExecuteTime": { - "end_time": "2021-03-23T16:27:30.647984Z", - "start_time": "2021-03-23T16:27:28.144633Z" + "end_time": "2021-05-09T06:06:35.839960Z", + "start_time": "2021-05-09T06:06:33.112686Z" } }, "outputs": [], @@ -254,11 +255,11 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": { "ExecuteTime": { - "end_time": "2021-03-23T16:27:35.399254Z", - "start_time": "2021-03-23T16:27:30.656938Z" + "end_time": "2021-05-09T06:07:00.143083Z", + "start_time": "2021-05-09T06:06:56.719659Z" } }, "outputs": [],