Pular para o conteúdo

Stdlib · extensões

linalg

álgebra linear (gráficos + densa + base de IA)

extensions.md · 104 linhas · 3 min de leitura

use linalg

Não é “linalg de gamedev”; é a biblioteca matemática que sustenta de gráficos a IA. São três camadas: tipos fixos (gráficos), Matrix[M,N]/dinâmica (densa geral), e álgebra densa estilo BLAS (multiplicação, decomposições, solve). Backend duplo (espelha o multi-engine do regex): a implementação própria portável é o piso, e o BLAS/LAPACK do sistema é o acelerador opt-in (via o FFI-C que já temos). Tier 1. Tensores de rank arbitrário e autograd/NN ficam para a lib de IA (tier 2/3) por cima; aqui é a fundação numérica.

Tamanho conhecido em compilação; as operações são por valor, sem alocar:

decl Vec2 { pub x: f32; pub y: f32 }
decl Vec3 { pub x: f32; pub y: f32; pub z: f32 }
decl Vec4 { pub x: f32; pub y: f32; pub z: f32; pub w: f32 }
decl Mat2 { ... } decl Mat3 { ... } decl Mat4 { ... }
decl Quat { pub x: f32; pub y: f32; pub z: f32; pub w: f32 } // quaternion
fn (a: Vec3) dot(b: Vec3) -> f32 // produto escalar (+ via operador onde numérico)
fn (a: Vec3) cross(b: Vec3) -> Vec3 // produto vetorial
fn (a: Vec3) length() -> f32
fn (a: Vec3) normalized() -> Vec3
fn (a: Vec3) add(b: Vec3) -> Vec3 // + também por operador (numérico)
fn (m: Mat4) mul(n: Mat4) -> Mat4
fn (m: Mat4) mul_vec(v: Vec4) -> Vec4
fn (m: Mat4) inverse() -> Optional[Mat4] // none se singular
fn (m: Mat4) transpose() -> Mat4
fn (q: Quat) mul(r: Quat) -> Quat
fn (q: Quat) to_mat4() -> Mat4
// transformações comuns (construtores):
fn Mat4.identity() -> Mat4
fn Mat4.translation(v: Vec3) -> Mat4
fn Mat4.rotation(axis: Vec3, angle: f32) -> Mat4
fn Mat4.scale(v: Vec3) -> Mat4
fn Mat4.perspective(fovy: f32, aspect: f32, near: f32, far: f32) -> Mat4
fn Mat4.look_at(eye: Vec3, center: Vec3, up: Vec3) -> Mat4

f32 genérico onde fizer sentido: Vec3[T] sobre qualquer float (f32/f64); os nomes nus (Vec3) são o alias de f32 (o caso gráfico). Na prática, alias Vec3 = Vec3f.

Dimensões em runtime; aloca pelo @mm do contexto. É a base numérica geral, e o que a IA puxa:

decl Matrix[T] { pub rows: usize; pub cols: usize; ... } // T = f32/f64
fn (Matrix[T]) zeros(rows: usize, cols: usize) -> Matrix[T]
fn (Matrix[T]) from(rows: usize, cols: usize, data: []T) -> Matrix[T] // row-major
fn (Matrix[T]) identity(n: usize) -> Matrix[T]
fn (m: Matrix[T]) get(i: usize, j: usize) -> Optional[T] // SAFE (none fora dos limites)
@requires(i < m.rows && j < m.cols)
fn (m: Matrix[T]) at(i: usize, j: usize) -> T // contratado (sem Optional)
fn (m: *Matrix[T]) set(i: usize, j: usize, v: T) -> error{OutOfBounds}
fn (m: Matrix[T]) mul(n: Matrix[T]) -> Result[Matrix[T], error{ShapeMismatch}] // produto matricial
fn (m: Matrix[T]) add(n: Matrix[T]) -> Result[Matrix[T], error{ShapeMismatch}]
fn (m: Matrix[T]) transpose() -> Matrix[T]
fn (m: Matrix[T]) slice(r0: usize, r1: usize, c0: usize, c1: usize) -> Matrix[T] // submatriz (view)
fn (m: Matrix[T]) row(i: usize) -> []T // view
fn (m: Matrix[T]) col(j: usize) -> List[T]

Acesso é at/get (sem overload de []), igual collections/containers: segura por fora, com o índice cru contido.

As decomposições e o solve, o coração da densa. Própria portável por default; BLAS/LAPACK opt-in quando linkado (o compilador escolhe o backend em comptime conforme linalg é configurado, como arch.has_feature escolhe caminho SIMD):

fn (m: Matrix[T]) lu() -> Result[(l: Matrix[T], u: Matrix[T], p: Permutation), error{Singular}]
fn (m: Matrix[T]) qr() -> (q: Matrix[T], r: Matrix[T])
fn (m: Matrix[T]) svd() -> (u: Matrix[T], s: List[T], vt: Matrix[T])
fn (m: Matrix[T]) cholesky() -> Result[Matrix[T], error{NotPositiveDefinite}]
fn (m: Matrix[T]) solve(b: Matrix[T]) -> Result[Matrix[T], error{Singular, ShapeMismatch}] // resolve Mx=b
fn (m: Matrix[T]) det() -> T
fn (m: Matrix[T]) inverse() -> Result[Matrix[T], error{Singular}]
fn (m: Matrix[T]) eig() -> Result[(values: List[T], vectors: Matrix[T]), error{NoConverge}]

A implementação própria é o piso (portável, sempre presente); o binding BLAS/LAPACK (via FFI-C, §17) é o acelerador, com a mesma API e backend trocável. Habilitar BLAS é declarar a dep C (cdeps, §17) + flag de build; sem isso, roda a própria.

  • Backend duplo (própria + BLAS opt-in): espelha o multi-engine do regex: portável é o piso (zero dependência), BLAS é o acelerador quando você o linka. Mesma API; o comptime escolhe.
  • Três camadas, um pacote: fixos (gráficos, zero-alloc, valor), Matrix[T] (densa geral), decomposições (densa séria). Crescem juntos porque compartilham os tipos.
  • Tensores (rank > 2) e autograd/NN são a lib de IA (tier 2/3): linalg é a fundação; broadcasting e Tensor[rank] entram na camada de cima, não aqui. Peça grande, deliberadamente fora.
  • at/get, sem overload de []: consistente com collections/containers; colorless (aloca pelo contexto).