{"id":13802365,"url":"https://github.com/iyassou/umatrix","last_synced_at":"2025-05-13T13:30:43.616Z","repository":{"id":143580879,"uuid":"145090226","full_name":"iyassou/umatrix","owner":"iyassou","description":"A matrix library for the MicroPython language","archived":false,"fork":false,"pushed_at":"2022-02-14T15:41:51.000Z","size":35,"stargazers_count":12,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-22T12:35:31.713Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iyassou.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-08-17T07:55:09.000Z","updated_at":"2024-04-22T12:35:31.714Z","dependencies_parsed_at":null,"dependency_job_id":"eb848faf-a287-432c-bdc4-54052e811815","html_url":"https://github.com/iyassou/umatrix","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iyassou%2Fumatrix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iyassou%2Fumatrix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iyassou%2Fumatrix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iyassou%2Fumatrix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iyassou","download_url":"https://codeload.github.com/iyassou/umatrix/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253949955,"owners_count":21989278,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T00:01:42.853Z","updated_at":"2025-05-13T13:30:43.380Z","avatar_url":"https://github.com/iyassou.png","language":"Python","readme":"# `umatrix` - A matrix library for MicroPython\r\n\r\n## Features\r\n\r\n`umatrix` was written mainly with speed and ease-of-use in mind. It aims to be a simple solution to matrix arithmetic needs in Micropython. It implements basic matrix operations (addition, subtraction, multiplication) as well as determinant (shortened to `det`), `inverse`, `trace`, `transpose`, `copy`, and other functions. The matrix class supports `int`, `float`, and `complex` coefficients, as well as `numpy`-like matrix slicing.\r\n\r\n## Examples\r\n\r\n### Creating and viewing a matrix\r\n```\r\n\u003e\u003e\u003e from umatrix import *\r\n\u003e\u003e\u003e A = matrix([1, 2, 3], [4, 5, 6], [7, 8, 9])\r\n\u003e\u003e\u003e A\r\nmatrix( [1,     2,      3],\r\n        [4,     5,      6],\r\n        [7,     8,      9] )\r\n\u003e\u003e\u003e M = matrix([12, 23, 31], [40, 50, 60], [71, 87, 98])\r\n\u003e\u003e\u003e print(M)\r\n[12,    23,     31,\r\n 40,    50,     60,\r\n 71,    87,     98]\r\n\u003e\u003e\u003e N = matrix([12.516j, 6.345, 7+.171j], are_rows=False)\r\n\u003e\u003e\u003e N\r\nmatrix( [   12.516j],\r\n        [     6.345],\r\n        [(7+0.171j)] )\r\n```\r\n\r\n### Matrix properties\r\n```\r\n\u003e\u003e\u003e A.order\r\n3\r\n\u003e\u003e\u003e A.is_square\r\nTrue\r\n\u003e\u003e\u003e N.is_square\r\nFalse\r\n\u003e\u003e\u003e N.shape\r\n(3, 1)\r\n\u003e\u003e\u003e N.rows\r\n[[12.516j], [6.345], [(7+0.171j)]]\r\n\u003e\u003e\u003e N.cols\r\n[[12.516j, 6.345, (7+0.171j)]]\r\n```\r\n\r\n### Adding, subtracting, multiplying, raising to the power of n\r\n```\r\n\u003e\u003e\u003e A+M\r\nmatrix( [ 13,    25,     34],\r\n        [ 44,    55,     66],\r\n        [ 78,    95,    107] )\r\n\u003e\u003e\u003e M-A\r\nmatrix( [11,    21,     28],\r\n        [36,    45,     54],\r\n        [64,    79,     89] )\r\n\u003e\u003e\u003e A*M\r\nmatrix( [ 305,   384,    445],\r\n        [ 674,   864,   1012],\r\n        [1043,  1344,   1579] )\r\n\u003e\u003e\u003e M*A\r\nmatrix( [ 321,   387,    453],\r\n        [ 660,   810,    960],\r\n        [1105,  1361,   1617] )\r\n\u003e\u003e\u003e A**2\r\nmatrix( [ 30,    36,     42],\r\n        [ 66,    81,     96],\r\n        [102,   126,    150] )\r\n\u003e\u003e\u003e M*2\r\nmatrix( [ 24,    46,     62],\r\n        [ 80,   100,    120],\r\n        [142,   174,    196] )\r\n\u003e\u003e\u003e 2*M\r\nmatrix( [ 24,    46,     62],\r\n        [ 80,   100,    120],\r\n        [142,   174,    196] )\r\n```\r\n\r\n### Determinant / `det`\r\nSupports \u003c= 4x4 matrices.\r\n```\r\n\u003e\u003e\u003e A.det\r\n0\r\n\u003e\u003e\u003e M.det\r\n1810\r\n\u003e\u003e\u003e abs(M)\r\n1810\r\n\u003e\u003e\u003e N.det\r\n[Traceback]\r\nAssertionError: Matrix must be square.\r\n```\r\n\r\n### Inverse.\r\nSupports \u003c= 4x4 matrices.\r\n```\r\n\u003e\u003e\u003e A.inverse\r\n[Traceback]\r\nAssertionError: Matrix is singular.\r\n\u003e\u003e\u003e M.inverse\r\nmatrix( [ -0.1767956,     0.2447514,    -0.09392265],\r\n        [  0.1878453,    -0.5662984,      0.2872928],\r\n        [-0.03867403,     0.3254144,     -0.1767956] )\r\n```\r\n\r\n### Rounding a matrix.\r\nNote that calling `round` on a matrix will not work as Micropython has not implemented the `__round__` magic method, so rounding a matrix requires you call its defined `round` method with the accustomed decimal places argument. The `round` method supports `complex` coefficients. The boolean argument `inplace` set to `False` by default makes the changes \"in place\" when set to `True`.\r\n```\r\n\u003e\u003e\u003e (M*M.inverse).round(5)\r\nmatrix( [ 1.0,   0.0,   -0.0],\r\n        [ 0.0,   1.0,    0.0],\r\n        [ 0.0,  -0.0,    1.0] )\r\n\u003e\u003e\u003e N\r\nmatrix( [12.516j],\r\n        [6.345],\r\n        [(7+0.171j)] )\r\n\u003e\u003e\u003e N.round(2, True)\r\n\u003e\u003e\u003e N\r\nmatrix( [12.52j],\r\n        [6.34],\r\n        [(7+0.17j)] )\r\n```\r\n\r\n### Copying a matrix\r\n```\r\n\u003e\u003e\u003e B = M.copy()\r\n\u003e\u003e\u003e M\r\nmatrix( [12,    23,     31],\r\n        [40,    50,     60],\r\n        [71,    87,     98] )\r\n\u003e\u003e\u003e B[0] = [2222,2222,2222]\r\n\u003e\u003e\u003e B\r\nmatrix( [2222,  2222,   2222],\r\n        [  40,    50,     60],\r\n        [  71,    87,     98] )\r\n\u003e\u003e\u003e M\r\nmatrix( [12,    23,     31],\r\n        [40,    50,     60],\r\n        [71,    87,     98] )\r\n\u003e\u003e\u003e M.det == B.det\r\nFalse\r\n```\r\n\r\n### Transpose\r\n```\r\n\u003e\u003e\u003e A\r\nmatrix( [1,     2,      3],\r\n        [4,     5,      6],\r\n        [7,     8,      9] )\r\n\u003e\u003e\u003e A.transpose\r\nmatrix( [1,     4,      7],\r\n        [2,     5,      8],\r\n        [3,     6,      9] )\r\n\u003e\u003e\u003e N\r\nmatrix( [12.516j],\r\n        [6.345],\r\n        [(7+0.171j)] )\r\n\u003e\u003e\u003e N.transpose\r\nmatrix( [12.516j,       6.345,  (7+0.171j)] )\r\n```\r\n\r\n### Trace\r\n```\r\n\u003e\u003e\u003e A\r\nmatrix( [1,     2,      3],\r\n        [4,     5,      6],\r\n        [7,     8,      9] )\r\n\u003e\u003e\u003e A.trace\r\n15\r\n\u003e\u003e\u003e M\r\nmatrix( [12,    23,     31],\r\n        [40,    50,     60],\r\n        [71,    87,     98] )\r\n\u003e\u003e\u003e M.trace\r\n160\r\n```\r\n\r\n### Eigenvalue and eigenvector checking\r\n`is_eigenvalue` takes the value to check as its single argument. `is_eigenvector` takes the vector and value to check respectively: the vector can be given in tuple/list form or in matrix form. \r\n```\r\n\u003e\u003e\u003e C = matrix([1, 2], [2, 1])\r\n\u003e\u003e\u003e C\r\nmatrix( [1,     2],\r\n        [2,     1] )\r\n\u003e\u003e\u003e C.is_eigenvalue(2.431)\r\nFalse\r\n\u003e\u003e\u003e C.is_eigenvalue(3)\r\nTrue\r\n\u003e\u003e\u003e C.is_eigenvalue(-1)\r\nTrue\r\n\u003e\u003e\u003e C.is_eigenvector((1, -1), -1)\r\nTrue\r\n\u003e\u003e\u003e C.is_eigenvector(matrix([1, 1], are_rows=False), 3)\r\nTrue\r\n```\r\n\r\n### Equality tests\r\n```\r\n\u003e\u003e\u003e A == M\r\nFalse\r\n\u003e\u003e\u003e N != A\r\nTrue\r\n\u003e\u003e\u003e A == A.copy()\r\nTrue\r\n```\r\n\r\n### `apply`\r\nThis method takes a function and the boolean `inplace` as its arguments. It applies that function to the matrix's coefficients and either returns a new matrix with the return values or overwrites the initial matrix's coefficients. By default, `inplace = False`.\r\n```\r\n\u003e\u003e\u003e A\r\nmatrix( [1,     2,      3],\r\n        [4,     5,      6],\r\n        [7,     8,      9] )\r\n\u003e\u003e\u003e from math import log\r\n\u003e\u003e\u003e A.apply(log).round(2)\r\nmatrix( [ 0.0,  0.69,    1.1],\r\n        [1.39,  1.61,   1.79],\r\n        [1.95,  2.08,    2.2] )\r\n\u003e\u003e\u003e A.apply(lambda x: x if x \u003e 5 else 0, inplace=True)\r\n\u003e\u003e\u003e A\r\nmatrix( [0,     0,      0],\r\n        [0,     0,      6],\r\n        [7,     8,      9] )\r\n```\r\n\r\n### `numpy`-like matrix slicing\r\n\r\nReferencing a matrix with a `tuple` of either two `slice`s or a `slice` and an `int` returns a new matrix.\r\nYou can also modify matrix coefficients by assigning values to a matrix `slice`.\r\nNote that for changing the value of a specific coefficient, you should use `matrix[idx1][idx2] = new_val` and not a `slice` assignment.\r\n```\r\n\u003e\u003e\u003e Z = matrix([1,2,3,4],[5,6,7,8],[8,9,0,1],[2,3,4,5],[6,7,8,9])\r\n\u003e\u003e\u003e Z\r\nmatrix( [1,     2,      3,      4],\r\n        [5,     6,      7,      8],\r\n        [8,     9,      0,      1],\r\n        [2,     3,      4,      5],\r\n        [6,     7,      8,      9] )\r\n\u003e\u003e\u003e Z[:, 3]\r\nmatrix( [4],\r\n        [8],\r\n        [1],\r\n        [5],\r\n        [9] )\r\n\u003e\u003e\u003e Z[0, ::2]\r\nmatrix( [1,     3] )\r\n\u003e\u003e\u003e Z[1:4, 1:]\r\nmatrix( [6,     7,      8],\r\n        [9,     0,      1],\r\n        [3,     4,      5] )\r\n\u003e\u003e\u003e Z[::2, 2]\r\nmatrix( [3],\r\n        [0],\r\n        [8] )\r\n\u003e\u003e\u003e Z[::2, 2] = 1111, 1111, 1111\r\n\u003e\u003e\u003e Z\r\nmatrix( [   1,     2,   1111,      4],\r\n        [   5,     6,      7,      8],\r\n        [   8,     9,   1111,      1],\r\n        [   2,     3,      4,      5],\r\n        [   6,     7,   1111,      9] )\r\n\u003e\u003e\u003e Z[:, 2:]\r\nmatrix( [1111,     4],\r\n        [   7,     8],\r\n        [1111,     1],\r\n        [   4,     5],\r\n        [1111,     9] )\r\n\u003e\u003e\u003e Z[:, 2:] = [[0]*2]*5\r\n\u003e\u003e\u003e Z\r\nmatrix( [1,     2,      0,      0],\r\n        [5,     6,      0,      0],\r\n        [8,     9,      0,      0],\r\n        [2,     3,      0,      0],\r\n        [6,     7,      0,      0] )\r\n\u003e\u003e\u003e Z[1, ::2]\r\nmatrix( [5,     0] )\r\n\u003e\u003e\u003e Z[1, ::2] = [5555, 5555]\r\n\u003e\u003e\u003e Z\r\nmatrix( [   1,     2,      0,      0],\r\n        [5555,     6,   5555,      0],\r\n        [   8,     9,      0,      0],\r\n        [   2,     3,      0,      0],\r\n        [   6,     7,      0,      0] )\r\n\u003e\u003e\u003e Z[4][1] = 9999\r\n\u003e\u003e\u003e Z\r\nmatrix( [   1,     2,      0,      0],\r\n        [5555,     6,   5555,      0],\r\n        [   8,     9,      0,      0],\r\n        [   2,     3,      0,      0],\r\n        [   6,  9999,      0,      0] )\r\n```\r\n\r\n## Useful functions: `eye`, `fill`, `zeros`, `ones`\r\n\r\nNote that for `fill`, `zeros`, and `ones`, if the number of columns is not supplied as a final argument a square matrix is returned.\r\n`eye` always returns a square matrix.\r\n\r\n- `eye`: returns the identity matrix.\r\n``````\r\n\u003e\u003e\u003e eye(4)\r\nmatrix( [1,     0,      0,      0],\r\n        [0,     1,      0,      0],\r\n        [0,     0,      1,      0],\r\n        [0,     0,      0,      1] )\r\n``````\r\n\r\n- `fill`: returns a matrix filled with the coefficient of your choice.\r\n``````\r\n\u003e\u003e\u003e fill(25, 3, 6)\r\nmatrix( [25,    25,     25,     25,     25,     25],\r\n        [25,    25,     25,     25,     25,     25],\r\n        [25,    25,     25,     25,     25,     25] )\r\n\u003e\u003e\u003e fill(3.14, A.order)\r\nmatrix( [3.14,  3.14,   3.14],\r\n        [3.14,  3.14,   3.14],\r\n        [3.14,  3.14,   3.14] )\r\n``````\r\n\r\n- `zeros`: returns a matrix filled with zeros.\r\n``````\r\n\u003e\u003e\u003e zeros(5,4)\r\nmatrix( [0,     0,      0,      0],\r\n        [0,     0,      0,      0],\r\n        [0,     0,      0,      0],\r\n        [0,     0,      0,      0],\r\n        [0,     0,      0,      0] )\r\n``````\r\n\r\n- `ones`: returns a matrix filled with ones.\r\n``````\r\n\u003e\u003e\u003e ones(4,5)\r\nmatrix( [1,     1,      1,      1,      1],\r\n        [1,     1,      1,      1,      1],\r\n        [1,     1,      1,      1,      1],\r\n        [1,     1,      1,      1,      1] )\r\n``````\r\n\r\n## Testing Inversion Speed\r\n\r\n### Environment\r\n\r\nFor both the Pyboard v1.1 and Pyboard v1.0 lite tests, no SD card was used.\r\nThere were only 3 files onboard the flash storage: `boot.py` (factory state), `main.py` (as described below), and `umatrix.py`.\r\n\r\n`main.py` consisted of the following lines:\r\n```\r\nfrom umatrix import *\r\nfrom utime import ticks_us\r\n\r\ndef t(func, n):\r\n        # returns the average time for n executions of func in milliseconds\r\n        start = ticks_us()\r\n        for i in range(n):\r\n                _ = func()\r\n        end = ticks_us()\r\n        return ((end-start)/1000)/n\r\n\r\nT = matrix([12, 62], [98, 21])  # 2x2\r\nU = T**3        # 2x2 large coefficients\r\n\r\nV = matrix([57, 45, 67], [77, 40, 93], [12, 76, 34])    # 3x3\r\nW = V**3        #3x3 large coefficients\r\n\r\nX = matrix([10, 49, 36, 54], [88, 61, 53, 20], [31, 42, 53, 64], [9, 75, 60, 75])       # 4x4\r\nY = X**3        # 4x4 large coefficients\r\n```\r\n\r\nVisualisation:\r\n```\r\n\u003e\u003e\u003e T\r\nmatrix( [12,    62],\r\n        [98,    21] )\r\n\r\n\u003e\u003e\u003e U\r\nmatrix( [275148,        428606],\r\n        [677474,        337365] )\r\n\r\n\u003e\u003e\u003e V\r\nmatrix( [57,    45,     67],\r\n        [77,    40,     93],\r\n        [12,    76,     34] )\r\n\r\n\u003e\u003e\u003e W\r\nmatrix( [1280099,       1498022,        1732795],\r\n        [1568078,       1786761,        2112958],\r\n        [ 978772,       1245168,        1345452] )\r\n\r\n\u003e\u003e\u003e X\r\nmatrix( [10,    49,     36,     54],\r\n        [88,    61,     53,     20],\r\n        [31,    42,     53,     64],\r\n        [ 9,    75,     60,     75] )\r\n\r\n\u003e\u003e\u003e Y\r\nmatrix( [1177869,       1777147,        1597682,        1614846],\r\n        [1535988,       2364798,        2117353,        2152054],\r\n        [1445741,       2205124,        1984654,        2000664],\r\n        [1724826,       2616789,        2351580,        2386851] )\r\n```\r\n\r\n### Execution\r\n\r\nThe timing function `t` was called in the REPL with `n = 5000`. The reported result is the slowest of 3 tests i.e. 3 executions of `t(func, 5000)`.\r\n\r\n### Results\r\n\r\nA reminder that the results are in milliseconds.\r\n\r\n#### Pyboard v1.1\r\n\r\n- `umatrix` `v1.1`\r\n\r\n| Matrix Size | Small Coefficients | Large Coefficients |\r\n|:-----------:|:------------------:|:------------------:|\r\n| 2x2         | 0.6740602          | 0.7213964          |\r\n| 3x3         | 1.072958           | 1.573              |\r\n| 4x4         | 1.734226           | 4.559215           |\r\n\r\n\r\n\r\n#### Pyboard v1.0 lite\r\n\r\n- `umatrix` `v1.1`\r\n\r\n| Matrix Size | Small Coefficients | Large Coefficients |\r\n|:-----------:|:------------------:|:------------------:|\r\n| 2x2         | 1.137326           | 1.217149           |\r\n| 3x3         | 1.831495           | 2.671786           |\r\n| 4x4         | 2.979418           | 7.701244           |\r\n\r\nThere is a clear increase in execution time for either tests as the matrix size and coefficients get larger.\r\n","funding_links":[],"categories":["Libraries"],"sub_categories":["Mathematics"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiyassou%2Fumatrix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiyassou%2Fumatrix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiyassou%2Fumatrix/lists"}