Skip to main content

Quickstart

Array vs Matrix: Understanding the Difference

One of Linear Algebra's key design features is the distinction between Arrays and Matrices:

Arrays: Element-wise Operations

Arrays are optimized for element-wise operations, similar to MATLAB's dot-operators or NumPy's default behavior:

var a = array([[1, 2], [3, 4]], as_real)
var b = array([[5, 6], [7, 8]], as_real)
var c = a * b // Element-wise multiplication
// c is [[5, 12], [21, 32]]
tip

Thinks of Arrays as multi-dimensional tables of numbers where operations are performed on each corresponding element.

Matrices: Linear Algebra Operations

Matrices follow linear algebra conventions, where * means matrix multiplication, and they support all standard linear algebra operations including matrix decompositions:

var A = matrix([[1, 2], [3, 4]], as_real)
var B = matrix([[5, 6], [7, 8]], as_real)
var C = A * B // Matrix multiplication
// C is [[19, 22], [43, 50]]

// Matrices also support decompositions
var qr_result = qr(A) // QR decomposition
var svd_result = svd(A) // Singular Value Decomposition
var eig_result = eigen(A) // Eigenvalue decomposition
tip

Thinks of Matrices as mathematical objects.

Talking Between Arrays and Matrices

This distinction between Arrays and Matrices makes code intent clear and prevents common errors. When there is a need to perform element-wise operations on a matrix and vice versa, you can easily convert between the two when needed:

var m = matrix([[1, 2], [3, 4]])
var a = array(m) // Convert to array for element-wise ops
var result = a * a // Element-wise square
var back_to_matrix = matrix(result) // Convert back

Type System

Linear Algebra provides explicit type control through type specifiers, allowing you to choose the appropriate numeric type for your computations:

  • as_bool - Boolean type
  • as_integer - Integer type
  • as_real - Real (double precision floating point)
  • as_complex - Complex (complex double precision)

You can also specify container types:

  • as_array - Array container (element-wise operations)
  • as_matrix - Matrix container (linear algebra operations)

These can be combined:

var a = zeros([3, 4], as_array | as_real)           // Real array
var m = identity([3, 3], as_matrix | as_complex) // Complex matrix
var data = array([[1, 2], [3, 4]], as_integer) // Integer array

Factory functions default to as_matrix and as_complex except where it does not make sense. For instance, matrix([[1]], as_array) is forbidden.

Object-Oriented Design

Linear Algebra fully embraces an object-oriented approach. Arrays and matrices are classes with numerous methods and attributes. This design allows for flexible programming styles:

// Method-based style
var v = uninitialized([5, 1], as_matrix | as_real)
v.set_zero()
v.linspace(0, 4)
var mean_val = v.mean()

// Factory function style
var v2 = linspaced(5, 0, 4)
var mean_val2 = mean(v2)

This flexibility allows you to code in whichever paradigm feels most natural for your specific use case.

Creating Arrays and Matrices

Linear Algebra offers multiple convenient ways to create arrays and matrices:

From Literals

var a = array([[1, 2, 3], [4, 5, 6]])           // 3x2 array: two columns
var m = matrix([[1.0, 2.0], [3.0, 4.0]], as_real) // 2x2 real matrix
var v = vector([1, 2, 3, 4]) // 4x1 column vector
warning

Aleph's nested list structure differs from NumPy in how dimensions are created. In Aleph, each level of nesting adds a dimension, and inner lists run along the first dimension (columns). For example, [[1, 2, 3], [4, 5, 6]] creates a 3×2 array where [1, 2, 3] is the first column and [4, 5, 6] is the second column. This is opposite to NumPy, where [[1, 2, 3], [4, 5, 6]] would create a 2×3 array with [1, 2, 3] as the first row. Think of Aleph's nested lists as stacking columns side-by-side rather than stacking rows vertically.

Factory Functions

var z = zeros([3, 4], as_real)        // 3x4 matrix of zeros
var o = ones([2, 5], as_complex) // 2x5 matrix of ones
var f = filled([2, 3], 5.0, as_real) // 2x3 matrix filled with 5.0
var I = identity([3, 3], as_matrix) // 3x3 identity matrix
var r = random([2, 3]) // 2x3 random matrix (default: complex matrix)

Factory functions have corresponding setter methods that modify arrays in-place. For example, zeros() corresponds to set_zero(), ones() to set_ones(), random() to set_random(), and filled() to set_constant() or fill():

var m = uninitialized([3, 3], as_matrix | as_real)
m.set_zero() // Initialize to zeros
m.set_ones() // Change to ones
m.set_random() // Fill with random values
m.set_constant(5.0) // Set all elements to 5.0
m.fill(7.0) // Alternative to set_constant
m.set_identity() // Make it an identity matrix

Ranges and Sequences

linspaced() creates linearly spaced values over a specified interval:

var v = linspaced(10, 0.0, 1.0)  // 10 evenly spaced values from 0 to 1
warning

The linspaced() function returns an Matrix by default. To get an Array, one must pass the result to the array() constructor.

Uninitialized Arrays

For performance-critical code where you'll immediately overwrite values:

var a = uninitialized([1000, 1000], as_array | as_real); // semicolon suppresses output
// ... fill with computed values

From Arrays

Create new arrays or matrices with the same shape and type as an existing one:

var original = random([3, 4], as_array | as_real)

var z = zeros_like(original) // Same shape and type, filled with zeros
var o = ones_like(original) // Same shape and type, filled with ones
var f = filled_like(original, 5.0) // Same shape and type, filled with 5.0
var r = random_like(original) // Same shape and type, random values

These functions are convenient when you need to create arrays that match the structure of existing data without manually specifying the shape and type.

Basic Operations

Indexing and Selection

Linear Algebra provides flexible ways to access individual elements and submatrices:

The [] operator is used for accessing and modifying elements:

var m = matrix([[1, 2, 3], [4, 5, 6]])
var val = m[2, 1] // Access row 2, column 1 (returns reference)
m[2, 1] = 10 // Modify element directly

var v = vector([1, 2, 3, 4])
var elem = v[2] // Access element 2 (returns reference)
v[2] = 5 // Modify element directly
info

Single index access (e.g., v[2]) only works for 1D containers (vectors with a singleton dimension). For 2D arrays or matrices, you must always use two indices (e.g., m[i, j]), otherwise an error will be thrown.

The [] operator also supports slice notation and fancy indexing:

var m = matrix([[1,2,3,4], [5,6,7,8], [9,10,11,12]])

// Slice notation with colon syntax
var sub1 = m[0:2, 1:3] // Rows 0-1, columns 1-2
var sub2 = m[:, -1:] // All rows, last column

// Fancy indexing with lists
var sub3 = m[[2, 0, 1], [2, 1]] // Select rows 2,0,1 and columns 2,1

Other Access Methods

Beyond the [] operator, additional methods provide more flexibility:

var m = matrix([[1,2,3,4], [5,6,7,8], [9,10,11,12]])

// Block operations - extract rectangular regions
var b = m.block(1, 1, 2, 2) // 2x2 block starting at (1,1)
var r = m.row(1) // Extract single row
var c = m.col(2) // Extract single column

// Mixed indexing with select() - combine indices, slices, and lists
var s1 = m.select([0, Slice()]) // First row, all columns
var s2 = m.select([Slice(0, 2), [2, 0]]) // Rows 0-1, columns 2 and 0
info

Besides single element access, all these methods return copies of the data. To modify the original matrix in-place, use set_block() or set_select().

Arithmetic Operations

All standard arithmetic operations are supported, both element-wise (arrays) and following linear algebra rules (matrices):

var a = array([[1, 2], [3, 4]], as_real)
var b = array([[5, 6], [7, 8]], as_real)

var sum = a + b // Element-wise addition
var diff = a - b // Element-wise subtraction
var prod = a * b // Element-wise multiplication
var quot = a / b // Element-wise division

// In-place operations
a += b
a *= 2.0

Mathematical Functions

The Linear Algebra modules provides a comprehensive set of mathematical functions for arrays:

var a = array(linspaced(5, 0.0, pi))

var s = sin(a)
var c = cos(a)
var e = exp(a)
var l = log(a)
var sq = sqrt(a)
var ab = abs(a)

Most operators and functions available in the Math module are also available as methods for arrays. This enables vectorizing the evaluation of mathematical expressions, allowing for seamless element-wise computations.

warning

These functions operate element-wise on arrays, and as matrix functions on matrices. They will throw an error if applied to matrices where the operation is not defined (e.g. if the matrix is rectangular).

These functions work seamlessly with complex numbers as well:

var z = array([[1 + 2i, 3 + 4i]])
var mag = abs(z) // Magnitude
var phase = phase(z) // Phase angle
var conj_z = conjugated(z) // Complex conjugate

Reduction Operations

Linear Algebra provides common reduction functions:

var a = array([[1, 2, 3], [4, 5, 6]], as_real)

var s = a.sum() // Sum of all elements
var m = a.mean() // Mean of all elements
var p = a.product() // Product of all elements
var mx = a.max() // Maximum element
var mn = a.min() // Minimum element

Matrix Multiplication

For matrices, the * operator performs standard matrix multiplication:

var A = matrix([[1, 2], [3, 4]], as_real)
var B = matrix([[5, 6], [7, 8]], as_real)
var C = A * B // Matrix multiplication

Transpose and Adjoint

Matrices can be transposed or adjointed (conjugate transpose). Note the important distinction between in-place and copy operations:

var A = matrix([[1, 2, 3], [4, 5, 6]])

// Past tense: returns a transposed copy, A remains unchanged
var At = transposed(A)
var Ah = adjointed(A) // Conjugate transpose (Hermitian)

// Present tense: transposes in-place and returns a reference
var B = matrix([[1, 2, 3], [4, 5, 6]])
var Bt = transpose(B) // B is now transposed, Bt references the transposed B
var Bh = adjoint(B) // B is now adjointed (conjugate transposed)
warning

Functions with active tense verbs like transpose() and adjoint() act in-place and return a reference to the modified matrix. To get a copy without modifying the original, use the past tense: transposed() and adjointed(). This pattern applies to many other operations in Linear Algebra.

Matrix Properties

Common matrix properties like determinant, trace, and norm are readily available:

var A = matrix([[1, 2], [3, 4]])
var d = determinant(A) // Determinant
var t = trace(A) // Trace
var n = norm(A) // Frobenius norm

Matrix Inversion and Solving

Linear systems can be solved directly, or you can compute the matrix inverse:

var A = matrix([[4, 2], [1, 3]], as_real)
var Ainv = inversed(A) // Returns inverse copy, A unchanged

var b = vector([8, 5])
var x = solve(A, b) // Solve Ax = b
info

Like transpose()/transposed(), the function inverse() modifies the matrix in-place and returns a reference, while inversed() returns a copy without modifying the original.

Matrix Decompositions

For more advanced numerical linear algebra, several matrix decompositions are available. Decomposition objects that encode the matrix factors and can be used to probe information such as the rank of the matrix, eigenvalues, and compress large matrices.

var m = matrix([[1,2],[3,4]],as_real)
var n = random([2,2],as_real | as_matrix)
var o = matrix([[-1+1i,2.2-1i],[2.0,3i]])

var qr = HouseholderQR(n)
var es = EigenSolver(m)
var svd = BDCSVD(o)