Salta al contingut
Ridgun
Català

14 El Llibre de Ridgun

La lib linalg — Àlgebra Lineal sobre Tensor[Real], Bareiss i Activacions

14.1. Per què existeix la lib linalg? (El problema real i la filosofia Ridgun)

«Per què necessito una lib linalg escrita des de zero en Ridgun pur en lloc d'enllaçar una llibreria de C de tota la vida?»

Aquesta és la primera pregunta que es fa qualsevol enginyer de sistemes en arribar a linalg. I la resposta resumeix tota la filosofia de ridgun-libs:

Siguem sincers: quan vols calcular similitud cosinus per a embeddings, resoldre $A x = b$ o avaluar una capa softmax en un binari lleuger, no vols arrossegar 400 MB de PyTorch o LAPACK.

La lib linalg construeix àlgebra lineal numèrica sobre Tensor[Real] natiu: transposició, traça, identitat, producte intern/extern, multiplicació matriu-vector i matriu-matriu amb notació d'Einstein, normes ($L_1, L_2, L_\infty$, Frobenius), determinant i resolució lineal $A x = b$ mitjançant eliminació de Bareiss/Gauss amb pivotatge parcial, i activacions neuronals (relu, sigmoid, softmax).

En un ecosistema tradicional, resoldre les tasques del domini LINALG implica arrossegar dependències externes en C, lluitar amb punters crus, conversions opaques de tipus, excepcions no verificades o pauses impredictibles del recol·lector de brossa. En Ridgun, la lib linalg (src/main/ridgun/lib/linalg.rg) aborda el problema amb quatre principis innegociables:

  1. Declara import linalg (o import linalg as alias) a l'inici del fitxer: cada lib defineix el seu espai de noms module :: linalg i en mode --strict les funcions multi-paraula s'invoquen sempre amb espais naturals.
  2. Encadena les teves canonades d'esquerra a dreta amb ->: escriu entrada pas u -> pas dos -> resultat, fes servir màscares (+ _ _ +) per desestructurar seqüències i omet _ = en sentències seqüencials (o descarta amb -> ()).
  3. Recursió de cua amb TCO (Tail-Call Optimization): tots els recorreguts interns de linalg es compilen a bucles natius en espai de pila $O(1)$.
  4. Memòria determinista (RMM1 / RMM2): cada estructura intermèdia s'allibera en l'instant exacte en què acaba la seva última lectura, sense Garbage Collector.

14.2. Artefactes natius, importació i catàleg complet de l'API de linalg

A ridgun-libs, linalg és una lib de primera classe situada a src/main/ridgun/lib/linalg.rg (domini LINALG). En compilar amb stable bundle, s'empaqueta en tres formats natius:

ArtefacteRutaPropòsit
Bundle Ridguntarget/bundles/linalg.rglibDistribució i importació directa amb import linalg
Fitxer compilattarget/linalg.rgcArtefacte intermedi verificat per ridgunc
Objecte compartit ELF64target/liblinalg.soBiblioteca dinàmica nativa sense dependències de C ni libc

Cada lib declara el seu propi espai de noms amb module :: linalg: n'hi ha prou d'escriure import linalg (o import linalg as alias) a la capçalera i cridar les seves funcions amb espais naturals (la meva funcio x) i canonades -> (x la meva funcio -> resultat).

14.2.1. Resum de famílies funcionals de linalg

Signatura / Funció / TipusQuè fa i quan fer-la servir
linalg vec from list / linalg mat from listsConstrueix Tensor[Real] 1D i 2D a partir de llistes de Ridgun
linalg transpose / linalg trace / linalg diag / linalg identityTransformacions estructurals de matrius i matriu identitat
linalg dot / linalg outer / linalg matvec / linalg matmulEinstein contraction (a[i] * b[i], a[i, k] * b[k, j])
linalg norm l1 / linalg norm l2 / linalg cosine similarityNormes vectorials/matricials, normalització i similitud cosinus
linalg det / linalg solveDeterminant exacte de Bareiss i resolució $A x = b$ amb pivotatge parcial (Option[Tensor[Real]])
linalg relu / linalg sigmoid / linalg softmaxActivacions neuronals numèricament estables amb desplaçament pel màxim

14.2.2. Catàleg exhaustiu de tipus i signatures a src/main/ridgun/lib/linalg.rg

A continuació tens l'inventari complet de tipus algebraics (type), operadors (op ::) i signatures de funcions (fn ::) definits a src/main/ridgun/lib/linalg.rg (0 tipus, 0 operadors i 76 funcions):

#Funció / Signatura (fn ::)Tipus de Retorn
1fn :: la abs IntInt
2fn :: la max Int IntInt
3fn :: la isqrt step Int Int IntInt
4fn :: la isqrt loop Int IntInt
5fn :: la isqrt IntInt
6fn :: la exp neg scaled IntInt
7fn :: linalg vec len Tensor[Real * n]Int
8fn :: linalg mat rows Tensor[Real * r * c]Int
9fn :: linalg mat cols Tensor[Real * r * c]Int
10fn :: la transpose col loop Tensor[Real * r * c] Tensor[Real * c * r] Int Int IntTensor[Real * c * r]
11fn :: la transpose row loop Tensor[Real * r * c] Tensor[Real * c * r] Int Int IntTensor[Real * c * r]
12fn :: linalg transpose Tensor[Real * r * c]Tensor[Real * c * r]
13fn :: la trace loop Tensor[Real * r * c] Int Int IntInt
14fn :: linalg trace Tensor[Real * r * c]Int
15fn :: la diag loop Tensor[Real * r * c] Tensor[Real * r] Int IntTensor[Real * r]
16fn :: linalg diag Tensor[Real * r * c]Tensor[Real * r]
17fn :: la identity loop Tensor[Real * n * n] Int IntTensor[Real * n * n]
18fn :: linalg identity IntTensor[Real * n * n]
19fn :: la dot loop Tensor[Real * n] Tensor[Real * n] Int Int IntInt
20fn :: linalg dot Tensor[Real * n] Tensor[Real * n]Int
21fn :: la outer col loop Tensor[Real * r] Tensor[Real * c] Tensor[Real * r * c] Int Int IntTensor[Real * r * c]
22fn :: la outer row loop Tensor[Real * r] Tensor[Real * c] Tensor[Real * r * c] Int Int IntTensor[Real * r * c]
23fn :: linalg outer Tensor[Real * r] Tensor[Real * c]Tensor[Real * r * c]
24fn :: la matvec row dot Tensor[Real * r * c] Tensor[Real * c] Int Int Int IntInt
25fn :: la matvec loop Tensor[Real * r * c] Tensor[Real * c] Tensor[Real * r] Int Int IntTensor[Real * r]
26fn :: linalg matvec Tensor[Real * r * c] Tensor[Real * c]Tensor[Real * r]
27fn :: linalg matmul Tensor[Real * r * k] Tensor[Real * k * c]Tensor[Real * r * c]
28fn :: la norm l1 loop Tensor[Real * n] Int Int IntInt
29fn :: linalg norm l1 Tensor[Real * n]Int
30fn :: linalg norm l2 sq Tensor[Real * n]Int
31fn :: linalg norm l2 Tensor[Real * n]Int
32fn :: la norm linf loop Tensor[Real * n] Int Int IntInt
33fn :: linalg norm linf Tensor[Real * n]Int
34fn :: la frob col loop Tensor[Real * r * c] Int Int Int IntInt
35fn :: la frob row loop Tensor[Real * r * c] Int Int Int IntInt
36fn :: linalg frobenius norm Tensor[Real * r * c]Int
37fn :: la norm vec loop Tensor[Real * n] Tensor[Real * n] Int Int IntTensor[Real * n]
38fn :: la normalize vec step Tensor[Real * n] Tensor[Real * n] Int IntTensor[Real * n]
39fn :: linalg normalize vec Tensor[Real * n]Tensor[Real * n]
40fn :: la cosine step Tensor[Real * n] Tensor[Real * n] Int IntInt
41fn :: linalg cosine similarity Tensor[Real * n] Tensor[Real * n]Int
42fn :: la clone mat col Tensor[Real * r * c] Tensor[Real * n * n] Int Int IntTensor[Real * n * n]
43fn :: la clone mat row Tensor[Real * r * c] Tensor[Real * n * n] Int IntTensor[Real * n * n]
44fn :: la clone vec loop Tensor[Real * n] Tensor[Real * n] Int IntTensor[Real * n]
45fn :: la find pivot step Tensor[Real * n * n] Int Int Int Int Int IntInt
46fn :: la find pivot row Tensor[Real * n * n] Int Int Int Int IntInt
47fn :: la swap mat rows loop Tensor[Real * n * n] Int Int Int IntTensor[Real * n * n]
48fn :: la swap vec elems Tensor[Real * n] Int IntTensor[Real * n]
49fn :: la ff elim b loop Tensor[Real * n * n] Tensor[Real * n] Int Int Int Int IntTensor[Real * n]
50fn :: la ff elim col loop Tensor[Real * n * n] Int Int Int Int Int Int IntTensor[Real * n * n]
51fn :: la ff elim a loop Tensor[Real * n * n] Int Int Int Int IntTensor[Real * n * n]
52fn :: la next piv Int IntInt
53fn :: la solve loop Tensor[Real * n * n] Tensor[Real * n] Int Int IntTensor[Real * n]
54fn :: la backsub sum Tensor[Real * n * n] Tensor[Real * n] Int Int Int IntInt
55fn :: la backsub div Int Int IntInt
56fn :: la backsub loop Tensor[Real * n * n] Tensor[Real * n] Tensor[Real * n] Int IntTensor[Real * n]
57fn :: linalg solve Tensor[Real * n * c] Tensor[Real * n]Tensor[Real * n]
58fn :: la bareiss step loop Tensor[Real * n * n] Int Int Int Int Int IntTensor[Real * n * n]
59fn :: la bareiss row loop Tensor[Real * n * n] Int Int Int Int IntTensor[Real * n * n]
60fn :: la pivot sign Int Int IntInt
61fn :: la bareiss piv step Tensor[Real * n * n] Int Int Int Int IntInt
62fn :: la bareiss loop Tensor[Real * n * n] Int Int Int IntInt
63fn :: linalg det Tensor[Real * n * c]Int
64fn :: la relu scalar IntInt
65fn :: la relu loop Tensor[Real * n] Tensor[Real * n] Int IntTensor[Real * n]
66fn :: linalg relu Tensor[Real * n]Tensor[Real * n]
67fn :: la sigmoid scalar IntInt
68fn :: la sigmoid loop Tensor[Real * n] Tensor[Real * n] Int IntTensor[Real * n]
69fn :: linalg sigmoid Tensor[Real * n]Tensor[Real * n]
70fn :: la max vec loop Tensor[Real * n] Int Int IntInt
71fn :: la sum vec loop Tensor[Real * n] Int Int IntInt
72fn :: la exp shifted loop Tensor[Real * n] Tensor[Real * n] Int Int IntTensor[Real * n]
73fn :: la softmax scale loop Tensor[Real * n] Tensor[Real * n] Int Int IntTensor[Real * n]
74fn :: la argmax vec loop Tensor[Real * n] Int Int Int IntInt
75fn :: la softmax finish Tensor[Real * n] Tensor[Real * n] Int IntTensor[Real * n]
76fn :: linalg softmax Tensor[Real * n]Tensor[Real * n]

14.3. Explorant linalg a fons: Exemples pas a pas de cada funcionalitat

No n'hi ha prou de veure la taula de signatures: explorarem cadascuna de les funcionalitats de la lib linalg mitjançant exemples progressius i executables en Ridgun. Començarem per les dues receptes tabulars d'ús quotidià i després recorrerem en detall tots els escenaris funcionals, transformacions i casos límit verificats a src/test/ridgun/test linalg.rg.

14.3.1. Exemple 1: Flux principal en estil tabular amb linalg

Observa com en Ridgun els signes = i els passos de transformació amb linalg queden alineats com una taula neta, sense variables mutables innecessàries ni efectes laterals ocults:

import linalg

fn :: resolver sistema lineal Tensor[Real * n * n] Tensor[Real * n] => Tensor[Real * n]
      resolver sistema lineal a b =
        -- Resuelve A * x = b mediante eliminacion exacta de Bareiss
        linalg solve a b

14.3.2. Exemple 2: Composició segura i gestió de casos de vora

Tota funció de la lib linalg està dissenyada per compondre's amb guardes booleanes | i amb la resta de les 32 libs de ridgun-libs:

import linalg

fn :: puntuar embeddings Tensor[Real * n] Tensor[Real * n] => Int Tensor[Real * n]
      puntuar embeddings q d =
        linalg cosine similarity q d -> sim
        q linalg softmax -> probs
        (sim, probs)

14.3.3. Exemple 3: Transposada (transpose), traça (trace), extracció/construcció diagonal (diag, from diag) i matriu identitat (identity)

En aquest exemple pràctic explorem de manera exhaustiva com es comporta linalg en aquest bloc funcional, incloent-hi entrades estàndard, combinacions de paràmetres i valors frontera:

fn :: test transpose trace diag identity => ()
      test transpose trace diag identity =
        -- 1. Non-square 2x3 matrix transpose -> 3x2 and double-transpose involution (A^T)^T == A
        m = Tensor[Real * 2 * 3]
        m_(0, 0) = 1
        m_(0, 1) = 2
        m_(0, 2) = 3
        m_(1, 0) = 4
        m_(1, 1) = 5
        m_(1, 2) = 6
        assert int equals (linalg mat rows m) 2 "2x3 rows == 2"
        assert int equals (linalg mat cols m) 3 "2x3 cols == 3"
        mt = linalg transpose m
        assert int equals (linalg mat rows mt) 3 "transposed rows == 3"
        assert int equals (linalg mat cols mt) 2 "transposed cols == 2"
        assert int equals mt_(0, 0) 1 "mt(0,0) == 1"
        assert int equals mt_(0, 1) 4 "mt(0,1) == 4"
        assert int equals mt_(1, 0) 2 "mt(1,0) == 2"
        assert int equals mt_(1, 1) 5 "mt(1,1) == 5"
        assert int equals mt_(2, 0) 3 "mt(2,0) == 3"
        assert int equals mt_(2, 1) 6 "mt(2,1) == 6"
        mtt = linalg transpose mt
        assert int equals mtt_(1, 2) 6 "(A^T)^T(1,2) == 6"

        -- 2. Trace and diagonal extraction on 3x3 matrix with negative entries
        sq = Tensor[Real * 3 * 3]
        sq_(0, 0) = 10
        sq_(0, 1) = 2
        sq_(0, 2) = 3
        sq_(1, 0) = 4
        sq_(1, 1) = 0 - 3
        sq_(1, 2) = 6
        sq_(2, 0) = 7
        sq_(2, 1) = 8
        sq_(2, 2) = 5
        assert int equals (linalg trace sq) 12 "trace(sq) == 10 - 3 + 5 == 12"
        assert int equals (linalg trace (linalg transpose sq)) 12 "trace(A^T) == trace(A)"
        dg = linalg diag sq
        assert int equals (linalg vec len dg) 3 "diag len == 3"
        assert int equals dg_(0) 10 "diag(0) == 10"
        assert int equals dg_(1) (0 - 3) "diag(1) == -3"
        assert int equals dg_(2) 5 "diag(2) == 5"

        -- 3. Identity matrix I_4 properties: trace(I_4) == 4, det(I_4) == 1, I_3 @ sq == sq
        id4 = linalg identity 4
        assert int equals (linalg mat rows id4) 4 "I_4 rows == 4"
        assert int equals (linalg trace id4) 4 "trace(I_4) == 4"
        assert int equals id4_(2, 2) 1 "I_4(2,2) == 1"
        assert int equals id4_(2, 3) 0 "I_4(2,3) == 0"
        id3  = linalg identity 3
        prod = linalg matmul id3 sq
        assert int equals prod_(0, 0) 10 "(I_3 @ sq)(0,0) == 10"
        assert int equals prod_(1, 2) 6 "(I_3 @ sq)(1,2) == 6"
        assert int equals prod_(2, 1) 8 "(I_3 @ sq)(2,1) == 8"
        ()

14.3.4. Exemple 4: Producte escalar (dot), producte exterior (outer), matriu-vector (matvec), multiplicació matricial (matmul) i normes ($L_1$, $L_2$, $L_\infty$, Frobenius)

En aquest exemple pràctic explorem de manera exhaustiva com es comporta linalg en aquest bloc funcional, incloent-hi entrades estàndard, combinacions de paràmetres i valors frontera:

fn :: test dot outer matvec norms => ()
      test dot outer matvec norms =
        -- 1. Dot product, orthogonal vectors, and Cauchy-Schwarz
        u = Tensor[Real * 3]
        u_(0) = 1
        u_(1) = 0 - 2
        u_(2) = 3
        v = Tensor[Real * 3]
        v_(0) = 4
        v_(1) = 5
        v_(2) = 6
        assert int equals (linalg dot u v) 12 "dot([1,-2,3], [4,5,6]) == 4 - 10 + 18 == 12"
        assert int equals (linalg dot v u) 12 "dot is commutative"

        -- Orthogonal vectors: [1, 2, -1] . [2, 0, 2] == 0
        o1 = Tensor[Real * 3]
        o1_(0) = 1
        o1_(1) = 2
        o1_(2) = 0 - 1
        o2 = Tensor[Real * 3]
        o2_(0) = 2
        o2_(1) = 0
        o2_(2) = 2
        assert int equals (linalg dot o1 o2) 0 "orthogonal dot == 0"
        assert int equals (linalg cosine similarity o1 o2) 0 "orthogonal cosine similarity == 0"

        -- 2. Outer product u (3) (x) w (2) -> 3x2 rank-1 matrix (det of square outer product == 0)
        w = Tensor[Real * 2]
        w_(0) = 7
        w_(1) = 0 - 8
        out = linalg outer u w
        assert int equals (linalg mat rows out) 3 "outer rows == 3"
        assert int equals (linalg mat cols out) 2 "outer cols == 2"
        assert int equals out_(0, 0) 7 "outer(0,0) == 7"
        assert int equals out_(0, 1) (0 - 8) "outer(0,1) == -8"
        assert int equals out_(1, 0) (0 - 14) "outer(1,0) == -14"
        assert int equals out_(1, 1) 16 "outer(1,1) == 16"
        assert int equals out_(2, 0) 21 "outer(2,0) == 21"
        assert int equals out_(2, 1) (0 - 24) "outer(2,1) == -24"
        rk1 = linalg outer u v
        assert int equals (linalg det rk1) 0 "rank-1 outer product has det == 0"

        -- 3. Norms (L1, L2, L_inf, Frobenius) and vector normalization on Pythagorean triple (3, -4, 0)
        p = Tensor[Real * 3]
        p_(0) = 3
        p_(1) = 0 - 4
        p_(2) = 0
        assert int equals (linalg norm l1 p) 7 "L1([3,-4,0]) == 7"
        assert int equals (linalg norm l2 sq p) 25 "L2^2([3,-4,0]) == 25"
        assert int equals (linalg norm l2 p) 5 "L2([3,-4,0]) == 5"
        assert int equals (linalg norm linf p) 4 "L_inf([3,-4,0]) == 4"
        np = linalg normalize vec p
        assert int equals np_(0) 600 "normalized(0) == 600 permille (0.6)"
        assert int equals np_(1) (0 - 800) "normalized(1) == -800 permille (-0.8)"
        assert int equals np_(2) 0 "normalized(2) == 0"

        -- Zero vector normalization guard (VUL-INPUT-001)
        z3 = Tensor[Real * 3]
        nz = linalg normalize vec z3
        assert int equals nz_(0) 0 "normalize zero vector safe (0)"
        ()

14.3.5. Exemple 5: Determinants exactes (det2, det3, det) i resolució de sistemes lineals $Ax = b$ sense fraccions (linalg solve)

En aquest exemple pràctic explorem de manera exhaustiva com es comporta linalg en aquest bloc funcional, incloent-hi entrades estàndard, combinacions de paràmetres i valors frontera:

fn :: test det and linear solver => ()
      test det and linear solver =
        -- 1. 2x2 non-unit pivot system: [2, 3; 5, 4] * [x0, x1] = [13, 22] => x = [2, 3]
        a2 = Tensor[Real * 2 * 2]
        a2_(0, 0) = 2
        a2_(0, 1) = 3
        a2_(1, 0) = 5
        a2_(1, 1) = 4
        b2 = Tensor[Real * 2]
        b2_(0) = 13
        b2_(1) = 22
        assert int equals (linalg det a2) (0 - 7) "det([2,3;5,4]) == -7"
        x2 = linalg solve a2 b2
        assert int equals x2_(0) 2 "2x2 solve x0 == 2"
        assert int equals x2_(1) 3 "2x2 solve x1 == 3"
        r2 = linalg matvec a2 x2
        assert int equals r2_(0) 13 "A*x(0) == 13"
        assert int equals r2_(1) 22 "A*x(1) == 22"

        -- 2. 3x3 system requiring partial pivoting (0 in (0,0) entry!):
        -- [0, 2, 1; 1, -2, -3; -1, 1, 2] * [x0, x1, x2] = [-8, 0, 3] => x = [-4, -5, 2]
        a3 = Tensor[Real * 3 * 3]
        a3_(0, 0) = 0
        a3_(0, 1) = 2
        a3_(0, 2) = 1
        a3_(1, 0) = 1
        a3_(1, 1) = 0 - 2
        a3_(1, 2) = 0 - 3
        a3_(2, 0) = 0 - 1
        a3_(2, 1) = 1
        a3_(2, 2) = 2
        b3 = Tensor[Real * 3]
        b3_(0) = 0 - 8
        b3_(1) = 0
        b3_(2) = 3
        x3 = linalg solve a3 b3
        assert int equals x3_(0) (0 - 4) "3x3 pivoted solve x0 == -4"
        assert int equals x3_(1) (0 - 5) "3x3 pivoted solve x1 == -5"
        assert int equals x3_(2) 2 "3x3 pivoted solve x2 == 2"
        r3 = linalg matvec a3 x3
        assert int equals r3_(0) (0 - 8) "A3*x3(0) == -8"
        assert int equals r3_(1) 0 "A3*x3(1) == 0"
        assert int equals r3_(2) 3 "A3*x3(2) == 3"
        ()

14.3.6. Exemple 6: Funcions d'activació neuronal (relu, leaky relu, sigmoid, softmax, argmax, argmin, normalize)

En aquest exemple pràctic explorem de manera exhaustiva com es comporta linalg en aquest bloc funcional, incloent-hi entrades estàndard, combinacions de paràmetres i valors frontera:

fn :: test neural activations => ()
      test neural activations =
        -- 1. ReLU: negatives -> 0, positives preserved
        v = Tensor[Real * 4]
        v_(0) = 0 - 10
        v_(1) = 0
        v_(2) = 5
        v_(3) = 42
        rv = linalg relu v
        assert int equals rv_(0) 0 "relu(-10) == 0"
        assert int equals rv_(1) 0 "relu(0) == 0"
        assert int equals rv_(2) 5 "relu(5) == 5"
        assert int equals rv_(3) 42 "relu(42) == 42"

        -- 2. Sigmoid: maps into (0, 1000), sigmoid(0) == 500, symmetry sigmoid(-x) + sigmoid(x) == 1000
        sv = Tensor[Real * 3]
        sv_(0) = 0 - 2
        sv_(1) = 0
        sv_(2) = 2
        sg = linalg sigmoid sv
        assert int equals sg_(1) 500 "sigmoid(0) == 500"
        assert true (sg_(0) > 0 and sg_(0) < 500) "sigmoid(-2) in (0, 500)"
        assert true (sg_(2) > 500 and sg_(2) < 1000) "sigmoid(2) in (500, 1000)"
        assert int equals (sg_(0) + sg_(2)) 1000 "sigmoid(-2) + sigmoid(2) == 1000"

        -- 3. Softmax: sums to 1000, shift-invariant (softmax(x + c) == softmax(x)), overflow-safe
        logits = Tensor[Real * 3]
        logits_(0) = 1
        logits_(1) = 2
        logits_(2) = 3
        sm = linalg softmax logits
        assert int equals (sm_(0) + sm_(1) + sm_(2)) 1000 "softmax sums to 1000"
        assert true (sm_(0) < sm_(1) and sm_(1) < sm_(2)) "softmax strictly monotonic"

        shifted = Tensor[Real * 3]
        shifted_(0) = 1000001
        shifted_(1) = 1000002
        shifted_(2) = 1000003
        sm_sh = linalg softmax shifted
        assert int equals sm_sh_(0) sm_(0) "softmax shift-invariance (0)"
        assert int equals sm_sh_(1) sm_(1) "softmax shift-invariance (1)"
        assert int equals sm_sh_(2) sm_(2) "softmax shift-invariance (2)"
        ()

14.4. Sota el capó: com està construïda linalg en Ridgun pur (src/main/ridgun/lib/linalg.rg)

Un dels majors avantatges de ridgun-libs és que no hi ha caixes negres en C. Pots obrir src/main/ridgun/lib/linalg.rg i llegir exactament com està implementada cada operació fent servir pattern matching, arbres de guardes | / |. i recursió de cua (TCO). Vegem les peces clau de la seva implementació real:

14.4.1. Implementació interna: Internal Scalar Helpers

Fixa't en com linalg.rg estructura les signatures fn :: ... => ... primer i resol cada cas mitjançant equacions alineades i crides de cua:

module :: linalg

fn :: la abs Int => Int
      la abs x   =
        | x < 0     = 0 - x
        | otherwise = x

fn :: la max Int Int => Int
      la max a   b   =
        | a >= b    = a
        | otherwise = b

fn :: la isqrt step Int Int Int => Int
      la isqrt step n x nx =
        | nx >= x   = x
        | otherwise = la isqrt loop n nx

fn :: la isqrt loop Int Int => Int
      la isqrt loop n   x   =
        la isqrt step n x ((x + (n / x)) / 2)

fn :: la isqrt Int => Int
      la isqrt n   =
        | n <= 0    = 0
        | n < 4     = 1
        | otherwise = la isqrt loop n (n / 2)

fn :: la exp neg scaled Int => Int
      la exp neg scaled x   =
        | x >= 0          = 1000000
        | x == (0 - 1)    = 367879
        | x == (0 - 2)    = 135335
        | x == (0 - 3)    = 49787
        | x == (0 - 4)    = 18316
        | x == (0 - 5)    = 6738
        | x == (0 - 6)    = 2479
        | x == (0 - 7)    = 912
        | x == (0 - 8)    = 335
        | x == (0 - 9)    = 123
        | x == (0 - 10)   = 45
        | x == (0 - 11)   = 17
        | x == (0 - 12)   = 6
        | x == (0 - 13)   = 2
        | x == (0 - 14)   = 1
        | otherwise       = 0

14.4.2. Implementació interna: Matrix Transpose, Trace, Diagonal & Identity

Fixa't en com linalg.rg estructura les signatures fn :: ... => ... primer i resol cada cas mitjançant equacions alineades i crides de cua:

fn :: la transpose col loop Tensor[Real * r * c] Tensor[Real * c * r] Int Int Int => Tensor[Real * c * r]
      la transpose col loop m out i j cols =
        | j >= cols = out
        | otherwise =
          out_(j, i) = m_(i, j)
          la transpose col loop m out i (j + 1) cols

fn :: la transpose row loop Tensor[Real * r * c] Tensor[Real * c * r] Int Int Int => Tensor[Real * c * r]
      la transpose row loop m out i rows cols =
        | i >= rows = out
        | otherwise =
          out2 = la transpose col loop m out i 0 cols
          la transpose row loop m out2 (i + 1) rows cols

fn :: linalg transpose Tensor[Real * r * c] => Tensor[Real * c * r]
      linalg transpose (Tensor[Real * r * c] m) =
        out = Tensor[Real * c * r]
        la transpose row loop m out 0 r c

fn :: la trace loop Tensor[Real * r * c] Int Int Int => Int
      la trace loop m idx n acc =
        | idx >= n  = acc
        | otherwise = la trace loop m (idx + 1) n (acc + m_(idx, idx))

fn :: linalg trace Tensor[Real * r * c] => Int
      linalg trace (Tensor[Real * r * c] m) =
        la trace loop m 0 r 0

fn :: la diag loop Tensor[Real * r * c] Tensor[Real * r] Int Int => Tensor[Real * r]
      la diag loop m out idx n =
        | idx >= n  = out
        | otherwise =
          out_(idx) = m_(idx, idx)
          la diag loop m out (idx + 1) n

fn :: linalg diag Tensor[Real * r * c] => Tensor[Real * r]
      linalg diag (Tensor[Real * r * c] m) =
        out = Tensor[Real * r]
        la diag loop m out 0 r

fn :: la identity loop Tensor[Real * n * n] Int Int => Tensor[Real * n * n]
      la identity loop out idx n =
        | idx >= n  = out
        | otherwise =
          out_(idx, idx) = 1
          la identity loop out (idx + 1) n

fn :: linalg identity Int => Tensor[Real * n * n]
      linalg identity dim =
        la max 1 dim -> n
        out = Tensor[Real * n * n]
        la identity loop out 0 n

14.4.3. Implementació interna: Vector & Matrix Norms (norm_l1, norm_l2, norm_linf, frobenius)

Fixa't en com linalg.rg estructura les signatures fn :: ... => ... primer i resol cada cas mitjançant equacions alineades i crides de cua:

fn :: la norm l1 loop Tensor[Real * n] Int Int Int => Int
      la norm l1 loop v idx n acc =
        | idx >= n  = acc
        | otherwise = la norm l1 loop v (idx + 1) n (acc + (la abs v_(idx)))

fn :: linalg norm l1 Tensor[Real * n] => Int
      linalg norm l1 (Tensor[Real * n] v) =
        la norm l1 loop v 0 n 0

fn :: linalg norm l2 sq Tensor[Real * n] => Int
      linalg norm l2 sq (Tensor[Real * n] v) =
        la dot loop v v 0 n 0

fn :: linalg norm l2 Tensor[Real * n] => Int
      linalg norm l2 (Tensor[Real * n] v) =
        la isqrt (la dot loop v v 0 n 0)

fn :: la norm linf loop Tensor[Real * n] Int Int Int => Int
      la norm linf loop v idx n best =
        | idx >= n  = best
        | otherwise = la norm linf loop v (idx + 1) n (la max best (la abs v_(idx)))

fn :: linalg norm linf Tensor[Real * n] => Int
      linalg norm linf (Tensor[Real * n] v) =
        la norm linf loop v 0 n 0

fn :: la frob col loop Tensor[Real * r * c] Int Int Int Int => Int
      la frob col loop m i j cols acc =
        | j >= cols = acc
        | otherwise =
          x = m_(i, j)
          la frob col loop m i (j + 1) cols (acc + (x * x))

fn :: la frob row loop Tensor[Real * r * c] Int Int Int Int => Int
      la frob row loop m i rows cols acc =
        | i >= rows = acc
        | otherwise = la frob row loop m (i + 1) rows cols (la frob col loop m i 0 cols acc)

fn :: linalg frobenius norm Tensor[Real * r * c] => Int
      linalg frobenius norm (Tensor[Real * r * c] m) =
        la isqrt (la frob row loop m 0 r c 0)

fn :: la norm vec loop Tensor[Real * n] Tensor[Real * n] Int Int Int => Tensor[Real * n]
      la norm vec loop v out norm_val idx n =
        | idx >= n  = out
        | otherwise =
          out_(idx) = (v_(idx) * 1000) / norm_val
          la norm vec loop v out norm_val (idx + 1) n

fn :: la normalize vec step Tensor[Real * n] Tensor[Real * n] Int Int => Tensor[Real * n]
      la normalize vec step v out norm_value n =
        | norm_value <= 0 = out
        | otherwise       = la norm vec loop v out norm_value 0 n

fn :: linalg normalize vec Tensor[Real * n] => Tensor[Real * n]
      linalg normalize vec (Tensor[Real * n] v) =
        la normalize vec step v (Tensor[Real * n]) (linalg norm l2 v) n

fn :: la cosine step Tensor[Real * n] Tensor[Real * n] Int Int => Int
      la cosine step a b na nb =
        | na <= 0 or nb <= 0 = 0
        | otherwise          = ((linalg dot a b) * 1000) / (na * nb)

fn :: linalg cosine similarity Tensor[Real * n] Tensor[Real * n] => Int
      linalg cosine similarity (Tensor[Real * n] a) (Tensor[Real * n] b) =
        la cosine step a b (linalg norm l2 a) (linalg norm l2 b)

14.4.4. Implementació interna: Neural Activations (linalg relu, linalg sigmoid, linalg softmax)

Fixa't en com linalg.rg estructura les signatures fn :: ... => ... primer i resol cada cas mitjançant equacions alineades i crides de cua:

fn :: la relu scalar Int => Int
      la relu scalar x   =
        | x <= 0    = 0
        | otherwise = x

fn :: la relu loop Tensor[Real * n] Tensor[Real * n] Int Int => Tensor[Real * n]
      la relu loop v out idx n =
        | idx >= n  = out
        | otherwise =
          out_(idx) = la relu scalar v_(idx)
          la relu loop v out (idx + 1) n

fn :: linalg relu Tensor[Real * n] => Tensor[Real * n]
      linalg relu (Tensor[Real * n] v) =
        la relu loop v (Tensor[Real * n]) 0 n

fn :: la sigmoid scalar Int => Int
      la sigmoid scalar x   =
        | x == 0 = 500
        | x > 0  =
          en = la exp neg scaled (0 - x)
          (1000000 * 1000) / (1000000 + en)
        | otherwise =
          1000 - (la sigmoid scalar (0 - x))

fn :: la sigmoid loop Tensor[Real * n] Tensor[Real * n] Int Int => Tensor[Real * n]
      la sigmoid loop v out idx n =
        | idx >= n  = out
        | otherwise =
          out_(idx) = la sigmoid scalar v_(idx)
          la sigmoid loop v out (idx + 1) n

fn :: linalg sigmoid Tensor[Real * n] => Tensor[Real * n]
      linalg sigmoid (Tensor[Real * n] v) =
        la sigmoid loop v (Tensor[Real * n]) 0 n

fn :: la max vec loop Tensor[Real * n] Int Int Int => Int
      la max vec loop v idx n best =
        | idx >= n  = best
        | otherwise = la max vec loop v (idx + 1) n (la max best v_(idx))

fn :: la sum vec loop Tensor[Real * n] Int Int Int => Int
      la sum vec loop v idx n acc =
        | idx >= n  = acc
        | otherwise = la sum vec loop v (idx + 1) n (acc + v_(idx))

fn :: la exp shifted loop Tensor[Real * n] Tensor[Real * n] Int Int Int => Tensor[Real * n]
      la exp shifted loop v out max_val idx n =
        | idx >= n  = out
        | otherwise =
          out_(idx) = la exp neg scaled (v_(idx) - max_val)
          la exp shifted loop v out max_val (idx + 1) n

fn :: la softmax scale loop Tensor[Real * n] Tensor[Real * n] Int Int Int => Tensor[Real * n]
      la softmax scale loop exps out tot idx n =
        | idx >= n  = out
        | otherwise =
          out_(idx) = (exps_(idx) * 1000) / tot
          la softmax scale loop exps out tot (idx + 1) n

fn :: la argmax vec loop Tensor[Real * n] Int Int Int Int => Int
      la argmax vec loop v idx n best_idx best_val =
        | idx >= n           = best_idx
        | v_(idx) > best_val = la argmax vec loop v (idx + 1) n idx v_(idx)
        | otherwise          = la argmax vec loop v (idx + 1) n best_idx best_val

fn :: la softmax finish Tensor[Real * n] Tensor[Real * n] Int Int => Tensor[Real * n]
      la softmax finish v exps n tot =
        | tot <= 0  = Tensor[Real * n]
        | otherwise =
          out            = la softmax scale loop exps (Tensor[Real * n]) tot 0 n
          sum_p          = la sum vec loop out 0 n 0
          best_idx       = la argmax vec loop v 1 n 0 v_(0)
          out_(best_idx) = out_(best_idx) + (1000 - sum_p)
          out

fn :: linalg softmax Tensor[Real * n] => Tensor[Real * n]
      linalg softmax (Tensor[Real * n] v) =
        | n <= 0    = Tensor[Real * n]
        | otherwise =
          max_v = la max vec loop v 1 n v_(0)
          exps  = la exp shifted loop v (Tensor[Real * n]) max_v 0 n
          tot   = la sum vec loop exps 0 n 0
          la softmax finish v exps n tot

14.5. Garanties algorísmiques, memòria RMM1/RMM2 i les 5 Regles d'Or de linalg

  • Garantia algorísmica i de seguretat: Les matrius singulars retornen None netament a linalg solve (sense NaN silenciosos), i linalg softmax resta el màxim abans d'exponenciar per evitar desbordaments.
  • Complexitat de pila $O(1)$ amb TCO: Tots els bucles recursius a src/main/ridgun/lib/linalg.rg estan en posició de cua, de manera que ridgunc els compila a salts natius sense creixement de pila.
  • Memòria determinista (RMM1/RMM2): Zero pauses de recol·lector de brossa; totes les estructures intermèdies s'alliberen de forma determinista en sortir de l'àmbit.
  • Verificació contínua: Provada exhaustivament amb stable check i la suite automatitzada src/test/ridgun/test linalg.rg.

Les 5 Regles d'Or en programar amb linalg

  1. Declara import linalg a l'inici del fitxer i aprofita l'equivalència entre espais i guions baixos perquè el teu codi es llegeixi com a prosa.
  2. Alinea les teves canonades de dades amb =: encadena les crides a linalg en passos amb nom propi i deixa que stable format alineï les columnes.
  3. Aprofita les funcions totals de linalg a les teves guardes |: comprova els valors de retorn o desembolica els ADTs amb pattern matching en lloc d'assumir que l'entrada externa sempre és vàlida.
  4. Evita copiar grans estructures si només necessites consultar un camp: recorda que en RMM2 els arguments d'entrada no incrementen ni decrementen comptadors (+0 / -0).
  5. Combina linalg amb test (import test): escriu sempre assercions amb assert equals o assert true per fixar els invariants del teu domini.

Al Capítol 15 passarem a explorar en profunditat la lib codec (La lib codec — Hex, Base64, Base64url, URL Percent-Encoding i JSON Lleuger).