1use crate::Error;
13use crate::circuit::{Circuit, OpKind, OutOfRange, resolve_index_position};
14use crate::numeric::{evaluate_boolean, relu32};
15use crate::wire::Wire;
16
17#[derive(Clone, Debug)]
18pub struct IndexReadRow {
19 pub row: u32,
20 pub index_row: u32,
21 pub below_zero: bool,
22 pub above_zero: bool,
23 pub candidate_positions: Vec<u32>,
25}
26
27#[derive(Clone, Debug)]
28pub struct TensorLayer {
29 pub input_indices: Vec<u32>,
31 pub output_size: usize,
32 pub bias: Vec<f32>,
34 pub row_offsets: Vec<u32>,
36 pub columns: Vec<u32>,
37 pub weights: Vec<f32>,
38 pub gate_rows: Vec<u32>,
40 pub boolean_rows: Vec<u32>,
41 pub index_reads: Vec<IndexReadRow>,
42}
43
44#[derive(Clone, Debug)]
45pub struct TensorProgram {
46 pub layers: Vec<TensorLayer>,
47 pub roots: Vec<Wire>,
48 pub source_size: usize,
49 pub value_count: usize,
51 pub wire_order: Vec<Wire>,
53 pub width: usize,
55}
56
57impl TensorProgram {
58 pub fn stored_weight_count(&self) -> usize {
59 self.layers.iter().map(|layer| layer.weights.len()).sum()
60 }
61
62 pub fn live_operation_count(&self) -> usize {
63 self.value_count - self.source_size
64 }
65}
66
67impl Circuit {
68 pub fn compile(&self, roots: Option<&[Wire]>) -> TensorProgram {
71 let width = self.width();
72 let source_size = self.source_size();
73 let roots: Vec<Wire> = match roots {
74 Some(roots) => roots.to_vec(),
75 None => (source_size..width).map(Wire::from_index).collect(),
76 };
77 let live = self.find_live_operations(&roots);
78 let mut operations_by_layer: Vec<Vec<usize>> = Vec::new();
79 for (operation_index, operation) in self.operations.iter().enumerate() {
80 if !live[operation_index] {
81 continue;
82 }
83 let layer = operation.depth as usize - 1;
84 if operations_by_layer.len() <= layer {
85 operations_by_layer.resize(layer + 1, Vec::new());
86 }
87 operations_by_layer[layer].push(operation_index);
88 }
89
90 let live_count = live.iter().filter(|&&is_live| is_live).count();
91 let value_count = source_size + live_count;
92 let mut wire_order: Vec<Wire> = (0..source_size).map(Wire::from_index).collect();
93 let mut packed_by_wire = vec![u32::MAX; width];
94 for (wire, packed) in packed_by_wire.iter_mut().enumerate().take(source_size) {
95 *packed = wire as u32;
96 }
97 let mut column_by_wire = vec![u32::MAX; width];
99 let mut available = source_size;
100 let mut layers = Vec::new();
101 for operation_indices in operations_by_layer.iter().filter(|indices| !indices.is_empty()) {
102 let output_size = operation_indices.len();
103 let mut input_wires: Vec<Wire> = Vec::new();
104 for &operation_index in operation_indices {
105 let operation = &self.operations[operation_index];
106 for &(wire, _) in operation.left_terms.iter().chain(&operation.gate_terms) {
107 if column_by_wire[wire.index()] == u32::MAX {
108 column_by_wire[wire.index()] = 0;
109 input_wires.push(wire);
110 }
111 }
112 }
113 input_wires.sort_by_key(|wire| packed_by_wire[wire.index()]);
114 for (column, wire) in input_wires.iter().enumerate() {
115 column_by_wire[wire.index()] = column as u32;
116 }
117 let gate_count =
118 operation_indices.iter().filter(|&&index| self.operations[index].kind == OpKind::Reglu).count();
119 let index_count =
120 operation_indices.iter().filter(|&&index| self.operations[index].kind == OpKind::Index).count();
121 let projection_size = output_size + gate_count + index_count;
122 let mut bias = vec![0f32; projection_size];
123 let mut projection_terms: Vec<&[(Wire, f64)]> = vec![&[]; projection_size];
124 let mut gate_rows = Vec::new();
125 let mut boolean_rows = Vec::new();
126 let mut pending_index_reads = Vec::new();
127 let (mut gate_row, mut index_row) = (0, 0);
128 for (row, &operation_index) in operation_indices.iter().enumerate() {
129 let operation = &self.operations[operation_index];
130 let output_wire = source_size + operation_index;
131 wire_order.push(Wire::from_index(output_wire));
132 packed_by_wire[output_wire] = (available + row) as u32;
133 bias[row] = operation.left_bias as f32;
134 projection_terms[row] = &operation.left_terms;
135 match operation.kind {
136 OpKind::Reglu => {
137 let projection_row = output_size + gate_row;
138 bias[projection_row] = operation.gate_bias as f32;
139 projection_terms[projection_row] = &operation.gate_terms;
140 gate_rows.push(row as u32);
141 gate_row += 1;
142 }
143 OpKind::Boolean => boolean_rows.push(row as u32),
144 OpKind::Index => {
145 let projection_row = output_size + gate_count + index_row;
146 bias[projection_row] = operation.gate_bias as f32;
147 projection_terms[projection_row] = &operation.gate_terms;
148 pending_index_reads.push((row, projection_row, operation.index_read.as_ref().unwrap()));
149 index_row += 1;
150 }
151 OpKind::Affine => {}
152 }
153 }
154 let index_reads = pending_index_reads
155 .into_iter()
156 .map(|(row, index_row, read)| IndexReadRow {
157 row: row as u32,
158 index_row: index_row as u32,
159 below_zero: read.below == OutOfRange::Zero,
160 above_zero: read.above == OutOfRange::Zero,
161 candidate_positions: self.tables[read.table]
162 .candidates
163 .iter()
164 .map(|candidate| {
165 let position = packed_by_wire[candidate.index()];
166 assert!((position as usize) < available, "index read candidate is not available");
167 position
168 })
169 .collect(),
170 })
171 .collect();
172 let mut row_offsets = Vec::with_capacity(projection_size + 1);
173 let mut columns = Vec::new();
174 let mut weights = Vec::new();
175 row_offsets.push(0);
176 for terms in &projection_terms {
177 for &(wire, coefficient) in terms.iter() {
178 let column = column_by_wire[wire.index()];
179 assert!(column != u32::MAX, "tensor dependency is not available");
180 columns.push(column);
181 weights.push(coefficient as f32);
182 }
183 row_offsets.push(columns.len() as u32);
184 }
185 for wire in &input_wires {
186 column_by_wire[wire.index()] = u32::MAX;
187 }
188 layers.push(TensorLayer {
189 input_indices: input_wires.iter().map(|wire| packed_by_wire[wire.index()]).collect(),
190 output_size,
191 bias,
192 row_offsets,
193 columns,
194 weights,
195 gate_rows,
196 boolean_rows,
197 index_reads,
198 });
199 available += output_size;
200 }
201 TensorProgram { layers, roots, source_size, value_count, wire_order, width }
202 }
203
204 fn find_live_operations(&self, roots: &[Wire]) -> Vec<bool> {
205 let source_size = self.source_size();
206 let mut live = vec![false; self.operations.len()];
207 let mut visited_tables = vec![false; self.tables.len()];
208 let mut pending = roots.to_vec();
209 while let Some(wire) = pending.pop() {
210 self.assert_wire(wire);
211 let Some(operation_index) = wire.index().checked_sub(source_size) else { continue };
212 if live[operation_index] {
213 continue;
214 }
215 live[operation_index] = true;
216 let operation = &self.operations[operation_index];
217 pending.extend(operation.left_terms.iter().chain(&operation.gate_terms).map(|&(wire, _)| wire));
218 if let Some(read) = &operation.index_read
219 && !visited_tables[read.table]
220 {
221 visited_tables[read.table] = true;
222 pending.extend(&self.tables[read.table].candidates);
223 }
224 }
225 live
226 }
227
228 pub fn evaluate_tensor(&self, source: &[f32], program: &TensorProgram) -> Result<Vec<f32>, Error> {
230 self.validate_source_ranges(source)?;
231 let mut executor = TensorExecutor::new(program);
232 executor.run(source);
233 let mut values = vec![0f32; program.width];
234 for (packed, wire) in program.wire_order.iter().enumerate() {
235 values[wire.index()] = executor.packed_values[packed];
236 }
237 Ok(values)
238 }
239
240 pub fn validate_tensor_evaluation(
243 &self,
244 source: &[f32],
245 program: &TensorProgram,
246 tensor_values: &[f32],
247 ) -> Result<(), String> {
248 let expected = self.evaluate_unchecked(source);
249 for wire in &program.wire_order {
250 let (got, want) = (tensor_values[wire.index()], expected[wire.index()]);
251 if got != want {
252 return Err(format!("tensor wire {} is {got}, the reference evaluator gives {want}", wire.index()));
253 }
254 }
255 Ok(())
256 }
257}
258
259pub struct TensorExecutor<'program> {
261 program: &'program TensorProgram,
262 pub packed_values: Vec<f32>,
264 input: Vec<f32>,
265 projection: Vec<f32>,
266 root_positions: Vec<usize>,
267 outputs: Vec<f32>,
268}
269
270impl<'program> TensorExecutor<'program> {
271 pub fn new(program: &'program TensorProgram) -> TensorExecutor<'program> {
272 let max_input = program.layers.iter().map(|layer| layer.input_indices.len()).max().unwrap_or(0);
273 let max_projection = program.layers.iter().map(|layer| layer.bias.len()).max().unwrap_or(0);
274 let root_positions = program
275 .roots
276 .iter()
277 .map(|root| program.wire_order.iter().position(|wire| wire == root).expect("root was removed"))
278 .collect();
279 TensorExecutor {
280 program,
281 packed_values: vec![0.0; program.value_count],
282 input: vec![0.0; max_input],
283 projection: vec![0.0; max_projection],
284 root_positions,
285 outputs: vec![0.0; program.roots.len()],
286 }
287 }
288
289 pub fn run(&mut self, source: &[f32]) -> &[f32] {
292 let program = self.program;
293 assert_eq!(source.len(), program.source_size, "source table has the wrong length");
294 let values = &mut self.packed_values;
295 values[..source.len()].copy_from_slice(source);
296 let mut available = source.len();
297 for layer in &program.layers {
298 for (column, &packed) in layer.input_indices.iter().enumerate() {
299 self.input[column] = values[packed as usize];
300 }
301 let projection = &mut self.projection[..layer.bias.len()];
302 for (row, output) in projection.iter_mut().enumerate() {
303 let mut value = layer.bias[row];
304 for entry in layer.row_offsets[row] as usize..layer.row_offsets[row + 1] as usize {
305 value += layer.weights[entry] * self.input[layer.columns[entry] as usize];
306 }
307 *output = value;
308 }
309 for read in &layer.index_reads {
310 let index = projection[read.index_row as usize] as f64;
311 let below = if read.below_zero { OutOfRange::Zero } else { OutOfRange::Clamp };
312 let above = if read.above_zero { OutOfRange::Zero } else { OutOfRange::Clamp };
313 projection[read.row as usize] =
314 match resolve_index_position(index, read.candidate_positions.len(), below, above) {
315 -2 => f32::NAN,
316 -1 => 0.0,
317 position => values[read.candidate_positions[position as usize] as usize],
318 };
319 }
320 for (gate, &row) in layer.gate_rows.iter().enumerate() {
321 let gate_value = relu32(projection[layer.output_size + gate]);
322 projection[row as usize] *= gate_value;
323 }
324 for &row in &layer.boolean_rows {
325 projection[row as usize] = evaluate_boolean(projection[row as usize]);
326 }
327 values[available..available + layer.output_size].copy_from_slice(&projection[..layer.output_size]);
328 available += layer.output_size;
329 }
330 for (output, &position) in self.outputs.iter_mut().zip(&self.root_positions) {
331 *output = values[position];
332 }
333 &self.outputs
334 }
335}