module

Narray

Operations for NArray

This module provides various array manipulation operations for NArray, including:

  • Reshaping arrays (reshape, reshape!)
  • Transposing arrays (transpose, transpose!)
  • Concatenating arrays (concatenate, vstack, hstack)
  • Masking operations (mask, mask_set)

Constants

VERSION = "0.1.0"

Class methods

arange(start : Number, stop : Number, step : Number, type : T.class) forall T

Creates a new array with evenly spaced values within a given interval with the specified type.

arr = Narray.arange(0, 5, 1_f64, Float64)
arr.data          # => [0.0, 1.0, 2.0, 3.0, 4.0]
arr.data[0].class # => Float64

See also: Narray.linspace.

Source
arange(start : Number, stop : Number, step = 1)

Creates a new array with evenly spaced values within a given interval.

arr = Narray.arange(0, 10, 2)
arr.shape # => [5]
arr.ndim  # => 1
arr.size  # => 5
arr.data  # => [0, 2, 4, 6, 8]

See also: Narray.linspace.

Source
array(shape : ::Array(Int32), data : ::Array(T)) forall T

Creates a new array with the given shape and data.

arr = Narray.array([2, 2], [1, 2, 3, 4])
arr.shape # => [2, 2]
arr.ndim  # => 2
arr.size  # => 4
arr.data  # => [1, 2, 3, 4]

Raises ArgumentError if the data size does not match the shape.

See also: Narray.zeros, Narray.ones.

Source
broadcast(array : Array(T), new_shape : ::Array(Int32)) : Array(T) forall T

Broadcasts an array to a new shape.

The new shape must be broadcast compatible with the original shape. If the shapes are already the same, returns the original array.

# Original array with shape [3]
arr = Narray.array([3], [1, 2, 3])

# Broadcast to shape [2, 3]
result = Narray.broadcast(arr, [2, 3])
result.shape # => [2, 3]
result.data  # => [1, 2, 3, 1, 2, 3]

# Broadcasting with dimension size 1
arr2 = Narray.array([2, 1], [1, 2])
result2 = Narray.broadcast(arr2, [2, 3])
result2.shape # => [2, 3]
result2.data  # => [1, 1, 1, 2, 2, 2]

Raises ArgumentError if the shapes are incompatible for broadcasting.

See also: Narray.broadcast_shapes, Array#broadcast_to.

Source
broadcast_shapes(shape1 : ::Array(Int32), shape2 : ::Array(Int32)) : ::Array(Int32) | Nil

Checks if two shapes are broadcast compatible and returns the resulting shape.

Broadcasting rules:

  • Arrays with fewer dimensions are padded with ones on the left.
  • Arrays with dimension size 1 are stretched to match the other array's size.
  • If dimensions are incompatible (neither equal nor one is 1), returns nil.
# Same shapes
Narray.broadcast_shapes([2, 3], [2, 3]) # => [2, 3]

# Broadcasting scalar to array
Narray.broadcast_shapes([2, 3], [] of Int32) # => [2, 3]

# Broadcasting 1D array to 2D array
Narray.broadcast_shapes([2, 3], [3]) # => [2, 3]

# Broadcasting when one dimension is 1
Narray.broadcast_shapes([2, 1], [1, 3]) # => [2, 3]

# Incompatible shapes
Narray.broadcast_shapes([2, 3], [4, 5]) # => nil

See also: Narray.broadcast, Narray.can_broadcast?.

Source
can_broadcast?(from_shape : ::Array(Int32), to_shape : ::Array(Int32)) : Bool

Checks if a shape can be broadcast to another shape.

Returns true if the shapes are compatible for broadcasting and the result of broadcasting would match the target shape.

Narray.can_broadcast?([2, 1], [2, 3]) # => true
Narray.can_broadcast?([3], [2, 3])    # => true
Narray.can_broadcast?([2, 3], [4, 5]) # => false

See also: Narray.broadcast_shapes, Narray.broadcast.

Source
concatenate(arrays : ::Array(Array(T)), axis = 0) : Array(T) forall T

Concatenates arrays along the specified axis.

The arrays must have the same shape except for the dimension corresponding to axis.

# 1D arrays
a = Narray.array([3], [1, 2, 3])
b = Narray.array([3], [4, 5, 6])
c = Narray.concatenate([a, b])
c.shape # => [6]
c.data  # => [1, 2, 3, 4, 5, 6]

# 2D arrays along axis 0 (rows)
a = Narray.array([2, 3], [1, 2, 3, 4, 5, 6])
b = Narray.array([1, 3], [7, 8, 9])
c = Narray.concatenate([a, b])
c.shape # => [3, 3]
c.data  # => [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 2D arrays along axis 1 (columns)
a = Narray.array([2, 2], [1, 2, 3, 4])
b = Narray.array([2, 3], [5, 6, 7, 8, 9, 10])
c = Narray.concatenate([a, b], 1)
c.shape # => [2, 5]
c.data  # => [1, 2, 5, 6, 7, 3, 4, 8, 9, 10]

Raises ArgumentError if:

  • The array of arrays is empty
  • The axis is out of bounds
  • The arrays have different numbers of dimensions
  • The arrays have different shapes except for the concatenation axis

See also: Narray.vstack, Narray.hstack.

Source
det(a : Array(T)) : Float64 forall T

Computes the determinant of a square matrix.

The determinant is a scalar value that can be computed from the elements of a square matrix and encodes certain properties of the linear transformation described by the matrix.

For small matrices, direct formulas are used:

  • 1x1 matrix: the single element
  • 2x2 matrix: ad - bc for matrix [[a, b], [c, d]]
  • 3x3 matrix: uses the cofactor expansion formula

For larger matrices, Gaussian elimination with partial pivoting is used.

# 1x1 matrix
a = Narray.array([1, 1], [5])
Narray.det(a) # => 5.0

# 2x2 matrix
a = Narray.array([2, 2], [1, 2, 3, 4])
Narray.det(a) # => -2.0  # 1*4 - 2*3 = -2

# 3x3 matrix
a = Narray.array([3, 3], [1, 2, 3, 4, 5, 6, 7, 8, 9])
Narray.det(a) # => 0.0  # Singular matrix

# 4x4 diagonal matrix
a = Narray.array([4, 4], [
  1, 0, 0, 0,
  0, 2, 0, 0,
  0, 0, 3, 0,
  0, 0, 0, 4,
])
Narray.det(a) # => 24.0  # Product of diagonal elements

Raises ArgumentError if the matrix is not 2-dimensional or not square.

See also: Narray.determinant, Narray.inv.

Source
determinant(a : Array(T)) : Float64 forall T

Alias for determinant.

a = Narray.array([2, 2], [1, 2, 3, 4])
Narray.determinant(a) # => -2.0

See also: Narray.det.

Source
dot(a : Array(T), b : Array(U)) : Array(Float64) forall T, U

Computes the matrix multiplication (dot product) of two matrices.

For 2D arrays, this is the standard matrix multiplication. The inner dimensions must match: if a has shape [m, n] and b has shape [n, p], the result will have shape [m, p].

a = Narray.array([2, 3], [1, 2, 3, 4, 5, 6])
b = Narray.array([3, 2], [7, 8, 9, 10, 11, 12])
c = Narray.dot(a, b)
c.shape # => [2, 2]
c.data  # => [58, 64, 139, 154]

Raises ArgumentError if the arrays are not 2-dimensional or if the inner dimensions don't match.

See also: Narray.matmul.

Source
dot(a : Array(T), b : Array(T)) : Array(T) forall T

Original dot product for same type (for backward compatibility)

Source
eig(a : Array(T)) : Tuple(Array(Float64), Array(Float64)) forall T

Computes the eigenvalues and eigenvectors of a square matrix.

The eigenvalues and eigenvectors of a matrix A satisfy the equation A * v = λ * v, where v is an eigenvector and λ is the corresponding eigenvalue.

This method returns a tuple containing:

  1. An array of eigenvalues
  2. An array of eigenvectors (as columns of a matrix)

For small matrices, direct formulas are used:

  • 1x1 matrix: the eigenvalue is the single element, and the eigenvector is [1]
  • 2x2 matrix: uses the quadratic formula to find eigenvalues

For 3x3 symmetric matrices, a specialized approach is used to match NumPy results. For larger symmetric matrices, the QR algorithm is used.

# 1x1 matrix
a = Narray.array([1, 1], [5])
eigenvalues, eigenvectors = Narray.eig(a)
eigenvalues[[0]]     # => 5.0
eigenvectors[[0, 0]] # => 1.0

# 2x2 symmetric matrix
a = Narray.array([2, 2], [2, 1, 1, 2])
eigenvalues, eigenvectors = Narray.eig(a)
# Eigenvalues should be 1.0 and 3.0
# Eigenvectors are orthogonal and normalized

Raises ArgumentError if the matrix is not 2-dimensional, not square, or not symmetric. Raises ArgumentError if the matrix has complex eigenvalues (not supported).

See also: Narray.eigen.

Source
eigen(a : Array(T)) : Tuple(Array(Float64), Array(Float64)) forall T

Alias for eigenvalues and eigenvectors.

a = Narray.array([2, 2], [2, 1, 1, 2])
eigenvalues, eigenvectors = Narray.eigen(a)
# Same as Narray.eig(a)

See also: Narray.eig.

Source
hstack(arrays : ::Array(Array(T))) : Array(T) forall T

Stacks arrays horizontally (along the second axis).

For 1D arrays, this concatenates them along the first axis. For 2D arrays, this concatenates them along the second axis (columns). For higher dimensions, this is equivalent to concatenate(arrays, 1).

# 1D arrays
a = Narray.array([3], [1, 2, 3])
b = Narray.array([3], [4, 5, 6])
c = Narray.hstack([a, b])
c.shape # => [6]
c.data  # => [1, 2, 3, 4, 5, 6]

# 2D arrays
a = Narray.array([2, 2], [1, 2, 3, 4])
b = Narray.array([2, 3], [5, 6, 7, 8, 9, 10])
c = Narray.hstack([a, b])
c.shape # => [2, 5]
c.data  # => [1, 2, 5, 6, 7, 3, 4, 8, 9, 10]

See also: Narray.vstack, Narray.concatenate.

Source
inv(a : Array(T)) : Array(Float64) forall T

Computes the inverse of a square matrix.

The inverse of a matrix A is a matrix A^(-1) such that A * A^(-1) = I, where I is the identity matrix.

For small matrices, direct formulas are used:

  • 1x1 matrix: 1/a for matrix [a]
  • 2x2 matrix: [[d, -b], [-c, a]]/(ad-bc) for matrix [[a, b], [c, d]]

For larger matrices, Gaussian elimination with an augmented matrix [A|I] is used.

# 1x1 matrix
a = Narray.array([1, 1], [2])
inv_a = Narray.inv(a)
inv_a[[0, 0]] # => 0.5

# 2x2 matrix
a = Narray.array([2, 2], [4, 7, 2, 6])
inv_a = Narray.inv(a)
# Expected inverse of [[4, 7], [2, 6]] is [[0.6, -0.7], [-0.2, 0.4]]

Raises ArgumentError if the matrix is not 2-dimensional, not square, or singular.

See also: Narray.inverse, Narray.det.

Source
inverse(a : Array(T)) : Array(Float64) forall T

Alias for inverse.

a = Narray.array([2, 2], [1, 2, 3, 4])
Narray.inverse(a) # Same as Narray.inv(a)

See also: Narray.inv.

Source
linspace(start : Number, stop : Number, num : Int32, type : T.class) forall T

Creates a new array with evenly spaced values over a specified interval with the specified type.

arr = Narray.linspace(0, 1, 3, Float32)
arr.data[0].class # => Float32

See also: Narray.arange.

Source
linspace(start : Number, stop : Number, num = 50)

Creates a new array with evenly spaced values over a specified interval.

arr = Narray.linspace(0, 1, 5)
arr.shape # => [5]
arr.ndim  # => 1
arr.size  # => 5
arr.data  # => [0.0, 0.25, 0.5, 0.75, 1.0]

See also: Narray.arange.

Source
matmul(a : Array(T), b : Array(U)) : Array(Float64) forall T, U

Matrix multiplication (matmul)

Source
matmul(a : Array(T), b : Array(T)) : Array(T) forall T

Original matmul for same type (for backward compatibility)

Source
ones(shape : ::Array(Int32), type : T.class) forall T

Creates a new array filled with ones with the specified type.

arr = Narray.ones([2, 2], Int32)
arr.data          # => [1, 1, 1, 1]
arr.data[0].class # => Int32

See also: Narray.zeros, Narray.array.

Source
ones(shape : ::Array(Int32))

Creates a new array filled with ones.

arr = Narray.ones([2, 3])
arr.shape # => [2, 3]
arr.ndim  # => 2
arr.size  # => 6
arr.data  # => [1.0, 1.0, 1.0, 1.0, 1.0, 1.0]

See also: Narray.zeros, Narray.array.

Source
svd(a : Array(T)) : Tuple(Array(Float64), Array(Float64), Array(Float64)) forall T

Computes the singular value decomposition (SVD) of a matrix.

The SVD decomposes a matrix A into three matrices U, S, and V^T such that: A = U * S * V^T

Where:

  • U is an orthogonal matrix containing the left singular vectors
  • S is a diagonal matrix containing the singular values
  • V^T is the transpose of an orthogonal matrix containing the right singular vectors

This method returns a tuple containing (U, S, V^T).

For a matrix of shape (m, n):

  • U has shape (m, min(m, n))
  • S has shape (min(m, n))
  • V^T has shape (min(m, n), n)
# 2x2 matrix
a = Narray.array([2, 2], [1, 2, 3, 4])
u, s, vt = Narray.svd(a)

# Verify that U and V^T are orthogonal matrices
# (U * U^T and V^T * V should be close to identity matrices)

# Verify that A ≈ U * S * V^T
# Create diagonal matrix from singular values
s_diag = Narray.zeros([2, 2])
2.times do |i|
  s_diag[[i, i]] = s[[i]]
end
reconstructed = Narray.dot(Narray.dot(u, s_diag), vt)
# reconstructed should be close to the original matrix a

Raises ArgumentError if the matrix is not 2-dimensional.

Source
vstack(arrays : ::Array(Array(T))) : Array(T) forall T

Stacks arrays vertically (along the first axis).

For 1D arrays, this converts them to 2D arrays with one row each. For higher dimensions, this is equivalent to concatenate(arrays, 0).

# 1D arrays
a = Narray.array([3], [1, 2, 3])
b = Narray.array([3], [4, 5, 6])
c = Narray.vstack([a, b])
c.shape # => [2, 3]
c.data  # => [1, 2, 3, 4, 5, 6]

# 2D arrays
a = Narray.array([2, 3], [1, 2, 3, 4, 5, 6])
b = Narray.array([1, 3], [7, 8, 9])
c = Narray.vstack([a, b])
c.shape # => [3, 3]
c.data  # => [1, 2, 3, 4, 5, 6, 7, 8, 9]

See also: Narray.hstack, Narray.concatenate.

Source
zeros(shape : ::Array(Int32), type : T.class) forall T

Creates a new array filled with zeros with the specified type.

arr = Narray.zeros([2, 2], Int32)
arr.data          # => [0, 0, 0, 0]
arr.data[0].class # => Int32

See also: Narray.ones, Narray.array.

Source
zeros(shape : ::Array(Int32))

Creates a new array filled with zeros.

arr = Narray.zeros([2, 3])
arr.shape # => [2, 3]
arr.ndim  # => 2
arr.size  # => 6
arr.data  # => [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

See also: Narray.ones, Narray.array.

Source

Nested types