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;