numpy.linalg
============
Functions in the ``linalg`` module can be called by prepending them by
``numpy.linalg.``. The module defines the following seven functions:
1. `numpy.linalg.cholesky <#cholesky>`__
2. `numpy.linalg.det <#det>`__
3. `numpy.linalg.eig <#eig>`__
4. `numpy.linalg.inv <#inv>`__
5. `numpy.linalg.norm <#norm>`__
6. `numpy.linalg.qr <#qr>`__
cholesky
--------
``numpy``:
https://docs.scipy.org/doc/numpy-1.17.0/reference/generated/numpy.linalg.cholesky.html
The function of the Cholesky decomposition takes a positive definite,
symmetric square matrix as its single argument, and returns the *square
root matrix* in the lower triangular form. If the input argument does
not fulfill the positivity or symmetry condition, a ``ValueError`` is
raised.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array([[25, 15, -5], [15, 18, 0], [-5, 0, 11]])
print('a: ', a)
print('\n' + '='*20 + '\nCholesky decomposition\n', np.linalg.cholesky(a))
.. parsed-literal::
a: array([[25.0, 15.0, -5.0],
[15.0, 18.0, 0.0],
[-5.0, 0.0, 11.0]], dtype=float)
====================
Cholesky decomposition
array([[5.0, 0.0, 0.0],
[3.0, 3.0, 0.0],
[-1.0, 1.0, 3.0]], dtype=float)
det
---
``numpy``:
https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.det.html
The ``det`` function takes a square matrix as its single argument, and
calculates the determinant. The calculation is based on successive
elimination of the matrix elements, and the return value is a float,
even if the input array was of integer type.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array([[1, 2], [3, 4]], dtype=np.uint8)
print(np.linalg.det(a))
.. parsed-literal::
-2.0
Benchmark
~~~~~~~~~
Since the routine for calculating the determinant is pretty much the
same as for finding the `inverse of a matrix <#inv>`__, the execution
times are similar:
.. code::
# code to be run in micropython
from ulab import numpy as np
@timeit
def matrix_det(m):
return np.linalg.inv(m)
m = np.array([[1, 2, 3, 4, 5, 6, 7, 8], [0, 5, 6, 4, 5, 6, 4, 5],
[0, 0, 9, 7, 8, 9, 7, 8], [0, 0, 0, 10, 11, 12, 11, 12],
[0, 0, 0, 0, 4, 6, 7, 8], [0, 0, 0, 0, 0, 5, 6, 7],
[0, 0, 0, 0, 0, 0, 7, 6], [0, 0, 0, 0, 0, 0, 0, 2]])
matrix_det(m)
.. parsed-literal::
execution time: 294 us
eig
---
``numpy``:
https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.eig.html
The ``eig`` function calculates the eigenvalues and the eigenvectors of
a real, symmetric square matrix. If the matrix is not symmetric, a
``ValueError`` will be raised. The function takes a single argument, and
returns a tuple with the eigenvalues, and eigenvectors. With the help of
the eigenvectors, amongst other things, you can implement sophisticated
stabilisation routines for robots.
.. code::
# code to be run in micropython
from ulab import numpy as np
a = np.array([[1, 2, 1, 4], [2, 5, 3, 5], [1, 3, 6, 1], [4, 5, 1, 7]], dtype=np.uint8)
x, y = np.linalg.eig(a)
print('eigenvectors of a:\n', y)
print('\neigenvalues of a:\n', x)
.. parsed-literal::
eigenvectors of a:
array([[0.8151560042509081, -0.4499411232970823, -0.1644660242574522, 0.3256141906686505],
[0.2211334179893007, 0.7846992598235538, 0.08372081379922657, 0.5730077734355189],
[-0.1340114162071679, -0.3100776411558949, 0.8742786816656, 0.3486109343758527],
[-0.5183258053659028, -0.292663481927148, -0.4489749870391468, 0.6664142156731531]], dtype=float)
eigenvalues of a:
array([-1.165288365404889, 0.8029365530314914, 5.585625756072663, 13.77672605630074], dtype=float)
The same matrix diagonalised with ``numpy`` yields:
.. code::
# code to be run in CPython
a = array([[1, 2, 1, 4], [2, 5, 3, 5], [1, 3, 6, 1], [4, 5, 1, 7]], dtype=np.uint8)
x, y = eig(a)
print('eigenvectors of a:\n', y)
print('\neigenvalues of a:\n', x)
.. parsed-literal::
eigenvectors of a:
[[ 0.32561419 0.815156 0.44994112 -0.16446602]
[ 0.57300777 0.22113342 -0.78469926 0.08372081]
[ 0.34861093 -0.13401142 0.31007764 0.87427868]
[ 0.66641421 -0.51832581 0.29266348 -0.44897499]]
eigenvalues of a:
[13.77672606 -1.16528837 0.80293655 5.58562576]
When comparing results, we should keep two things in mind:
1. the eigenvalues and eigenvectors are not necessarily sorted in the
same way
2. an eigenvector can be multiplied by an arbitrary non-zero scalar, and
it is still an eigenvector with the same eigenvalue. This is why all
signs of the eigenvector belonging to 5.58, and 0.80 are flipped in
``ulab`` with respect to ``numpy``. This difference, however, is of
absolutely no consequence.
Computation expenses
~~~~~~~~~~~~~~~~~~~~
Since the function is based on `Givens
rotations