Esc
Start typing to search...

Matrices

The Matrix module provides two-dimensional matrix support for linear algebra, statistical computing, and numerical data processing. Matrices in Keel are homogeneous — each matrix holds values of one element type.

Element Types

Keel supports four matrix element types:

TypeDescription
Matrix FloatDense floating-point matrix (default for most constructors)
Matrix IntInteger matrix; use fromRowsInt, zerosInt, etc.
Matrix DecimalArbitrary-precision decimal matrix
Matrix (Maybe Float)Sparse or nullable Float matrix

The compiler rejects invalid element types at compile time. Matrix String or Matrix Bool are type errors.

Creating Matrices

import Matrix

let a = Matrix.fromRows [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
Matrix.shape a   -- (2, 3)
Try it
import Matrix

-- Integer matrix
let b = Matrix.fromRowsInt [[1, 2], [3, 4]]

-- Identity matrix
let eye = Matrix.identity 2

-- From a flat list (column vector)
let v = Matrix.fromList [1.0, 2.0, 3.0]
Matrix.shape v   -- (3, 1)
Try it

Shape and Access

import Matrix

let m = Matrix.fromRows [[10.0, 20.0], [30.0, 40.0]]

Matrix.shape m        -- (2, 2)
Matrix.nrows m        -- 2
Matrix.ncols m        -- 2

Matrix.get 0 1 m      -- Just 20.0  (row 0, col 1)
Matrix.get 5 0 m      -- Nothing    (out of bounds)

Matrix.toRows m       -- [[10.0, 20.0], [30.0, 40.0]]
Matrix.toCols m       -- [[10.0, 30.0], [20.0, 40.0]]
Matrix.toList m       -- Ok [10.0, 20.0, 30.0, 40.0]
Try it

Operators

Matrix operators follow standard mathematical notation:

OperatorMeaningResult type
+Element-wise additionResult (Matrix T) String
-Element-wise subtractionResult (Matrix T) String
*Matrix multiplicationResult (Matrix Float) String
.*Hadamard (element-wise) productMatrix T
./Element-wise divisionMatrix Float

Dimension-mismatched operations return Err at runtime.

import Matrix

let a = Matrix.fromRows [[1.0, 2.0], [3.0, 4.0]]
let b = Matrix.fromRows [[5.0, 6.0], [7.0, 8.0]]

-- Matrix multiplication
case a * b of
    Ok result -> Matrix.toRows result
    Err msg   -> [[]]

-- Element-wise multiply (Hadamard product)
Matrix.toRows (a .* b)   -- [[5.0, 12.0], [21.0, 32.0]]

-- Scale: multiply every element by a scalar
Matrix.scale 2.0 a |> Matrix.toRows   -- [[2.0, 4.0], [6.0, 8.0]]

-- Element-wise map
Matrix.map (\x -> x * x) a |> Matrix.toRows   -- [[1.0, 4.0], [9.0, 16.0]]
Try it

Decompositions

Decompositions operate on Matrix Float only. They return Result values so dimension and singularity errors are handled in keel code:

import Matrix

let m = Matrix.fromRows [[4.0, 3.0], [6.0, 3.0]]

-- Inverse
case Matrix.inv m of
    Ok mi -> Matrix.toRows mi
    Err e -> [[]]

-- Determinant
case Matrix.det m of
    Ok d -> d
    Err e -> 0.0

-- Frobenius norm (infallible)
Matrix.norm m   -- sqrt(sum of squared elements)

-- SVD: (U, singular values, V^T)
let (u, s, vt) = Matrix.svd m

-- QR decomposition: (Q, R)
let (q, r) = Matrix.qr m

-- Cholesky: requires a positive-definite matrix
let spd = Matrix.fromRows [[4.0, 2.0], [2.0, 3.0]]
case Matrix.chol spd of
    Ok l -> Matrix.toRows l
    Err e -> [[]]
Try it

Nullable Matrices

Use Matrix (Maybe Float) for sparse data or when some values may be absent:

import Matrix

let sparse = Matrix.fromRowsMaybe
    [ [Just 1.0, Nothing, Just 3.0]
    , [Nothing,  Just 5.0, Nothing]
    ]

-- Fill missing values with a default, then check shape
let filled = Matrix.fromRowsMaybeWith 0.0
    [ [Just 1.0, Nothing, Just 3.0]
    , [Nothing,  Just 5.0, Nothing]
    ]
Matrix.shape filled   -- (2, 3)
Try it

DataFrame Interop

Matrices and DataFrames can be converted in both directions:

import Matrix
import DataFrame

let df = DataFrame.fromRecords [{ x = 1.0, y = 2.0 }, { x = 3.0, y = 4.0 }]

-- DataFrame columns → Matrix Float (fails on nulls)
case Matrix.fromDataFrame df of
    Ok m  -> Matrix.shape m   -- (2, 2)
    Err e -> (0, 0)
Try it

Solving Linear Systems

Matrix.solve a b solves Ax = b for x (uses LU decomposition):

import Matrix

-- Solve: 2x = 6, 4y = 8
let a = Matrix.fromRows [[2.0, 0.0], [0.0, 4.0]]
let b = Matrix.fromList [6.0, 8.0]

case Matrix.solve a b of
    Ok x -> Matrix.toList x   -- Ok [3.0, 2.0]
    Err e -> Err e
Try it

Float-only operations

Several operations require Matrix Float because they rely on floating-point arithmetic internally. Passing any other element type is a compile error — the type checker enforces this before the program runs.

OperationWhy Float only
Matrix.invLU decomposition requires floating-point pivoting
Matrix.detComputed via LU; result is always a real number
Matrix.solveLU-based linear solve; produces a float solution vector
Matrix.svdSingular value decomposition — iterative float algorithm
Matrix.svdTruncatedTruncated SVD variant of the same
Matrix.qrQR factorisation via Householder reflections
Matrix.cholCholesky factorisation; requires positive-definite float input
Matrix.normFrobenius norm — sum of squares, then square root
Matrix.traceSum of diagonal; result type is Float
Matrix.rankRank via SVD — requires float computation
Matrix.toDataFrameProduces Float columns; only meaningful for float data
Matrix.fromDataFrameAlways produces Matrix Float

The ./ operator also always produces Matrix Float, even when both inputs are Matrix Int — division cannot stay exact for integers.

To use any Float-only operation on integer data, convert first with Matrix.toFloat:

import Matrix

let ints = Matrix.fromRowsInt [[1, 2], [3, 4]]
let floats = Matrix.toFloat ints      -- Matrix Float
Matrix.norm floats                    -- ok: Frobenius norm
Matrix.inv floats                     -- ok: matrix inverse
Try it

Matrix Decimal has no conversion path to Matrix Float — Decimal is arbitrary-precision and cannot be silently narrowed. If you need decompositions on decimal data, round or truncate to Float explicitly before constructing the matrix.

Type Safety

The Keel compiler enforces element-type restrictions at compile time:

  • Matrix String is a compile error — only numeric element types are permitted
  • Matrix (Maybe String) is a compile error — Maybe must wrap a numeric type
  • Matrix Int |> Matrix.norm is a compile errornorm requires Matrix Float; use Matrix.toFloat first
  • Matrix Float == Matrix Float is a compile error — use Matrix.toRows and compare element-wise with a tolerance