{"id":13424701,"url":"https://github.com/versilov/matrex","last_synced_at":"2025-05-16T08:06:26.000Z","repository":{"id":41497656,"uuid":"129436114","full_name":"versilov/matrex","owner":"versilov","description":"A blazing fast matrix library for Elixir/Erlang with C implementation using CBLAS.","archived":false,"fork":false,"pushed_at":"2020-08-03T13:56:30.000Z","size":36939,"stargazers_count":483,"open_issues_count":5,"forks_count":30,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-05-10T22:16:19.925Z","etag":null,"topics":["c","cblas","elixir","erlang","jupyter-notebook","linear-algebra","machine-learning","matrix","numpy"],"latest_commit_sha":null,"homepage":"https://hexdocs.pm/matrex","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/versilov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"versilov","patreon":"versilov"}},"created_at":"2018-04-13T17:51:18.000Z","updated_at":"2025-03-20T21:47:18.000Z","dependencies_parsed_at":"2022-09-26T18:01:18.183Z","dependency_job_id":null,"html_url":"https://github.com/versilov/matrex","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/versilov%2Fmatrex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/versilov%2Fmatrex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/versilov%2Fmatrex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/versilov%2Fmatrex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/versilov","download_url":"https://codeload.github.com/versilov/matrex/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254493378,"owners_count":22080126,"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":["c","cblas","elixir","erlang","jupyter-notebook","linear-algebra","machine-learning","matrix","numpy"],"created_at":"2024-07-31T00:00:58.065Z","updated_at":"2025-05-16T08:06:20.988Z","avatar_url":"https://github.com/versilov.png","language":"Elixir","funding_links":["https://github.com/sponsors/versilov","https://patreon.com/versilov"],"categories":["Elixir","Algorithms and Data structures"],"sub_categories":[],"readme":"# \u003cimg src=\"https://raw.githubusercontent.com/versilov/matrex/master/docs/matrex_logo_dark_rounded.png\" width=\"50px\" /\u003e Matrex\n\n[![Build Status](https://travis-ci.org/versilov/matrex.svg?branch=master)](https://travis-ci.org/versilov/matrex)\n[![Coverage Status](https://coveralls.io/repos/github/versilov/matrex/badge.svg?branch=master)](https://coveralls.io/github/versilov/matrex?branch=master)\n[![Inline docs](http://inch-ci.org/github/versilov/matrex.svg?branch=master)](http://inch-ci.org/github/versilov/matrex)\n[![hex.pm version](https://img.shields.io/hexpm/v/matrex.svg)](https://hex.pm/packages/matrex)\n\nFast matrix manipulation library for Elixir implemented in C native code with highly optimized CBLAS sgemm() used for matrix multiplication.\n\nFor example, vectorized linear regression is about 13 times faster, than Octave single threaded implementation.\n\nIt's also memory efficient, so you can work with large matrices,\nabout billion of elements in size.\n\nBased on matrix code from https://gitlab.com/sdwolfz/experimental/-/tree/master/exlearn\n\n## Benchmarks\n\n#### Comparison with NumPy\n\n2015 MacBook Pro, 2.2 GHz Core i7, 16 GB RAM\n\nOperations are performed on 3000×3000 matrices filled with random numbers.\n\nYou can run benchmarks from the `/bench` folder with `python numpy_bench.py` and `MIX_ENV=bench mix bench` commands.\n\n#### NumPy\n\n```\nbenchmark         iterations\taverage time\nlogistic_cost()   1000         \t1.23 ms/op\nnp.divide(A, B)   100           15.43 ms/op\nnp.add(A, B)      100           14.62 ms/op\nsigmoid(A)        50            93.28 ms/op\nnp.dot(A, B)      10            196.57 ms/op\n```\n\n#### Matrex\n\n```\nbenchmark     iterations   average time\nlogistic_cost()      1000  1.23 ms/op (on par)\ndivide(A, B)         200   7.32 ms/op (~ 2× faster)\nadd(A, B)            200   7.71 ms/op (~ 2× faster)\nsigmoid(A)           50    71.47 ms/op (23% faster)\ndot(A, B)            10    213.31 ms/op (8% slower)\n```\n\n#### Comparison with pure Elixir libraries\n\nSlaughter of the innocents, actually.\n\n2015 MacBook Pro, 2.2 GHz Core i7, 16 GB RAM\n\nDot product of 500×500 matrices\n\n| Library  | Ops/sec | Compared to Matrex |\n| -------- | ------- | ------------------ |\n| Matrex   | 674.70  |                    |\n| Matrix   | 0.0923  | 7 312.62× slower   |\n| Numexy   | 0.0173  | 38 906.14× slower  |\n| ExMatrix | 0.0129  | 52 327.40× slower  |\n\nDot product of 3×3 matrices\n\n| Library   | Ops/sec   | Compared to Matrex |\n| --------- | --------- | ------------------ |\n| Matrex    | 3624.36 K |                    |\n| GraphMath | 1310.16 K | 2.77x slower       |\n| Matrix    | 372.58 K  | 9.73x slower       |\n| Numexy    | 89.72 K   | 40.40x slower      |\n| ExMatrix  | 35.76 K   | 101.35x slower     |\n\nTransposing 1000x1000 matrix\n\n| Library  | Ops/sec | Compared to Matrex |\n| -------- | ------- | ------------------ |\n| Matrex   | 428.69  |                    |\n| ExMatrix | 9.39    | 45.64× slower      |\n| Matrix   | 8.54    | 50.17× slower      |\n| Numexy   | 6.83    | 62.80× slower      |\n\n![Transpose benchmark](https://raw.githubusercontent.com/versilov/matrex/master/docs/transposing_benchmark.png)\n\n## Example\n\nComplete example of Matrex library at work:\n[Linear regression on MNIST digits (Jupyter notebook)](https://github.com/versilov/matrex/blob/master/Matrex.ipynb)\n\n## Visualization\n\nMatrex implements `Inspect` protocol and looks nice in your console:\n\n![Inspect Matrex](https://raw.githubusercontent.com/versilov/matrex/master/docs/matrex_inspect.png)\n\nIt can even draw a heatmap of your matrix in console! Here is an animation of logistic regression training with Matrex library and some matrix heatmaps:\n\n\u003cimg src=\"https://raw.githubusercontent.com/versilov/matrex/master/docs/mnist8.png\" width=\"200px\" /\u003e\u0026nbsp;\n\u003cimg src=\"https://raw.githubusercontent.com/versilov/matrex/master/docs/mnist_sum.png\" width=\"200px\" /\u003e\u0026nbsp;\n\u003cimg src=\"https://raw.githubusercontent.com/versilov/matrex/master/docs/magic_square.png\" width=\"200px\" /\u003e\u0026nbsp;\n\u003cimg src=\"https://raw.githubusercontent.com/versilov/matrex/master/docs/twin_peaks.png\" width=\"220px\"  /\u003e\u0026nbsp;\n\u003cimg src=\"https://raw.githubusercontent.com/versilov/matrex/master/docs/neurons_mono.png\" width=\"233px\"  /\u003e\u0026nbsp;\n\u003cimg src=\"https://raw.githubusercontent.com/versilov/matrex/master/docs/logistic_regression.gif\" width=\"180px\" /\u003e\u0026nbsp;\n\u003cimg src=\"https://raw.githubusercontent.com/versilov/matrex/master/docs/iex_matrex_logo.png\" width=\"215px\" /\u003e\u0026nbsp;\n\n## Installation\n\nThe package can be installed\nby adding `matrex` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:matrex, \"~\u003e 0.6\"}\n  ]\nend\n```\n\n### MacOS\n\nEverything works out of the box, thanks to Accelerate framework. If you encounter a compilation error\n\n`native/src/matrix_dot.c:5:10: fatal error: 'cblas.h' file not found`\n\nthen make sure the XCode command-line tools are installed (`xcode-select --install`).\nIf the error still not resolved, for MacOS Mojave, run\n`open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg`\nto restore /usr/include and /usr/lib.\n\nOn MacOS 10.15 this error can be solved with\n\n`export CPATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/`\n\nor with\n\n`C_INCLUDE_PATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/Headers mix compile`\n\n### Ubuntu\n\nYou need to install scientific libraries for this package to compile:\n\n```bash\n\u003e sudo apt-get install build-essential erlang-dev libatlas-base-dev\n```\n\n### Windows\n\nIt will definitely work on Windows, but we need a makefile\nand installation instruction. Please, contribute.\n\n### Choosing which BLAS to use\n\nWith the help of `MATREX_BLAS` environment variable you can choose which BLAS library to link with.\nIt can take values `blas` (the default), `atlas`, `openblas` or `noblas`.\n\nThe last option means that you compile C code without any external dependencies, so, it should work\nanywhere with a C compiler place:\n\n```bash\n$ mix clean\n$ MATREX_BLAS=noblas mix compile\n```\n\n## Access behaviour\n\nAccess behaviour is partly implemented for Matrex, so you can do:\n\n```elixir\n\n    iex\u003e m = Matrex.magic(3)\n    #Matrex[3×3]\n    ┌                         ┐\n    │     8.0     1.0     6.0 │\n    │     3.0     5.0     7.0 │\n    │     4.0     9.0     2.0 │\n    └                         ┘\n    iex\u003e m[2][3]\n    7.0\n```\n\nOr even:\n\n```elixir\n\n    iex\u003e m[1..2]\n    #Matrex[2×3]\n    ┌                         ┐\n    │     8.0     1.0     6.0 │\n    │     3.0     5.0     7.0 │\n    └                         ┘\n```\n\nThere are also several shortcuts for getting dimensions of matrix:\n\n```elixir\n\n    iex\u003e m[:rows]\n    3\n\n    iex\u003e m[:size]\n    {3, 3}\n```\n\ncalculating maximum value of the whole matrix:\n\n```elixir\n\n    iex\u003e m[:max]\n    9.0\n```\n\nor just one of it's rows:\n\n```elixir\n\n    iex\u003e m[2][:max]\n    7.0\n```\n\ncalculating one-based index of the maximum element for the whole matrix:\n\n```elixir\n\n    iex\u003e m[:argmax]\n    8\n```\n\nand a row:\n\n```elixir\n\n    iex\u003e m[2][:argmax]\n    3\n```\n\n## Math operators overloading\n\n`Matrex.Operators` module redefines `Kernel` math operators (+, -, \\*, / \u003c|\u003e) and\ndefines some convenience functions, so you can write calculations code in more natural way.\n\nIt should be used with great caution. We suggest using it only inside specific functions\nand only for increased readability, because using `Matrex` module functions, especially\nones which do two or more operations at one call, are 2-3 times faster.\n\n### Usage example\n\n```elixir\n\n    def lr_cost_fun_ops(%Matrex{} = theta, { %Matrex{} = x, %Matrex{} = y, lambda } = _params)\n        when is_number(lambda) do\n      # Turn off original operators\n      import Kernel, except: [-: 1, +: 2, -: 2, *: 2, /: 2, \u003c|\u003e: 2]\n      import Matrex.Operators\n      import Matrex\n\n      m = y[:rows]\n\n      h = sigmoid(x * theta)\n      l = ones(size(theta)) |\u003e set(1, 1, 0.0)\n\n      j = (-t(y) * log(h) - t(1 - y) * log(1 - h) + lambda / 2 * t(l) * pow2(theta)) / m\n\n      grad = (t(x) * (h - y) + (theta \u003c|\u003e l) * lambda) / m\n\n      {scalar(j), grad}\n    end\n```\n\nThe same function, coded with module methods calls (2.5 times faster):\n\n```elixir\n    def lr_cost_fun(%Matrex{} = theta, { %Matrex{} = x, %Matrex{} = y, lambda } = _params)\n        when is_number(lambda) do\n      m = y[:rows]\n\n      h = Matrex.dot_and_apply(x, theta, :sigmoid)\n      l = Matrex.ones(theta[:rows], theta[:cols]) |\u003e Matrex.set(1, 1, 0)\n\n      regularization =\n        Matrex.dot_tn(l, Matrex.square(theta))\n        |\u003e Matrex.scalar()\n        |\u003e Kernel.*(lambda / (2 * m))\n\n      j =\n        y\n        |\u003e Matrex.dot_tn(Matrex.apply(h, :log), -1)\n        |\u003e Matrex.subtract(\n          Matrex.dot_tn(\n            Matrex.subtract(1, y),\n            Matrex.apply(Matrex.subtract(1, h), :log)\n          )\n        )\n        |\u003e Matrex.scalar()\n        |\u003e (fn\n              :nan -\u003e :nan\n              x -\u003e x / m + regularization\n            end).()\n\n      grad =\n        x\n        |\u003e Matrex.dot_tn(Matrex.subtract(h, y))\n        |\u003e Matrex.add(Matrex.multiply(theta, l), 1.0, lambda)\n        |\u003e Matrex.divide(m)\n\n      {j, grad}\n    end\n```\n\n## Enumerable protocol\n\nMatrex implements `Enumerable`, so, all kinds of `Enum` functions are applicable:\n\n```elixir\n\n    iex\u003e Enum.member?(m, 2.0)\n    true\n\n    iex\u003e Enum.count(m)\n    9\n\n    iex\u003e Enum.sum(m)\n    45\n```\n\nFor functions, that exist both in `Enum` and in `Matrex` it's preferred to use Matrex\nversion, beacuse it's usually much, much faster. I.e., for 1 000 x 1 000 matrix `Matrex.sum/1`\nand `Matrex.to_list/1` are 438 and 41 times faster, respectively, than their `Enum` counterparts.\n\n## Saving and loading matrix\n\nYou can save/load matrix with native binary file format (extra fast)\nand CSV (slow, especially on large matrices).\n\nMatrex CSV format is compatible with GNU Octave CSV output,\nso you can use it to exchange data between two systems.\n\n### Example\n\n```elixir\n\n    iex\u003e Matrex.random(5) |\u003e Matrex.save(\"rand.mtx\")\n    :ok\n    iex\u003e Matrex.load(\"rand.mtx\")\n    #Matrex[5×5]\n    ┌                                         ┐\n    │ 0.05624 0.78819 0.29995 0.25654 0.94082 │\n    │ 0.50225 0.22923 0.31941  0.3329 0.78058 │\n    │ 0.81769 0.66448 0.97414 0.08146 0.21654 │\n    │ 0.33411 0.59648 0.24786 0.27596 0.09082 │\n    │ 0.18673 0.18699 0.79753 0.08101 0.47516 │\n    └                                         ┘\n    iex\u003e Matrex.magic(5) |\u003e Matrex.divide(Matrex.eye(5)) |\u003e Matrex.save(\"nan.csv\")\n    :ok\n    iex\u003e Matrex.load(\"nan.csv\")\n    #Matrex[5×5]\n    ┌                                         ┐\n    │    16.0     ∞       ∞       ∞       ∞   │\n    │     ∞       4.0     ∞       ∞       ∞   │\n    │     ∞       ∞      12.0     ∞       ∞   │\n    │     ∞       ∞       ∞      25.0     ∞   │\n    │     ∞       ∞       ∞       ∞       8.0 │\n    └                                         ┘\n```\n\n## NaN and Infinity\n\nFloat special values, like `:nan` and `:inf` live well inside matrices,\ncan be loaded from and saved to files.\nBut when getting them into Elixir they are transferred to `:nan`,`:inf` and `:neg_inf` atoms,\nbecause BEAM does not accept special values as valid floats.\n\n```elixir\n    iex\u003e m = Matrex.eye(3)\n    #Matrex[3×3]\n    ┌                         ┐\n    │     1.0     0.0     0.0 │\n    │     0.0     1.0     0.0 │\n    │     0.0     0.0     1.0 │\n    └                         ┘\n\n    iex\u003e n = Matrex.divide(m, Matrex.zeros(3))\n    #Matrex[3×3]\n    ┌                         ┐\n    │     ∞      NaN     NaN  │\n    │    NaN      ∞      NaN  │\n    │    NaN     NaN      ∞   │\n    └                         ┘\n\n    iex\u003e n[1][1]\n    :inf\n\n    iex\u003e n[1][2]\n    :nan\n```\n\n## Creating Matrex logo from MNIST letters\n\n```elixir\n    iex(166)\u003e matrex_logo = \\\n    ...(166)\u003e \"../emnist/emnist-letters-test-images-idx3-ubyte\" \\\n    ...(166)\u003e |\u003e Matrex.load(:idx) \\\n    ...(166)\u003e |\u003e Access.get(9601..10200) \\\n    ...(166)\u003e |\u003e Matrex.list_of_rows() \\\n    ...(166)\u003e |\u003e Enum.reduce(fn x, sum -\u003e add(x, sum) end) \\\n    ...(166)\u003e |\u003e Matrex.reshape(28, 28) \\\n    ...(166)\u003e |\u003e Matrex.transpose() \\\n    ...(166)\u003e |\u003e Matrex.resize(2) \\\n    ...(166)\u003e |\u003e Matrex.heatmap(:color24bit)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fversilov%2Fmatrex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fversilov%2Fmatrex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fversilov%2Fmatrex/lists"}