{"id":17166375,"url":"https://github.com/andreaferretti/linear-algebra","last_synced_at":"2025-04-09T16:17:46.692Z","repository":{"id":66316535,"uuid":"36453014","full_name":"andreaferretti/linear-algebra","owner":"andreaferretti","description":"Linear algebra for Nim","archived":false,"fork":false,"pushed_at":"2021-01-21T10:31:07.000Z","size":1034,"stargazers_count":139,"open_issues_count":6,"forks_count":13,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-04-09T16:17:37.507Z","etag":null,"topics":["linear-algebra","matrices","nim","vectors"],"latest_commit_sha":null,"homepage":"http://andreaferretti.github.io/linear-algebra/","language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andreaferretti.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-05-28T16:56:24.000Z","updated_at":"2025-03-21T16:53:20.000Z","dependencies_parsed_at":"2023-02-24T17:00:17.479Z","dependency_job_id":null,"html_url":"https://github.com/andreaferretti/linear-algebra","commit_stats":{"total_commits":403,"total_committers":5,"mean_commits":80.6,"dds":0.00992555831265507,"last_synced_commit":"450e2f4a4903329c9266686866892b2724490c53"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreaferretti%2Flinear-algebra","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreaferretti%2Flinear-algebra/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreaferretti%2Flinear-algebra/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreaferretti%2Flinear-algebra/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andreaferretti","download_url":"https://codeload.github.com/andreaferretti/linear-algebra/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248065282,"owners_count":21041872,"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":["linear-algebra","matrices","nim","vectors"],"created_at":"2024-10-14T23:05:25.670Z","updated_at":"2025-04-09T16:17:46.649Z","avatar_url":"https://github.com/andreaferretti.png","language":"Nim","funding_links":[],"categories":[],"sub_categories":[],"readme":"  #Linear Algebra for Nim\n\n[![nimble](https://raw.githubusercontent.com/yglukhov/nimble-tag/master/nimble.png)](https://github.com/yglukhov/nimble-tag)\n\n**This library is not mantained anymore. It still works fine, but new\ndevelopment happens on [Neo](https://andreaferretti.github.io/neo/). The main\ndifference between the two libraries is that Neo only focuses in what we call\nhere dynamic vectors and matrices. Using static types to encode dimensions\nwas a nice experiment, but it turned out to be one more dimension to support\n(along with 32 vs 64 bit, CPU vs GPU, dense vs sparse...).**\n\nThis library is meant to provide basic linear algebra operations for Nim\napplications. The ambition would be to become a stable basis on which to\ndevelop a scientific ecosystem for Nim, much like Numpy does for Python.\n\nThe library has been tested on Ubuntu Linux 14.10 through 15.10 64-bit using\neither ATLAS, OpenBlas or Intel MKL. It was also tested on OSX Yosemite. The\nGPU support has been tested using NVIDIA CUDA 7.0 and 7.5.\n\nThe library is currently aligned with latest Nim devel. For versions of Nim up\nto 0.13 use version 0.4.2 of linalg.\n\nAPI documentation is [here](http://andreaferretti.github.io/linear-algebra/api.html)\n\nA lot of examples are available in the tests.\n\nTable of contents\n-----------------\n\u003c!-- TOC depthFrom:2 depthTo:6 withLinks:1 updateOnSave:1 orderedList:0 --\u003e\n\n- [Introduction](#introduction)\n- [Initialization](#initialization)\n- [Working with 32-bit](#working-with-32-bit)\n- [Accessors](#accessors)\n- [Iterators](#iterators)\n- [Equality](#equality)\n- [Pretty-print](#pretty-print)\n- [Operations](#operations)\n- [Trivial operations](#trivial-operations)\n- [Universal functions](#universal-functions)\n- [Linear Algebra functions](#linear-algebra-functions)\n- [Rewrite rules](#rewrite-rules)\n- [Type safety guarantees](#type-safety-guarantees)\n- [Linking BLAS implementations](#linking-blas-implementations)\n- [GPU support](#gpu-support)\n- [TODO](#todo)\n- [Contributing](#contributing)\n\n\u003c!-- /TOC --\u003e\n\n## Introduction\n\nThe library revolves around operations on vectors and matrices of floating\npoint numbers. It allows to compute operations either on the CPU or on the\nGPU offering identical APIs. Also, whenever possible, the dimension of vectors\nand matrices are encoded into the types in the form of `static[int]` type\nparameters. This allow to check dimensions at compile time and refuse to\ncompile invalid operations, such as summing two vectors of different sizes,\nor multiplying two matrices of incompatible dimensions.\n\nThe library defines types `Matrix64[M, N]` and `Vector64[N]` for 64-bit matrices\nand vectors of static dimension, as well as their 32-bit counterparts\n`Matrix32[M, N]` and `Vector32[N]`.\n\nFor the case where the dimension is not known at compile time, one can use\nso-called *dynamic* vectors and matrices, whose types are `DVector64` and\n`DMatrix64` (resp. `DVector32` and `DMatrix32`). Note that `DVector64` is just\nand alias for `seq[float64]` (and similarly for 32-bit), which allows to\nperform linear algebra operations on standard Nim sequences.\n\n**In all examples, types are inferred, and are shown just for explanatory purposes.**\n\n## Initialization\n\nHere we show a few ways to create matrices and vectors. All matrices methods\naccept a parameter to define whether to store the matrix in row-major (that is,\ndata are laid out in memory row by row) or column-major order (that is, data\nare laid out in memory column by column). The default is in each case\ncolumn-major.\n\nWhenever possible, we try to deduce whether to use 32 or 64 bits by appropriate\nparameters. When this is not possible, there is an optional parameter `float32`\nthat can be passed to specify the precision (the default is 64 bit).\n\nStatic matrices and vectors can be created like this:\n\n```nim\nimport linalg\n\nlet\n  v1: Vector64[5] = makeVector(5, proc(i: int): float64 = (i * i).float64)\n  v2: Vector64[7] = randomVector(7, max = 3.0) # max is optional, default 1\n  v3: Vector64[5] = constantVector(5, 3.5)\n  v4: Vector64[8] = zeros(8)\n  v5: Vector64[9] = ones(9)\n  v6: Vector64[5] = vector([1.0, 2.0, 3.0, 4.0, 5.0]) # initialize from an array...\n  m1: Matrix64[6, 3] = makeMatrix(6, 3, proc(i, j: int): float64 = (i + j).float64)\n  m2: Matrix64[2, 8] = randomMatrix(2, 8, max = 1.6) # max is optional, default 1\n  m3: Matrix64[3, 5] = constantMatrix(3, 5, 1.8, order = rowMajor) # order is optional, default colMajor\n  m4: Matrix64[3, 6] = ones(3, 6)\n  m5: Matrix64[5, 2] = zeros(5, 2)\n  m6: Matrix64[7, 7] = eye(7)\n  m7: Matrix64[2, 3] = matrix([\n    [1.2, 3.5, 4.3],\n    [1.1, 4.2, 1.7]\n  ])\n  m8: Matrix64[2, 3] = matrix(@[\n    @[1.2, 3.5, 4.3],\n    @[1.1, 4.2, 1.7]\n  ], 2, 3)\n```\n\nThe last `matrix` constructor takes a `seq` of `seq`s, but also requires\nstatically passing the dimensions to be used. The following are equivalent\nwhen `xs` is a `seq[seq[float64]]` and `M`, `N` are integers known at compile\ntime:\n\n```nim\nlet\n  m1 = matrix(xs).toStatic(M, N)\n  m2 = matrix(xs, M, N)\n```\n\nbut the latter form avoids the construction of an intermediate matrix.\n\nAll constructors that take as input an existing array or seq perform a copy of\nthe data for memory safety.\n\nDynamic matrices and vectors have similar constructors - the difference is that\nthe dimension parameter are not known at compile time:\n\n```nim\nimport linalg\n\nlet\n  M = 5\n  N = 7\n  v1: DVector64 = makeVector(M, proc(i: int): float64 = (i * i).float64)\n  v2: DVector64 = randomVector(N, max = 3.0) # max is optional, default 1\n  v3: DVector64 = constantVector(M, 3.5)\n  v4: DVector64 = zeros(M)\n  v5: DVector64 = ones(N)\n  v6: DVector64 = @[1.0, 2.0, 3.0, 4.0, 5.0] # DVectors are just seqs...\n  m1: DMatrix64 = makeMatrix(M, N, proc(i, j: int): float64 = (i + j).float64)\n  m2: DMatrix64 = randomMatrix(M, N, max = 1.6) # max is optional, default 1\n  m3: DMatrix64 = constantMatrix(M, N, 1.8, order = rowMajor) # order is optional, default colMajor\n  m4: DMatrix64 = ones(M, N)\n  m5: DMatrix64 = zeros(M, N)\n  m6: DMatrix64 = eye(M)\n  m7: DMatrix64 = matrix(@[\n    @[1.2, 3.5, 4.3],\n    @[1.1, 4.2, 1.7]\n  ])\n```\n\nIf for some reason you want to create a dynamic vector of matrix, but you want\nto write literal dimensions, you can either assign these numbers to variables\nor use the `toDynamic` proc to convert the static instances to dynamic ones:\n\n```nim\nimport linalg\n\nlet\n  M = 5\n  v1 = makeVector(M, proc(i: int): float64 = (i * i).float64)\n  v2 = makeVector(5, proc(i: int): float64 = (i * i).float64)\n\nassert v1.toStatic(5) == v2\nassert v2.toDynamic == v1\n```\n\n## Working with 32-bit\n\nOne can also instantiate 32-bit matrices and vectors. Whenever possible, the\nAPI is identical. In cases that would be ambiguous (such as `zeros`), one can\nexplicitly specify the `float32` parameter.\n\n```nim\nimport linalg\n\nlet\n  v1: Vector32[5] = makeVector(5, proc(i: int): float32 = (i * i).float32)\n  v2: Vector32[7] = randomVector(7, max = 3'f32) # max is no longer optional, to distinguish 32/64 bit\n  v3: Vector32[5] = constantVector(5, 3.5'f32)\n  v4: Vector32[8] = zeros(8, float32)\n  v5: Vector32[9] = ones(9, float32)\n  v6: Vector32[5] = vector([1'f32, 2'f32, 3'f32, 4'f32, 5'f32])\n  m1: Matrix32[6, 3] = makeMatrix(6, 3, proc(i, j: int): float32 = (i + j).float32)\n  m2: Matrix32[2, 8] = randomMatrix(2, 8, max = 1.6'f32)\n  m3: Matrix32[3, 5] = constantMatrix(3, 5, 1.8'f32, order = rowMajor) # order is optional, default colMajor\n  m4: Matrix32[3, 6] = ones(3, 6, float32)\n  m5: Matrix32[5, 2] = zeros(5, 2, float32)\n  m6: Matrix32[7, 7] = eye(7, float32)\n  m7: Matrix32[2, 3] = matrix([\n    [1.2'f32, 3.5'f32, 4.3'f32],\n    [1.1'f32, 4.2'f32, 1.7'f32]\n  ])\n  m8: Matrix32[2, 3] = matrix(@[\n    @[1.2'f32, 3.5'f32, 4.3'f32],\n    @[1.1'f32, 4.2'f32, 1.7'f32]\n  ], 2, 3)\n```\n\nSimilarly,\n\n```nim\nimport linalg\n\nlet\n  M = 5\n  N = 7\n  v1: DVector32 = makeVector(M, proc(i: int): float32 = (i * i).float32)\n  v2: DVector32 = randomVector(N, max = 3'f32) # max is not optional\n  v3: DVector32 = constantVector(M, 3.5'f32)\n  v4: DVector32 = zeros(M, float32)\n  v5: DVector32 = ones(N, float32)\n  v6: DVector32 = @[1'f32, 2'f32, 3'f32, 4'f32, 5'f32] # DVectors are just seqs...\n  m1: DMatrix32 = makeMatrix(M, N, proc(i, j: int): float32 = (i + j).float32)\n  m2: DMatrix32 = randomMatrix(M, N, max = 1.6'f32) # max is not optional\n  m3: DMatrix32 = constantMatrix(M, N, 1.8'f32, order = rowMajor) # order is optional, default colMajor\n  m4: DMatrix32 = ones(M, N, float32)\n  m5: DMatrix32 = zeros(M, N, float32)\n  m6: DMatrix32 = eye(M, float32)\n  m7: DMatrix32 = matrix(@[\n    @[1.2'f32, 3.5'f32, 4.3'f32],\n    @[1.1'f32, 4.2'f32, 1.7'f32]\n  ])\n```\n\nOne can convert precision with `to32` or `to64`:\n\n```nim\nlet\n  v64: Vector64[10] = randomVector(10)\n  v32: Vector32[10] = v64.to32()\n  m32: Matrix32[3, 8] = randomMatrix(3, 8, max = 1'f32)\n  m64: Matrix64[3, 8] = m32.to64()\n```\n\nOnce vectors and matrices are created, everything is inferred, so there are no\ndifferences in working with 32-bit or 64-bit. All examples that follow are for\n64-bit, but they would work as well for 32-bit.\n\n## Accessors\n\nVectors can be accessed as expected:\n\n```nim\nvar v = randomVector(6)\nv[4] = 1.2\necho v[3]\n```\n\nSame for matrices, where `m[i, j]` denotes the item on row `i` and column `j`,\nregardless of the matrix order:\n\n```nim\nvar m = randomMatrix(3, 7)\nm[1, 3] = 0.8\necho m[2, 2]\n```\n\nAlso one can see rows and columns as vectors\n\n```nim\nlet\n  r2: Vector64[7] = m.row(2)\n  c5: Vector64[3] = m.column(5)\n```\n\nFor memory safety, this performs a **copy** of the row or column values, at\nleast for now. One can also map vectors and matrices via a proc:\n\n```nim\nlet\n  v1 = v.map(proc(x: float64): float64 = 2 - 3 * x)\n  m1 = m.map(proc(x: float64): float64 = 1 / x)\n```\n\nSimilar operations are available for dynamic matrices and vectors as well.\n\n## Iterators\n\nOne can iterate over vector or matrix elements, as well as over rows and columns\n\n```nim\nlet\n  v = randomVector(6)\n  m = randomMatrix(3, 5)\nfor x in v: echo x\nfor i, x in v: echo i, x\nfor x in m: echo x\nfor t, x in m:\n  let (i, j) = t\n  echo i, j, x\nfor row in m.rows:\n  echo row[0]\nfor column in m.columns:\n  echo column[1]\n```\n\n## Equality\n\nThere are two kinds of equality. The usual `==` operator will compare the\ncontents of vector and matrices exactly\n\n```nim\nlet\n  u = vector([1.0, 2.0, 3.0, 4.0])\n  v = vector([1.0, 2.0, 3.0, 4.0])\n  w = vector([1.0, 3.0, 3.0, 4.0])\nu == v # true\nu == w # false\n```\n\nUsually, though, one wants to take into account the errors introduced by\nfloating point operations. To do this, use the `=~` operator, or its\nnegation `!=~`:\n\n```nim\nlet\n  u = vector([1.0, 2.0, 3.0, 4.0])\n  v = vector([1.0, 2.000000001, 2.99999999, 4.0])\nu == v # false\nu =~ v # true\n```\n\n## Pretty-print\n\nBoth vectors and matrix have a pretty-print operation, so one can do\n\n```nim\nlet m = randomMatrix(3, 7)\necho m8\n```\n\nand get something like\n\n    [ [ 0.5024584865674662  0.0798945419892334  0.7512423051567048  0.9119041361916302  0.5868388894943912  0.3600554448403415  0.4419034543022882 ]\n      [ 0.8225964245706265  0.01608615513584155 0.1442007939324697  0.7623388321096165  0.8419745686508193  0.08792951865247645 0.2902529012579151 ]\n      [ 0.8488187232786935  0.422866666087792 0.1057975175658363  0.07968277822379832 0.7526946339452074  0.7698915909784674  0.02831893268471575 ] ]\n\n## Operations\n\nA few linear algebra operations are available, wrapping BLAS libraries:\n\n```nim\nvar v1 = randomVector(7)\nlet\n  v2 = randomVector(7)\n  m1 = randomMatrix(6, 9)\n  m2 = randomMatrix(9, 7)\necho 3.5 * v1\nv1 *= 2.3\necho v1 + v2\necho v1 - v2\necho v1 * v2 # dot product\necho v1 |*| v2 # Hadamard (component-wise) product\necho l_1(v1) # l_1 norm\necho l_2(v1) # l_2 norm\necho m2 * v1 # matrix-vector product\necho m1 * m2 # matrix-matrix product\necho m1 |*| m2 # Hadamard (component-wise) product\necho max(m1)\necho min(v2)\n```\n\n## Trivial operations\n\nThe following operations do not change the underlying memory layout of matrices\nand vectors. This means they run in very little time even on big matrices, but\nyou have to pay attention when mutating matrices and vectors produced in this\nway, since the underlying data is shared.\n\n```nim\nlet\n  m1 = randomMatrix(6, 9)\n  m2 = randomMatrix(9, 6)\n  v1 = randomVector(9)\necho m1.t # transpose, done in constant time without copying\necho m1 + m2.t\nlet m3: Matrix64[9, 6] = m1.reshape(9, 6)\nlet m4: Matrix64[3, 3] = v1.asMatrix(3, 3)\nlet v2: Vector64[54] = m2.asVector\n```\n\nIn case you need to allocate a copy of the original data, say in order to\ntranspose a matrix and then mutate the transpose without altering the original\nmatrix, a `clone` operation is available:\n\n```nim\nlet m5 = m1.clone\n```\n\n## Universal functions\n\nUniversal functions are real-valued functions that are extended to vectors\nand matrices by working element-wise. There are many common functions that are\nimplemented as universal functions:\n\n```nim\nsqrt\ncbrt\nlog10\nlog2\nlog\nexp\narccos\narcsin\narctan\ncos\ncosh\nsin\nsinh\ntan\ntanh\nerf\nerfc\nlgamma\ntgamma\ntrunc\nfloor\nceil\ndegToRad\nradToDeg\n```\n\nThis means that, for instance, the following check passes:\n\n```nim\n  let\n    v1 = vector([1.0, 2.3, 4.5, 3.2, 5.4])\n    v2 = log(v1)\n    v3 = v1.map(log)\n\n  assert v2 == v3\n```\n\nUniversal functions work both on 32 and 64 bit precision, on vectors and\nmatrices, both static and dynamic.\n\nIf you have a function `f` of type `proc(x: float64): float64` you can use\n\n```nim\nmakeUniversal(f)\n```\n\nto turn `f` into a (public) universal function. If you do not want to export\n`f`, there is the equivalent template `makeUniversalLocal`.\n\n## Linear Algebra functions\n\nSome linear algebraic functions are included, currently for solving systems of\nlinear equations of the form Ax = b, for square matrices A. Functions to invert\nsquare invertible matrices are also provided. These throw floating-point errors\nin the case of non-invertible matrices.\n\nAt the moment, only static matrices are supported for system solution and\nmatrix inversion.\n\n## Rewrite rules\n\nA few rewrite rules allow to optimize a chain of linear algebra operations\ninto a single BLAS call. For instance, if you try\n\n```nim\nimport linalg/rewrites\n\necho v1 + 5.3 * v2\n```\n\nthis is not implemented as a scalar multiplication followed by a sum, but it\nis turned into a single function call.\n\n## Type safety guarantees\n\nThe library is designed with the use case of having dimensions known at compile\ntime, and leverages the compiles to ensure that dimensions match when performing\nthe appropriate operations - for instance in matrix multiplication.\n\nTo see some examples where the compiler avoids malformed operations, look\ninside `tests/compilation` (yes, in Nim one can actually test that some\noperations do not compile!).\n\nAlso, operations that mutate a vector of matrix in place are only available if\nthat vector of matrix is defined via `var` instead of `let`.\n\n## Linking BLAS implementations\n\nThe library requires to link some BLAS implementation to perform the actual\nlinear algebra operations. By default, it tries to link whatever is the default\nsystem-wide BLAS implementation.\n\nA few compile flags are available to link specific BLAS implementations\n\n    -d:atlas\n    -d:openblas\n    -d:mkl\n    -d:mkl -d:threaded\n\nPackages for various BLAS implementations are available from the package\nmanagers of many Linux distributions. On OSX one can add the brew formulas\nfrom [Homebrew Science](https://github.com/Homebrew/homebrew-science), such\nas `brew install homebrew/science/openblas`.\n\nYou may also need to add suitable paths for the includes and library dirs.\nOn OSX, this should do the trick\n\n```nim\nswitch(\"clibdir\", \"/usr/local/opt/openblas/lib\")\nswitch(\"cincludes\", \"/usr/local/opt/openblas/include\")\n```\n\nIf you have problems with MKL, you may want to link it statically. Just pass\nthe options\n\n```nim\n--dynlibOverride:mkl_intel_lp64\n--passL:${PATH_TO_MKL}/libmkl_intel_lp64.a\n```\n\nto enable static linking.\n\n## GPU support\n\nIt is possible to delegate work to the GPU using CUDA. The library has been\ntested to work with NVIDIA CUDA 7.0 and 7.5, but it is possible that earlier\nversions will work as well. In order to compile and link against CUDA, you\nshould make the appropriate headers and libraries available. If they are not\nglobally set, you can pass suitable options to the Nim compiler, such as\n\n    --cincludes:\"/usr/local/cuda/targets/x86_64-linux/include\" \\\n    --clibdir:\"/usr/local/cuda/targets/x86_64-linux/lib\"\n\nYou will also need to explicitly add `linalg` support for CUDA with the flag\n\n    -d:cublas\n\nIf you have a matrix or vector, you can move it on the GPU, and back\nlike this:\n\n```nim\nlet\n  v: Vector32[12] = randomVector(12, max=1'f32)\n  vOnTheGpu: CudaVector[12] = v.gpu()\n  vBackOnTheCpu: Vector32[12] = vOnTheGpu.cpu()\n```\n\nVectors and matrices on the GPU support linear-algebraic operations via cuBLAS,\nexactly like their CPU counterparts. A few operation - such as reading a single\nelement - are not supported, as it does not make much sense to copy a single\nvalue back and forth from the GPU. Usually it is advisable to move vectors\nand matrices to the GPU, make as many computations as possible there, and\nfinally move the result back to the CPU.\n\nThe following are all valid operations, assuming `v` and `w` are vectors on the\nGPU, `m` and `n` are matrices on the GPU and the dimensions are compatible:\n\n```nim\nv * 3'f32\nv + w\nv -= w\nm * v\nm - n\nm * n\n```\n\nFor more information, look at the tests in `tests/cuda`.\n\n## TODO\n\n* Add support for matrices and vector on the stack\n* Add more functional interfaces (foldl, scanl)\n* Use rewrite rules to optimize complex operations into a single BLAS call\n* More specialized BLAS operations\n* Add operations from LAPACK\n* Support slicing/nonconstant steps\n* Make `row` and `column` operations non-copying\n* Better types to avoid out of bounds exceptions when statically checkable\n* Add a fallback Nim implementation, that is valid over other rings\n* Move CUBLAS and LAPACK to separate libraries required via nimble\n* Try on more platforms/configurations\n* Make a proper benchmark\n* Improve documentation\n* Better pretty-print\n\n## Contributing\n\nEvery contribution is very much appreciated! This can range from:\n\n* using the library and reporting any issues and any configuration on which\n  it works fine\n* building other parts of the scientific environment on top of it\n* writing blog posts and tutorials\n* contributing actual code (see the **TODO** section)\n\nThe library has to cater many different use cases, hence the vector and matrix\ntypes differ in various axes:\n\n* 32/64 bit\n* CPU/GPU\n* static/dynamic\n* (on the stack? non-contiguous? non GC pointers?)\n\nIn order to avoid a combinatorial explosion of operations, a judicious use of\ntemplates and union types helps to limit the actual implementations that have\nto be written.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreaferretti%2Flinear-algebra","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandreaferretti%2Flinear-algebra","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreaferretti%2Flinear-algebra/lists"}