Skip to main content

laddu_kernel/
ir.rs

1pub use crate::KernelIrError;
2use laddu_expr::{BinaryOp, UnaryOp, parameters::ParamId};
3use num::complex::Complex64;
4
5/// Stable identifier for a value in kernel IR.
6#[derive(Copy, Clone, Debug, PartialEq, Eq)]
7pub struct KernelValueId(usize);
8
9impl KernelValueId {
10    /// Creates an identifier from a zero-based value index.
11    pub fn from_index(index: usize) -> Self {
12        Self(index)
13    }
14
15    /// Returns the zero-based value index.
16    pub fn index(self) -> usize {
17        self.0
18    }
19}
20
21/// Runtime shape and scalar representation of a kernel value.
22#[derive(Copy, Clone, Debug, PartialEq, Eq)]
23pub enum KernelValueKind {
24    /// A real scalar.
25    Real,
26    /// A complex scalar.
27    Complex,
28    /// A complex vector.
29    Vector {
30        /// Number of elements.
31        len: usize,
32    },
33    /// A complex matrix.
34    Matrix {
35        /// Number of rows.
36        rows: usize,
37        /// Number of columns.
38        cols: usize,
39    },
40}
41
42impl KernelValueKind {
43    /// Returns the number of logical scalar elements.
44    ///
45    /// # Panics
46    ///
47    /// Panics when matrix dimensions exceed the addressable `usize` width.
48    /// Validated kernel IR rejects such dimensions during construction.
49    pub fn width(self) -> usize {
50        match self {
51            Self::Real | Self::Complex => 1,
52            Self::Vector { len } => len,
53            Self::Matrix { rows, cols } => checked_matrix_width(rows, cols)
54                .expect("kernel matrix dimensions exceed addressable width"),
55        }
56    }
57
58    fn scalar_combine(self, rhs: Self) -> Option<Self> {
59        match (self, rhs) {
60            (Self::Real, Self::Real) => Some(Self::Real),
61            (Self::Real | Self::Complex, Self::Real | Self::Complex) => Some(Self::Complex),
62            _ => None,
63        }
64    }
65
66    fn is_scalar(self) -> bool {
67        matches!(self, Self::Real | Self::Complex)
68    }
69}
70
71fn checked_matrix_width(rows: usize, cols: usize) -> Option<usize> {
72    rows.checked_mul(cols)
73}
74
75fn checked_row_major_index(rows: usize, cols: usize, row: usize, col: usize) -> Option<usize> {
76    checked_matrix_width(rows, cols)?;
77    if row >= rows || col >= cols {
78        return None;
79    }
80    row.checked_mul(cols)?.checked_add(col)
81}
82
83/// Whether a kernel value is constant across events or event-dependent.
84#[derive(Copy, Clone, Debug, PartialEq, Eq)]
85pub enum KernelValueClass {
86    /// The value depends only on constants and parameters.
87    Invariant,
88    /// The value depends on event data or cache inputs.
89    Event,
90}
91
92/// How an instruction's event dependence is determined.
93#[derive(Copy, Clone, Debug, PartialEq, Eq)]
94pub enum KernelEventDependence {
95    /// The instruction is invariant regardless of its operands.
96    Invariant,
97    /// The instruction is event-dependent regardless of its operands.
98    Event,
99    /// The instruction is event-dependent when any direct operand is event-dependent.
100    Operands,
101}
102
103/// Operation that produces one value in kernel IR.
104#[derive(Clone, Debug)]
105pub enum KernelInstruction {
106    /// Reads a precomputed cache slot.
107    Cached(usize),
108    /// Emits a real constant.
109    RealConstant(f64),
110    /// Emits a complex constant.
111    ComplexConstant(Complex64),
112    /// Reads a scalar parameter.
113    Parameter(ParamId),
114    /// Applies a unary scalar operation.
115    Unary {
116        /// Operation to apply.
117        op: UnaryOp,
118        /// Input value.
119        input: KernelValueId,
120    },
121    /// Applies a binary scalar operation.
122    Binary {
123        /// Operation to apply.
124        op: BinaryOp,
125        /// Left operand.
126        lhs: KernelValueId,
127        /// Right operand.
128        rhs: KernelValueId,
129    },
130    /// Adds scalar operands.
131    Add(Vec<KernelValueId>),
132    /// Multiplies scalar operands.
133    Mul(Vec<KernelValueId>),
134    /// Constructs a complex scalar from real components.
135    Complex {
136        /// Real component.
137        re: KernelValueId,
138        /// Imaginary component.
139        im: KernelValueId,
140    },
141    /// Constructs a vector from scalar elements.
142    Vector(Vec<KernelValueId>),
143    /// Constructs a row-major matrix.
144    Matrix {
145        /// Number of rows.
146        rows: usize,
147        /// Number of columns.
148        cols: usize,
149        /// Row-major scalar elements.
150        elements: Vec<KernelValueId>,
151    },
152    /// Selects a vector component.
153    Component {
154        /// Vector input.
155        input: KernelValueId,
156        /// Zero-based component index.
157        index: usize,
158    },
159    /// Selects a matrix element.
160    MatrixElement {
161        /// Matrix input.
162        input: KernelValueId,
163        /// Zero-based row index.
164        row: usize,
165        /// Zero-based column index.
166        col: usize,
167    },
168    /// Multiplies two matrices.
169    MatMul {
170        /// Left matrix.
171        lhs: KernelValueId,
172        /// Right matrix.
173        rhs: KernelValueId,
174    },
175    /// Multiplies a matrix by a vector.
176    MatVec {
177        /// Matrix operand.
178        matrix: KernelValueId,
179        /// Vector operand.
180        vector: KernelValueId,
181    },
182    /// Computes a vector dot product.
183    Dot {
184        /// Left vector.
185        lhs: KernelValueId,
186        /// Right vector.
187        rhs: KernelValueId,
188    },
189    /// Solves a linear system.
190    Solve {
191        /// Coefficient matrix.
192        matrix: KernelValueId,
193        /// Right-hand-side vector.
194        rhs: KernelValueId,
195    },
196    /// Evaluates one row of a specialized cached solve.
197    SolveRow {
198        /// Cache slot containing the decomposed matrix row data.
199        row_slot: usize,
200        /// Right-hand-side scalar values.
201        rhs: Vec<KernelValueId>,
202    },
203    /// Evaluates one adjoint element for a specialized solve row.
204    SolveRowAdjointElement {
205        /// Cache slot containing the decomposed matrix row data.
206        row_slot: usize,
207        /// Element index within the row.
208        index: usize,
209        /// Row length.
210        len: usize,
211        /// Incoming scalar adjoint.
212        adjoint: KernelValueId,
213    },
214}
215
216/// Typed instruction and evaluation class for one kernel IR value.
217#[derive(Clone, Debug)]
218pub struct KernelValue {
219    /// Value shape and scalar representation.
220    pub kind: KernelValueKind,
221    /// Event-dependency class.
222    pub class: KernelValueClass,
223    /// Instruction that produces the value.
224    pub instruction: KernelInstruction,
225}
226
227/// Validated IR for a kernel with one scalar output.
228#[derive(Clone, Debug)]
229pub struct ScalarKernelIr {
230    values: Vec<KernelValue>,
231    root: KernelValueId,
232}
233
234/// Validated IR for a kernel that populates multiple cache outputs.
235#[derive(Clone, Debug)]
236pub struct CacheKernelIr {
237    values: Vec<KernelValue>,
238    outputs: Vec<KernelValueId>,
239}
240
241/// Scalar component of a complex primal output to differentiate.
242#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
243pub enum OutputComponent {
244    /// Differentiate the real component.
245    Real,
246    /// Differentiate the imaginary component.
247    Imag,
248}
249
250/// Validated IR containing a primal computation and real gradient outputs.
251#[derive(Clone, Debug)]
252pub struct GradientKernelIr {
253    values: Vec<KernelValue>,
254    primal_root: KernelValueId,
255    outputs: Vec<KernelValueId>,
256    component: OutputComponent,
257}
258
259/// Builder for appending type-checked instructions to existing scalar IR.
260#[derive(Clone, Debug)]
261pub struct KernelIrBuilder {
262    values: Vec<KernelValue>,
263}
264
265mod builder;
266mod instruction;
267mod validate;
268mod wrappers;
269
270use validate::validate_graph;
271
272#[cfg(test)]
273mod tests;