{"id":16686435,"url":"https://github.com/dastrobu/ndarray","last_synced_at":"2025-07-04T13:36:32.984Z","repository":{"id":42424597,"uuid":"187095574","full_name":"dastrobu/NdArray","owner":"dastrobu","description":"N dimensional array package for numeric computing in swift.","archived":false,"fork":false,"pushed_at":"2023-12-17T21:21:18.000Z","size":2334,"stargazers_count":24,"open_issues_count":3,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-18T21:21:40.860Z","etag":null,"topics":["linear-algebra","ndarray","numerics","swift"],"latest_commit_sha":null,"homepage":"https://dastrobu.github.io/NdArray/documentation/ndarray/","language":"Swift","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/dastrobu.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-16T20:17:32.000Z","updated_at":"2024-08-13T06:20:40.000Z","dependencies_parsed_at":"2023-12-17T20:39:41.679Z","dependency_job_id":"07709987-e833-4fa2-b21f-63adfb6a6108","html_url":"https://github.com/dastrobu/NdArray","commit_stats":{"total_commits":117,"total_committers":1,"mean_commits":117.0,"dds":0.0,"last_synced_commit":"8c676ce793da18dddb4c9ae97187803c3bd6abc6"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dastrobu%2FNdArray","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dastrobu%2FNdArray/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dastrobu%2FNdArray/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dastrobu%2FNdArray/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dastrobu","download_url":"https://codeload.github.com/dastrobu/NdArray/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245115945,"owners_count":20563260,"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","ndarray","numerics","swift"],"created_at":"2024-10-12T15:05:56.966Z","updated_at":"2025-03-23T14:31:38.441Z","avatar_url":"https://github.com/dastrobu.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NdArray\n\n[![Swift Version](https://img.shields.io/badge/swift-5.9-blue.svg)](https://swift.org)\n![Platform](https://img.shields.io/badge/platform-macOS|iOS|tvOS|whatchOS-lightgray.svg)\n![Build](https://github.com/dastrobu/NdArray/actions/workflows/ci.yaml/badge.svg)\n\nN dimensional array package for numeric computing in [Swift](https://swift.org).\n\nThe package is inspired by [NumPy](https://www.numpy.org), the well known [python](https://python.org) package for\nnumerical computations. This Swift package is certainly far away from the maturity of NumPy but implements some key\nfeatures to enable fast and simple handling of multidimensional numeric data.\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n## Table of Contents\n\n- [Installation](#installation)\n  - [Swift Package Manager](#swift-package-manager)\n- [Multiple Views on Underlying Data](#multiple-views-on-underlying-data)\n- [Sliced and Strided Access](#sliced-and-strided-access)\n  - [Slices and the Stride Operator `~`](#slices-and-the-stride-operator-)\n  - [Single Slice](#single-slice)\n  - [`UnboundedRange` Slices](#unboundedrange-slices)\n  - [`Range` and `ClosedRange` Slices](#range-and-closedrange-slices)\n  - [`PartialRangeFrom`, `PartialRangeUpTo` and `PartialRangeThrough` Slices](#partialrangefrom-partialrangeupto-and-partialrangethrough-slices)\n- [Element Manipulation](#element-manipulation)\n- [Reshaping](#reshaping)\n- [Elementwise Operations](#elementwise-operations)\n  - [Scalars](#scalars)\n  - [Basic Functions](#basic-functions)\n- [Linear Algebra Operations for `Double` and `Float` `NdArray`s.](#linear-algebra-operations-for-double-and-float-ndarrays)\n  - [Matrix Vector Multiplication](#matrix-vector-multiplication)\n  - [Matrix Matrix Multiplication](#matrix-matrix-multiplication)\n  - [Matrix Transpose](#matrix-transpose)\n  - [Matrix Inversion](#matrix-inversion)\n  - [LU Factorization](#lu-factorization)\n  - [Singular Value Decomposition (SVD)](#singular-value-decomposition-svd)\n  - [Solve a Linear System of Equations](#solve-a-linear-system-of-equations)\n- [Pretty Printing](#pretty-printing)\n- [Interaction with Swift Arrays](#interaction-with-swift-arrays)\n- [Raw Data Access](#raw-data-access)\n- [Type Concept](#type-concept)\n  - [Subtypes](#subtypes)\n- [Numerical Backend](#numerical-backend)\n- [Debugging](#debugging)\n- [API Changes](#api-changes)\n  - [TLDR](#tldr)\n  - [Removal of `NdArraySlice`](#removal-of-ndarrayslice)\n- [Not Implemented](#not-implemented)\n- [Out of Scope](#out-of-scope)\n- [Docs](#docs)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Installation\n\nThe API is stable from versions up to `0.3.0`. Version `0.4.0` deprecates the old slicing API and introduces a more type\nsafe API. Version `0.5.0` will remove the old slicing API and thus contain breaking changes. It is recommended to fix\nall compiler warnings on `0.4.0` before upgrading to `0.5.0`, see also [API Changes](#api-changes).\n\n### Swift Package Manager\n\n```swift\nlet package = Package(\n    dependencies: [\n        .package(url: \"https://github.com/dastrobu/NdArray.git\", from: \"0.5.0\"),\n    ]\n)\n```\n\n## Multiple Views on Underlying Data\n\nTwo arrays can easily point to the same data and data can be modified through both views. This is significantly\ndifferent from the Swift internal array object, which has copy on write semantics, meaning you cannot pass around\npointers to the same data. Whereas this behaviour is very nice for small amounts of data, since it reduces side effects.\nFor numerical computation with huge arrays, it is preferable to let the programmer manage copies. The behaviour of the\nNdArray is very similar to\n[NumPy's ndarray object](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html). Here is an example:\n\n```swift\nlet a = NdArray\u003cDouble\u003e([9, 9, 0, 9])\nlet b = NdArray(a)\na[2] = 9.0\nprint(b) // [9.0, 9.0, 9.0, 9.0]\nprint(a.ownsData) // true\nprint(b.ownsData) // false\n``` \n\n## Sliced and Strided Access\n\nLike NumPy's ndarray, slices and strides can be created.\n\n```swift\nlet a = NdArray\u003cDouble\u003e.range(to: 10)\nlet b = NdArray(a[0... ~ 2]) // every second element\nprint(a) // [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]\nprint(b) // [0.0, 2.0, 4.0, 6.0, 8.0]\nprint(b.strides) // [2]\nb[0...].set(0)\nprint(a) // [0.0, 1.0, 0.0, 3.0, 0.0, 5.0, 0.0, 7.0, 0.0, 9.0]\nprint(b) // [0.0, 0.0, 0.0, 0.0, 0.0]\n``` \n\nThis creates an array first, then a strided view on the data, making it easy to set every second element to 0.\n\n### Slices and the Stride Operator `~`\n\nAs shown in the previous example, strides can be defined via the stride operator `~`. The unbounded range slice `0...`\ntakes all elements along an axis. The stride `~ 2` selects only every second element. Here is a short comparison with\nNumPy's syntax.\n\n```\nNdArray        NumPy\na[0...]        a[::]\na[0... ~ 2]    a[::2]\na[..\u003c42 ~ 2]   a[:42:2]\na[3..\u003c42 ~ 2]  a[3:42:2]\na[3...42 ~ 2]  a[3:41:2]\n```\n\nAlternatively, slice objects can be created programmatically. The following notations are equivalent:\n\n```\n a[0...] ≡ a[Slice()]\n a[1...] ≡ a[Slice(lowerBound: 1)]\n a[..\u003c42] ≡ a[Slice(upperBound: 42)]\n a[...42] ≡ a[Slice(upperBound: 43)]\n a[1..\u003c42] ≡ a[Slice(lowerBound: 1, upperBound: 42)]\n a[1... ~ 2] ≡ a[Slice(lowerBound: 1, upperBound, stride: 2)]\n a[..\u003c42 ~ 3] ≡ a[Slice(upperBound: 42, stride: 3)]\n a[1..\u003c42 ~ 3] ≡ a[Slice(lowerBound: 1, upperBound: 42, stride: 3)]\n```\n\nNote, to avoid confusion with pure indexing, integer literals need to be converted to a slice explicitly. This means\n\n```swift\nlet a = NdArray\u003cDouble\u003e.range(to: 10)\nlet _ = a[1] // does not work\nlet s1: NdArray\u003cDouble\u003e = a[Slice(1)] // selects slice at index one along zeroth dimension\nlet a1: Double = a[1] // selects first element\n```\n\nMore detailed examples on each slice type are provided in the sections below.\n\n### Single Slice\n\nA single slice e.g. a row of a matrix is indexed by a so called index slice `Slice(_: Int)`:\n\n```swift\nlet a = NdArray\u003cDouble\u003e.ones([2, 2])\nprint(a)\n// [[1.0, 1.0],\n//  [1.0, 1.0]]\na[Slice(1)].set(0.0)\nprint(a)\n// [[1.0, 1.0],\n//  [0.0, 0.0]]\na[0..., 1].set(2.0)\nprint(a)\n// [[1.0, 2.0],\n//  [0.0, 2.0]]\n``` \n\nNote, using element index on a one dimensional array will not access the element,\nuse [element indexing](#element-manipulation) instead or use the `Vector` subtype which supports element indexing.\n\n```swift\nlet a = NdArray\u003cDouble\u003e.range(to: 4)\nprint(a[Slice(0)]) // [0.0]\nprint(a[0]) // 0.0\nlet v = Vector(a)\nprint(v[0] as Double) // 0.0\nprint(v[0]) // 0.0\n```\n\n### `UnboundedRange` Slices\n\nUnbounded ranges select all elements, this is helpful to access lower dimensions of a multidimensional array\n\n```swift\nlet a = NdArray\u003cDouble\u003e.ones([2, 2])\nprint(a)\n// [[1.0, 1.0],\n//  [1.0, 1.0]]\na[0..., 1].set(0.0)\nprint(a)\n// [[1.0, 0.0],\n//  [1.0, 0.0]]\n``` \n\nor with a stride, selecting every nth element.\n\n```swift\nlet a = NdArray\u003cDouble\u003e.range(to: 10).reshaped([5, 2])\nprint(a)\n// [[0.0, 1.0],\n//  [2.0, 3.0],\n//  [4.0, 5.0],\n//  [6.0, 7.0],\n//  [8.0, 9.0]]\na[0... ~ 2].set(0.0)\nprint(a)\n// [[0.0, 0.0],\n//  [2.0, 3.0],\n//  [0.0, 0.0],\n//  [6.0, 7.0],\n//  [0.0, 0.0]]\n``` \n\nDue to a limitation in the type system, the true unbounded range operator `...` cannot be used. Instead, the\nidiom `0...`\nshould be preferred to specify an unbound range.\n\n### `Range` and `ClosedRange` Slices\n\nRanges `n..\u003cm` and closed ranges `n...m` allow selecting certain sub arrays.\n\n```swift\nlet a = NdArray\u003cDouble\u003e.range(to: 10)\nprint(a[2..\u003c4]) // [2.0, 3.0]\nprint(a[2...4]) // [2.0, 3.0, 4.0]\nprint(a[2...4 ~ 2]) // [2.0, 4.0]\n``` \n\n### `PartialRangeFrom`, `PartialRangeUpTo` and `PartialRangeThrough` Slices\n\nPartial ranges `...\u003cm`, `...m` and `n...` define only one bound.\n\n```swift\nlet a = NdArray\u003cDouble\u003e.range(to: 10)\nprint(a[..\u003c4]) // [0.0, 1.0, 2.0, 3.0]\nprint(a[...4]) // [0.0, 1.0, 2.0, 3.0, 4.0]\nprint(a[4...]) // [4.0, 5.0, 6.0, 7.0, 8.0, 9.0]\nprint(a[4... ~ 2]) // [4.0, 6.0, 8.0]\n``` \n\n## Element Manipulation\n\nIndividual elements can be indexed by passing a (Swift) array as index or varargs.\n\n```swift\nlet a = NdArray\u003cDouble\u003e.range(to: 12).reshaped([2, 2, 3])\na[[0, 1, 2]]\na[0, 1, 2]\n```\n\nFor efficient iteration of all indices consider using e.g. `apply`, `map` or `reduce`.\n\n```swift\nlet a = NdArray\u003cDouble\u003e.ones(4).reshaped([2, 2])\nlet b = a.map {\n    $0 * 2\n} // map to new array\nprint(b)\n// [[2.0, 2.0],\n//  [2.0, 2.0]]\na.apply {\n    $0 * 3\n} // in place\nprint(a)\n// [[3.0, 3.0],\n//  [3.0, 3.0]]\nprint(a.reduce(0) {\n    $0 + $1\n}) // 12.0\n```\n\nScaling every second element in a matrix by its row index could be done in the following way\n\n```swift\nlet a = NdArray\u003cDouble\u003e.ones([4, 3])\nfor i in 0..\u003ca.shape[0] {\n    a[Slice(i), 0... ~ 2] *= Double(i)\n}\nprint(a)\n// [[0.0, 1.0, 0.0],\n//  [1.0, 1.0, 1.0],\n//  [2.0, 1.0, 2.0],\n//  [3.0, 1.0, 3.0]]\n```\n\nAlternatively one can use classical loops and convert each row to a vector for efficient element indexing\n\n```swift\nlet a = NdArray\u003cDouble\u003e.ones([4, 3])\nfor i in 0..\u003ca.shape[0] {\n    let ai = Vector(a[Slice(i)])\n    for j in stride(from: 0, to: a.shape[1], by: 2) {\n        ai[j] *= Double(i)\n    }\n}\nprint(a)\n// [[0.0, 1.0, 0.0],\n//  [1.0, 1.0, 1.0],\n//  [2.0, 1.0, 2.0],\n//  [3.0, 1.0, 3.0]]\n```\n\n## Reshaping\n\nLike in NumPy, an array can be reshaped to any compatible shape without modifying data. That means the shape and strides\nare recomputed to re-interpret the data.\n\n```swift\nlet a = NdArray\u003cDouble\u003e.range(to: 12)\nprint(a.reshaped([2, 6]))\n// [[ 0.0,  1.0,  2.0,  3.0,  4.0,  5.0],\n//  [ 6.0,  7.0,  8.0,  9.0, 10.0, 11.0]]\nprint(a.reshaped([2, 6], order: .F))\n// [[ 0.0,  2.0,  4.0,  6.0,  8.0, 10.0],\n//  [ 1.0,  3.0,  5.0,  7.0,  9.0, 11.0]]\nprint(a.reshaped([3, 4]))\n// [[ 0.0,  1.0,  2.0,  3.0],\n//  [ 4.0,  5.0,  6.0,  7.0],\n//  [ 8.0,  9.0, 10.0, 11.0]]\nprint(a.reshaped([4, 3]))\n// [[ 0.0,  1.0,  2.0],\n//  [ 3.0,  4.0,  5.0],\n//  [ 6.0,  7.0,  8.0],\n//  [ 9.0, 10.0, 11.0]]\nprint(a.reshaped([2, 2, 3]))\n// [[[ 0.0,  1.0,  2.0],\n//   [ 3.0,  4.0,  5.0]],\n//\n//  [[ 6.0,  7.0,  8.0],\n//   [ 9.0, 10.0, 11.0]]]\n```\n\nA copy will only be made if required to create an array with the specified order.\n\n## Elementwise Operations\n\n### Scalars\n\nArithmetic operations with scalars work in-place,\n\n```swift\nlet a = NdArray\u003cDouble\u003e.ones([2, 2])\na *= 2\na /= 2\na += 2\na /= 2\n```\n\nor with implicit copies.\n\n```swift\nvar b: NdArray\u003cDouble\u003e\nb = a * 2\nb = a / 2\nb = a + 2\nb = a - 2\n```\n\n### Basic Functions\n\nThe following basic functions can be applied to any `Float` or `Double` array.\n\n```swift\nlet a = NdArray\u003cDouble\u003e.ones([2, 2])\nvar b: NdArray\u003cDouble\u003e\n\nb = abs(a)\n\nb = acos(a)\nb = asin(a)\nb = atan(a)\n\nb = cos(a)\nb = sin(a)\nb = tan(a)\n\nb = cosh(a)\nb = sinh(a)\nb = tanh(a)\n\nb = exp(a)\nb = exp2(a)\n\nb = log(a)\nb = log10(a)\nb = log1p(a)\nb = log2(a)\nb = logb(a)\n```\n\nThe `abs` function is also defined for `SignedNumeric`, such as `Int` arrays.\n\n```swift\nlet a = NdArray\u003cInt\u003e.range(from: -2, to: 2)\nprint(a) // [-2, -1,  0,  1]\nprint(abs(a)) // [2, 1, 0, 1]\n```\n\n## Linear Algebra Operations for `Double` and `Float` `NdArray`s.\n\nLinear algebra support is currently very basic.\n\n### Matrix Vector Multiplication\n\n```swift\nlet A = Matrix\u003cDouble\u003e.ones([2, 2])\nlet x = Vector\u003cDouble\u003e.ones(2)\nprint(A * x) // [2.0, 2.0]\n```\n\n### Matrix Matrix Multiplication\n\n```swift\nlet A = Matrix\u003cDouble\u003e.ones([2, 2])\nlet x = Matrix\u003cDouble\u003e.ones([2, 2])\nprint(A * x)\n// [[2.0, 2.0],\n//  [2.0, 2.0]]\n```\n\n### Matrix Transpose\n\n```swift\nlet A = Matrix\u003cDouble\u003e(NdArray.range(to: 4).reshaped([2, 2]))\nprint(A.transposed())\n// [[0.0,  2.0],\n//  [1.0,  3.0]]\n```\n\n### Matrix Inversion\n\n```swift\nlet A = Matrix\u003cDouble\u003e(NdArray.range(to: 4).reshaped([2, 2]))\nprint(try A.inverted())\n// [[-1.5,  0.5],\n//  [ 1.0,  0.0]]\n```\n\n### LU Factorization\n\n```swift\nlet A = Matrix\u003cDouble\u003e(NdArray.range(to: 4).reshaped([2, 2]))\nlet (P, L, U) = try A.lu()\nprint(P)\n// [[0.0, 1.0],\n//  [1.0, 0.0]]\nprint(L)\n// [[1.0, 0.0],\n//  [0.0, 1.0]]\nprint(U)\n// [[2.0, 3.0],\n//  [0.0, 1.0]]\nprint(P * L * U)\n// [[0.0, 1.0],\n//  [2.0, 3.0]]\n```\n\nSee also `luInPlace()` for more advanced use cases that avoid creating full matrices.\n\n### Singular Value Decomposition (SVD)\n\n```swift\nlet A = Matrix\u003cDouble\u003e(NdArray.range(from: 1, to: 9).reshaped([2, 4]))\nlet (U, s, Vt) = try A.svd()\nprint(U)\n// [[-0.3761682344281408, -0.9265513797988838],\n//  [-0.9265513797988838,  0.3761682344281408]]\nprint(s)\n// [14.227407412633742, 1.2573298353791098]\nprint(Vt)\n// [[ -0.3520616924890126, -0.44362578258952023,  -0.5351898726900277,  -0.6267539627905352],\n//  [  0.7589812676751458,  0.32124159914593237,  -0.1164980693832819,   -0.554237737912496],\n//  [ -0.4000874340557387,  0.25463292200666415,   0.6909964581538871,  -0.5455419461048127],\n//  [ -0.3740722458438949,   0.7969705609558909,   -0.471724384380099,  0.04882606926810252]]\nlet Sd = Matrix(diag: s)\nlet S = Matrix\u003cDouble\u003e.zeros(A.shape)\nlet mn = A.shape.min()!\nS[..\u003cmn, ..\u003cmn] = Sd\nprint(S)\n// [[14.227407412633742,                0.0,                0.0,                0.0],\n//  [               0.0, 1.2573298353791098,                0.0,                0.0]]\nprint(U * S * Vt)\n// [[1.0000000000000004,                2.0, 3.0000000000000004, 3.9999999999999996],\n//  [ 4.999999999999999,  6.000000000000001,  7.000000000000001,                8.0]]\n```\n\n### Solve a Linear System of Equations\n\nwith single right-hand side\n\n```swift\nlet A = Matrix\u003cDouble\u003e(NdArray.range(to: 4).reshaped([2, 2]))\nlet x = Vector\u003cDouble\u003e.ones(2)\nprint(try A.solve(x)) // [-1.0,  1.0]\n```\n\nwith multiple right hand sides\n\n```swift\nlet A = Matrix\u003cDouble\u003e(NdArray.range(to: 4).reshaped([2, 2]))\nlet x = Matrix\u003cDouble\u003e.ones([2, 2])\nprint(try A.solve(x))\n// [[-1.0, -1.0],\n//  [ 1.0,  1.0]]\n```\n\n## Pretty Printing\n\nMultidimensional arrays can be printed in a human friendly way.\n\n```swift\nprint(NdArray\u003cDouble\u003e.ones([2, 3, 4]))\n// [[[1.0, 1.0, 1.0, 1.0],\n//  [1.0, 1.0, 1.0, 1.0],\n//  [1.0, 1.0, 1.0, 1.0]],\n//\n// [[1.0, 1.0, 1.0, 1.0],\n//  [1.0, 1.0, 1.0, 1.0],\n//  [1.0, 1.0, 1.0, 1.0]]]\nprint(\"this is a 2d array in one line \\(NdArray\u003cDouble\u003e.zeros([2, 2]), style: .singleLine)\")\n// this is a 2d array in one line [[0.0, 0.0], [0.0, 0.0]]\nprint(\"this is a 2d array in multi line format line \\n\\(NdArray\u003cDouble\u003e.zeros([2, 2]), style: .multiLine)\")\n// this is a 2d array in multi line format line\n// [[0.0, 0.0],\n//  [0.0, 0.0]]\n```\n\n## Interaction with Swift Arrays\n\nNormal Swift arrays can be converted to a NdArray and back as follows.\n\n```swift\nlet a = [1, 2, 3]\nlet b = NdArray(a)\nlet c = b.dataArray\nprint(c)\n// [1, 2, 3]\n```\n\nIt should be noted that the conversion requires copying data. This is usually quite fast, but if a numeric algorithm\nwould convert very small array back and forth, it could slow down the algorithm unnecessarily.\nMultidimensional arrays will be represented as flat arrays, to convert a vector or a matrix to nested arrays, make use\nof the sequence protocols as shown below.\n\n```swift\nlet v = Vector\u003cInt\u003e([1, 2, 3])\nprint(Array(v))\n// [1, 2, 3]\nlet M = Matrix\u003cInt\u003e([\n  [1, 2, 3],\n  [3, 2, 1],\n])\nlet a = Array(M).map({ Array($0) })\nprint(a)\n// [[1, 2, 3], [3, 2, 1]]\n```\n\n## Raw Data Access\n\nInstead of converting to another type, sometimes it can be helpful to access raw data. Especially, when passing data to\nanother low level numeric library.\nRaw data can be accessed via the `data` property.\n\n```swift\nlet a = NdArray([1, 2, 3])\nlet aData = a.data\nprint(aData)\n// UnsafeMutableBufferPointer(start: 0x0000600002796760, count: 3)\n```\n\nNote that strides and dimensions must be taken care of manually.\n\n## Type Concept\n\nThe idea is to have basic `NdArray` type, which keeps a pointer to data and stores shape and stride information. Since\nthere can be multiple `NdArray` objects referring to the same data, ownership is tracked explicitly. If an array owns\nits data is stored in the `ownsData` flag (similar to NumPy's ndarray)\nWhen creating a new array from an existing one, no copy is made unless necessary. Here are a few examples\n\n```swift\nlet A = NdArray\u003cDouble\u003e.ones(5)\nvar B = NdArray(A) // no copy\nB = NdArray(copy: A) // copy explicitly required\nB = NdArray(A[0... ~ 2]) // no copy, but B will not be contiguous\nB = NdArray(A[0... ~ 2], order: .C) // copy, because otherwise new array will not have C ordering\n```\n\n### Subtypes\n\nTo be able to define operators for matrix vector multiplication and matrix matrix multiplication, subtypes like\n`Matrix` and `Vector` are defined. Since no data is copied when creating a matrix or vector from an array, they can be\nconverted anytime, thereby making sure the shapes match requirements of the subtype.\n\n```swift\nlet a = NdArray\u003cDouble\u003e.ones([2, 2])\nlet b = NdArray\u003cDouble\u003e.zeros(2)\nlet A = Matrix\u003cDouble\u003e(a) // matrix from array without copy\nlet x = Vector\u003cDouble\u003e(b) // vector from array without copy\nlet Ax = A * x // matrix vector multiplication is defined\nlet _ = Vector\u003cDouble\u003e(a) // Precondition failed: Cannot create vector with shape [2, 2]. Vector must have one dimension.\n````\n\nFurthermore, algorithms specific for subtypes like a matrix will be defined as method on the subtype, e.g. `solve`\n\n```swift\nlet A = Matrix\u003cDouble\u003e(NdArray.range(to: 4).reshaped([2, 2]))\nlet x = Vector\u003cDouble\u003e.ones(2)\nprint(try A.solve(x)) // [-1.0,  1.0]\n```\n\n## Numerical Backend\n\nNumerical operations are performed using [BLAS](http://www.netlib.org/blas), see also\n[BLAS cheat sheet](http://www.netlib.org/blas/blasqr.pdf) for an overview and [LAPACK](http://www.netlib.org/lapack).\nThe functions of these libraries are provided by the\n[Accelerate Framework](https://developer.apple.com/documentation/accelerate) and are available on most Apple platforms.\n\n## Debugging\n\nWhen debugging some code, sometimes it can be helpful to look at the raw data in the debugger. This can be done with\nhelp of the `data` property, which is a typed `UnsafeMutableBufferPointer` pointing to the raw data.\n\nHere is an example in lldb:\n\n```\n(lldb) p a.data\n(UnsafeMutableBufferPointer\u003cDouble\u003e) $R1 = 6 values (0x113d05000) {\n  [0] = 1\n  [1] = 2\n  [2] = 3\n  [3] = 4\n  [4] = 5\n  [5] = 6\n}\n```\n\n## API Changes\n\n### TLDR\n\nTo migrate from `\u003c=0.3.0` to `0.4.0` upgrade to `0.4.0` first and fix all compile warnings. Do not skip `0.4.0`, since\nthis can result in undesired behaviour (`a[0..., 2]` will be interpreted as \"take slice along zeroth and first\ndimension\" from `0.5.0` instead of \"take slice along zeroth dimension with stride 2\" `\u003c=0.3.0`).\n\nHere are a few rules of thumb to fix compile warnings after upgrading to `0.4.0`:\n\n```\na[...] =\u003e a[[0...]] // UnboundedRange is now expresed by 0...\na[..., 2] =\u003e a[[0... ~ 2]] // strides are now expressed by the stride operator ~\na[...][3] =\u003e a[[0..., Slice(3)]] // multi dimensional slices are now created within one subscript call [] not many [][][]\n```\n\n### Removal of `NdArraySlice`\n\nPrior to version `0.4.0` using slices on an `NdArray` returned a `NdArraySlice` object. This slice object is similar to\nan array but keeps track how deeply it is sliced.\n\n```swift\nlet A = NdArray\u003cDouble\u003e.ones([2, 2, 2])\nvar B = A[...] // NdArraySlice with sliced = 1, i.e. one dimension has been sliced\nB = A[0...][0... ~ 2] // NdArraySlice with sliced = 2, i.e. one dimension has been sliced\nB = A[0...][0... ~ 2][..\u003c1] // NdArraySlice with sliced = 3, i.e. one dimension has been sliced\nB = A[0...][0... ~ 2][..\u003c1][0...] // Precondition failed: Cannot slice array with ndim 3 more than 3 times.\n```\n\nSo it was recommended to convert to an `NdArray` after slicing before continuing to work with the data.\n\n```swift\nlet A = NdArray\u003cDouble\u003e.ones([2, 2, 2])\nvar B = NdArray(A[...]) // B has shape [2, 2, 2]\nB = NdArray(A[...][..., 2]) // B has shape [2, 1, 2]\nB = NdArray(A[0...][0..., 2][..\u003c1]) // B has shape [2, 1, 1]\n```\n\nWhen using slices to assign data, no type conversion is required.\n\n```swift\nlet A = NdArray\u003cDouble\u003e.ones([2, 2])\nlet B = NdArray\u003cDouble\u003e.zeros(2)\nA[0..., 0] = B[0...]\nprint(A)\n// [[0.0, 1.0],\n//  [0.0, 1.0]]\n```\n\nThese conversions are not necessary anymore, starting from version `0.4.0`. With the new slice API, based on the `Slice`\nobject, slices are obtained by\n\n```swift\nlet A = NdArray\u003cDouble\u003e.ones([2, 2, 2])\nvar B = A[0...] // NdArray with sliced = 1, i.e. one dimension has been sliced\nB = A[0..., 0... ~ 2] // NdArray with sliced = 2, i.e. one dimension has been sliced\nB = A[0..., 0... ~ 2, ..\u003c1] // NdArray with sliced = 3, i.e. one dimension has been sliced\nB = A[0..., 0... ~ 2, ..\u003c1, 0...] // Precondition failed: Cannot slice array with ndim 3 more than 3 times.\n```\n\nWith this API, there is no subtypes returned when slicing, requiring to remember how many times the array was already\nsliced. The old slice API is deprecated and will be removed in `0.5.0`.\n\n## Not Implemented\n\nSome features are not implemented yet, but are planned for the near future.\n\n* Elementwise multiplication of Double and Float arrays. Planned as `multiply(elementwiseBy), divide(elementwiseBy)`\n  employing `vDSP_vmulD`\n  Note that this can be done with help of `map` currently.\n\n## Out of Scope\n\nSome features would be nice to have at some time but currently out of scope.\n\n* Complex number arithmetic (explicit support for complex numbers is not planned). One can create arrays for any type\n  though (`NdArray\u003cComplex\u003e`), just arithmetic operations will not be defined. These could of course be added inside\n  application code.\n\n## Docs\n\nRead the generated [docs](https://dastrobu.github.io/NdArray/documentation/ndarray).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdastrobu%2Fndarray","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdastrobu%2Fndarray","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdastrobu%2Fndarray/lists"}