Skip to main content

shape_runtime/intrinsics/
matrix.rs

1//! Matrix intrinsics — full migration to typed marshal layer.
2//!
3//! Per the intrinsics-typed-CC migration's per-file table, all 4 matrix
4//! intrinsics (`matmul_vec`, `matmul_mat`, `mat_add`, `mat_sub`) migrate to
5//! `register_typed_fn_2` typed entries via [`create_matrix_intrinsics_module`].
6//!
7//! Inputs use the existing Phase 2d Array `Vec<Arc<HeapValue>>` FromSlot for
8//! nested `Array<Array<number>>` matrix arguments and `Arc<Vec<f64>>`
9//! for flat `Array<number>` vector arguments. Outputs project through
10//! `ConcreteReturn::ArrayHeapValue(Vec<Arc<HeapValue>>)` for nested-array
11//! returns (Phase 2d Array landed; production-active per arrow_module /
12//! csv_module / process_ops migrations) and `ConcreteReturn::ArrayF64` for
13//! flat returns.
14//!
15//! Body-side row extraction was previously via direct `Arc<HeapValue>`
16//! pattern-match against `HeapValue::TypedArray(TypedArrayData::F64(buf))`
17//! and `TypedArrayData::I64(...)` arms (mirror of `Arc<DataTable>`'s shape
18//! at `marshal.rs:200-217`). Per V3-S5 ckpt-1 (2026-05-15) the
19//! `TypedArrayData` enum was DELETED at `crates/shape-value/src/heap_value.rs`
20//! (W12-typed-array-data-deletion audit §3.5 + ADR-006 §2.7.24 Q25.A
21//! SUPERSEDED). The previous per-variant row-extraction + row-rebuild path
22//! cascade-breaks here; production migration target is the v2-raw
23//! `TypedArray<f64>` flat-struct carrier per audit §1.2 + §3.1 scalar
24//! recipe (the only existing monomorphization for `f64` rows). The
25//! `HeapValue::TypedArray(Arc<TypedArrayData>)` arm at
26//! `heap_variants.rs:476` is ckpt-4 territory; until that arm migrates,
27//! row-extract / row-rebuild surface-and-stop at runtime via the body
28//! helpers below.
29//!
30//! Matrices are represented as `Array<Array<number>>` at runtime.
31//! This module validates matrix shape once, flattens to contiguous row-major
32//! buffers, and runs tight numeric kernels.
33
34use crate::intrinsics::matrix_kernels;
35use crate::marshal::register_typed_fn_2;
36use crate::module_exports::ModuleExports;
37use crate::typed_module_exports::{ConcreteReturn, ConcreteType, TypedReturn};
38use shape_value::aligned_vec::AlignedVec;
39use shape_value::heap_value::{HeapValue, MatrixData};
40use std::sync::Arc;
41
42// ───────────────────── Module factory (4 typed entries) ─────────────────────
43
44/// Create the matrix intrinsics module with all 4 typed-marshal entry points.
45pub fn create_matrix_intrinsics_module() -> ModuleExports {
46    let mut module = ModuleExports::new("std::core::intrinsics::matrix");
47    module.description =
48        "Matrix intrinsics (matmul_vec, matmul_mat, mat_add, mat_sub)".to_string();
49
50    register_typed_fn_2::<_, Vec<Arc<HeapValue>>, Arc<Vec<f64>>>(
51        &mut module,
52        "__intrinsic_matmul_vec",
53        "Matrix-vector multiplication: `Mat<number> * Vec<number> -> Vec<number>`",
54        [("matrix", "Array<Array<number>>"), ("vector", "Array<number>")],
55        ConcreteType::ArrayNumber,
56        |matrix, vector, _ctx| {
57            let (a, rows, inner) = extract_matrix(&matrix, "Left matrix")?;
58            let b = vector.as_slice();
59            if inner != b.len() {
60                return Err(format!(
61                    "Matrix/vector dimension mismatch: matrix is {}x{}, vector is length {}",
62                    rows,
63                    inner,
64                    b.len()
65                ));
66            }
67            let mut out = vec![0.0; rows];
68            for i in 0..rows {
69                let row_base = i * inner;
70                let mut acc = 0.0;
71                for k in 0..inner {
72                    acc += a[row_base + k] * b[k];
73                }
74                out[i] = acc;
75            }
76            Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(out)))
77        },
78    );
79
80    register_typed_fn_2::<_, Vec<Arc<HeapValue>>, Vec<Arc<HeapValue>>>(
81        &mut module,
82        "__intrinsic_matmul_mat",
83        "Matrix-matrix multiplication: `Mat<number> * Mat<number> -> Mat<number>`",
84        [
85            ("a", "Array<Array<number>>"),
86            ("b", "Array<Array<number>>"),
87        ],
88        ConcreteType::ArrayHeapValue("Array<Array<number>>".to_string()),
89        |a_rows_arc, b_rows_arc, _ctx| {
90            let (a, a_rows, a_cols) = extract_matrix(&a_rows_arc, "Left matrix")?;
91            let (b, b_rows, b_cols) = extract_matrix(&b_rows_arc, "Right matrix")?;
92            if a_cols != b_rows {
93                return Err(format!(
94                    "Matrix dimension mismatch: left is {}x{}, right is {}x{}",
95                    a_rows, a_cols, b_rows, b_cols
96                ));
97            }
98            if a_rows == 0 || b_cols == 0 {
99                return Ok(TypedReturn::Concrete(ConcreteReturn::ArrayHeapValue(
100                    matrix_to_heap_value_vec(&[], a_rows, b_cols),
101                )));
102            }
103            let mut out = vec![0.0; a_rows * b_cols];
104            for i in 0..a_rows {
105                let a_row_base = i * a_cols;
106                let out_row_base = i * b_cols;
107                for k in 0..a_cols {
108                    let a_ik = a[a_row_base + k];
109                    let b_row_base = k * b_cols;
110                    for j in 0..b_cols {
111                        out[out_row_base + j] += a_ik * b[b_row_base + j];
112                    }
113                }
114            }
115            Ok(TypedReturn::Concrete(ConcreteReturn::ArrayHeapValue(
116                matrix_to_heap_value_vec(&out, a_rows, b_cols),
117            )))
118        },
119    );
120
121    register_typed_fn_2::<_, Vec<Arc<HeapValue>>, Vec<Arc<HeapValue>>>(
122        &mut module,
123        "__intrinsic_mat_add",
124        "Element-wise matrix addition: `Mat<number> + Mat<number>`",
125        [
126            ("a", "Array<Array<number>>"),
127            ("b", "Array<Array<number>>"),
128        ],
129        ConcreteType::ArrayHeapValue("Array<Array<number>>".to_string()),
130        |a_rows_arc, b_rows_arc, _ctx| {
131            let a = matrix_data_from_heap_value_vec(&a_rows_arc, "Left matrix")?;
132            let b = matrix_data_from_heap_value_vec(&b_rows_arc, "Right matrix")?;
133            let out = matrix_kernels::matrix_add(&a, &b)?;
134            Ok(TypedReturn::Concrete(ConcreteReturn::ArrayHeapValue(
135                matrix_data_to_heap_value_vec(&out),
136            )))
137        },
138    );
139
140    register_typed_fn_2::<_, Vec<Arc<HeapValue>>, Vec<Arc<HeapValue>>>(
141        &mut module,
142        "__intrinsic_mat_sub",
143        "Element-wise matrix subtraction: `Mat<number> - Mat<number>`",
144        [
145            ("a", "Array<Array<number>>"),
146            ("b", "Array<Array<number>>"),
147        ],
148        ConcreteType::ArrayHeapValue("Array<Array<number>>".to_string()),
149        |a_rows_arc, b_rows_arc, _ctx| {
150            let a = matrix_data_from_heap_value_vec(&a_rows_arc, "Left matrix")?;
151            let b = matrix_data_from_heap_value_vec(&b_rows_arc, "Right matrix")?;
152            let out = matrix_kernels::matrix_sub(&a, &b)?;
153            Ok(TypedReturn::Concrete(ConcreteReturn::ArrayHeapValue(
154                matrix_data_to_heap_value_vec(&out),
155            )))
156        },
157    );
158
159    module
160}
161
162// ───────────────────── Body-side helpers ─────────────────────
163
164/// Extract a row-`&[f64]`-equivalent from a single `Arc<HeapValue>` row element.
165///
166/// Pattern-match shape previously mirrored `marshal.rs:200-217`'s
167/// `FromSlot for Arc<DataTable>` and dispatched on `TypedArrayData::F64`
168/// / `TypedArrayData::I64` arms. Per V3-S5 ckpt-1 (2026-05-15) the
169/// `TypedArrayData` enum is DELETED (W12-typed-array-data-deletion audit
170/// §3.5 + ADR-006 §2.7.24 Q25.A SUPERSEDED); the post-deletion target is
171/// the v2-raw `TypedArray<f64>` flat-struct carrier per audit §1.2.
172/// Row-extract surface-and-stops at runtime until ckpt-4 lands the
173/// `HeapValue::TypedArray(Arc<TypedArrayData>)` arm migration to a v2-raw
174/// row carrier (`heap_variants.rs:476`). The legacy per-`TypedArrayData::X`
175/// dispatch shell is **refused on sight** under Refusal #1 (resurrection
176/// under rename) per ckpt-1 close-marker.
177fn row_to_f64_vec(_hv: &Arc<HeapValue>, label: &str, row_idx: usize) -> Result<Vec<f64>, String> {
178    Err(format!(
179        "{label} row {row_idx}: SURFACE — matrix row-extract reached a \
180         `HeapValue::TypedArray(Arc<TypedArrayData>)` carrier whose inner \
181         enum was DELETED at V3-S5 ckpt-1 (2026-05-15). Production target \
182         is the v2-raw `TypedArray<f64>` / `TypedArray<i64>` flat-struct \
183         carrier per W12-typed-array-data-deletion audit §1.2 + §3.1 \
184         scalar recipe + ADR-006 §2.7.24 Q25.A SUPERSEDED. The \
185         `HeapValue::TypedArray` variant migration to v2-raw rows is \
186         ckpt-4 territory (`heap_variants.rs:476`). Cascade-broken \
187         surface; UNREACHABLE until ckpt-4 + ckpt-5 land the row-carrier \
188         cascade.",
189        label = label,
190        row_idx = row_idx,
191    ))
192}
193
194/// Walk a `Vec<Arc<HeapValue>>` of rows; produce a flat row-major `Vec<f64>`
195/// + dimensions. Validates rectangularity and rejects non-numeric rows.
196fn extract_matrix(
197    rows: &[Arc<HeapValue>],
198    label: &str,
199) -> Result<(Vec<f64>, usize, usize), String> {
200    if rows.is_empty() {
201        return Ok((Vec::new(), 0, 0));
202    }
203    let mut cols: Option<usize> = None;
204    let mut flat = Vec::new();
205    for (row_idx, hv) in rows.iter().enumerate() {
206        let row = row_to_f64_vec(hv, label, row_idx)?;
207        match cols {
208            Some(expected) if row.len() != expected => {
209                return Err(format!(
210                    "{} has non-rectangular rows: expected {}, got {} at row {}",
211                    label,
212                    expected,
213                    row.len(),
214                    row_idx
215                ));
216            }
217            None => cols = Some(row.len()),
218            _ => {}
219        }
220        flat.extend_from_slice(&row);
221    }
222    Ok((flat, rows.len(), cols.unwrap_or(0)))
223}
224
225/// Build a `MatrixData` from the nested `Vec<Arc<HeapValue>>` row representation.
226/// Used by `mat_add` / `mat_sub` so their dimension-check error paths stay
227/// identical to `matmul_mat`.
228fn matrix_data_from_heap_value_vec(
229    rows: &[Arc<HeapValue>],
230    label: &str,
231) -> Result<MatrixData, String> {
232    let (flat, num_rows, cols) = extract_matrix(rows, label)?;
233    let aligned = if flat.is_empty() {
234        AlignedVec::new()
235    } else {
236        AlignedVec::from_vec(flat)
237    };
238    Ok(MatrixData::from_flat(aligned, num_rows as u32, cols as u32))
239}
240
241/// Convert a flat row-major `&[f64]` of dimensions `rows`x`cols` into
242/// `Vec<Arc<HeapValue>>` rows.
243///
244/// Previously built each row as
245/// `Arc::new(HeapValue::TypedArray(Arc::new(TypedArrayData::F64(Arc::new(
246/// AlignedTypedBuffer::from(...))))))`. Per V3-S5 ckpt-1 (2026-05-15) the
247/// inner `TypedArrayData` enum is DELETED (W12-typed-array-data-deletion
248/// audit §3.5 + ADR-006 §2.7.24 Q25.A SUPERSEDED). The v2-raw `TypedArray
249/// <f64>` row carrier exists at the producer side (audit §1.3) but the
250/// `HeapValue::TypedArray(Arc<TypedArrayData>)` enum arm is ckpt-4
251/// territory (`heap_variants.rs:476`) — until ckpt-4 lands the row
252/// carrier migration, host-side row-rebuild surface-and-stops at runtime.
253/// Empty-matrix dimension reporting still works via the zero-row early-return.
254fn matrix_to_heap_value_vec(_flat: &[f64], rows: usize, cols: usize) -> Vec<Arc<HeapValue>> {
255    if rows == 0 {
256        return Vec::new();
257    }
258    // SURFACE: cannot construct row carriers post-V3-S5 ckpt-1.
259    // The non-empty case structurally cascade-breaks at the production
260    // target until ckpt-4 lands the `HeapValue::TypedArray` arm
261    // v2-raw migration. Production callers (`__intrinsic_matmul_vec` /
262    // `_mat` / `mat_add` / `mat_sub`) surface a typed `String` error via
263    // their `register_typed_fn_2` shell; this body would only be reached
264    // post-ckpt-4 v2-raw row-carrier landing. Return empty as a
265    // dimension-preserving placeholder; the calling intrinsic's
266    // `extract_matrix` -> `row_to_f64_vec` surface-and-stop fires before
267    // this path is hit on any non-trivial matrix input.
268    debug_assert!(
269        cols > 0,
270        "matrix_to_heap_value_vec ckpt-2 broken-state: \
271         non-empty rows={} requested but row-carrier rebuild path \
272         cascade-broken (TypedArrayData DELETED at ckpt-1; \
273         HeapValue::TypedArray arm migration is ckpt-4 territory). \
274         W12-typed-array-data-deletion audit §1.2 + §3.1 production \
275         target = v2-raw TypedArray<f64>.",
276        rows,
277    );
278    let _ = (cols,);
279    Vec::new()
280}
281
282/// Convert a kernel-produced `MatrixData` back to the nested-array
283/// representation.
284fn matrix_data_to_heap_value_vec(mat: &MatrixData) -> Vec<Arc<HeapValue>> {
285    matrix_to_heap_value_vec(mat.data.as_slice(), mat.rows as usize, mat.cols as usize)
286}
287
288// V3-S5 ckpt-4 (2026-05-15): the `_ckpt4_carrier_pin` dead-code marker
289// introduced by V3-S5 ckpt-2 to hold the `AlignedTypedBuffer` import name
290// alive across the chain is DELETED. `typed_buffer.rs` (the file defining
291// `AlignedTypedBuffer`) is itself deleted at ckpt-4 per W12-typed-array-
292// data-deletion-audit §B + ADR-006 §2.7.24 Q25.A SUPERSEDED — pinning a
293// deleted type makes no sense.
294//
295// The four `register_typed_fn_2::<_, _, Arc<AlignedTypedBuffer>>` /
296// `register_typed_fn_1::<_, Arc<AlignedTypedBuffer>>` registrations
297// earlier in this file (matmul_vec at line 51 and the wider intrinsics
298// fleet at statistical.rs / math.rs / fft.rs / array_transforms.rs /
299// convolution.rs / distributions.rs / rolling.rs / recurrence.rs /
300// vector.rs) cascade-break with the typed_buffer.rs deletion. Their
301// resolution is V3-S5 ckpt-5 + downstream-wave territory (per
302// supervisor 2026-05-15 partition; the dispatch-territory list for
303// ckpt-5 names marshal.rs / wire_conversion.rs / json_value.rs cascade
304// pickup, which contains the `FromSlot<Arc<AlignedTypedBuffer>>` /
305// `ToSlot<Arc<AlignedTypedBuffer>>` impls that route the intrinsics'
306// typed-marshal contract).
307//
308// Refusal #1 binding: no resurrection under any rename/shim/bridge.