Skip to main content

handwired_rs/
cells.rs

1//! Cell declarations for a program's source table.
2//!
3//! A simplified port of handwired's `cells.ts`. Each cell is declared once,
4//! with what happens to it around an evaluation, its range, and optionally a
5//! grid. The table gives the circuit its declarations, the initial table, a
6//! validator, and the next table after an evaluation.
7//!
8//! Building has two phases, since a circuit needs its source size first:
9//! declare every cell and call [`SourceTable::create_circuit`], then build,
10//! pair each state cell with its next value through [`SourceTable::next`],
11//! and call [`SourceTable::finish`].
12
13use crate::circuit::Circuit;
14use crate::numeric::{Range, is_power_of_two_grid};
15use crate::wire::Wire;
16
17/// What happens to a cell around an evaluation.
18#[derive(Copy, Clone, Debug, PartialEq, Eq)]
19pub enum Policy {
20    /// Set once in the initial table. The program keeps it.
21    Constant,
22    /// The host writes it before an evaluation. The program keeps it.
23    Input,
24    /// An exact bit the host sets to 1 when an event arrives. The program resets it to 0.
25    Event,
26    /// The program computes its next value.
27    State,
28}
29
30#[derive(Copy, Clone, Debug)]
31pub struct CellSpec {
32    pub policy: Policy,
33    pub range: Range,
34    pub initial: f64,
35    pub exact_bit: bool,
36    pub grid: Option<f64>,
37}
38
39impl CellSpec {
40    fn new(policy: Policy, min: f64, max: f64, exact_bit: bool) -> CellSpec {
41        assert!(min <= max, "a cell's range is inverted: {min}..{max}");
42        CellSpec { policy, range: Range::new(min, max), initial: 0.0, exact_bit, grid: None }
43    }
44
45    pub fn initial(mut self, value: f64) -> CellSpec {
46        self.initial = value;
47        self
48    }
49
50    /// Every value is a multiple of `step`, a power of two, e.g. 1/64 for pointer coordinates.
51    pub fn grid(mut self, step: f64) -> CellSpec {
52        assert!(is_power_of_two_grid(step), "a grid step must be a power of two");
53        self.grid = Some(step);
54        self
55    }
56}
57
58pub fn constant(value: f64, min: f64, max: f64) -> CellSpec {
59    CellSpec::new(Policy::Constant, min, max, false).initial(value)
60}
61pub fn input(min: f64, max: f64) -> CellSpec {
62    CellSpec::new(Policy::Input, min, max, false)
63}
64pub fn input_bit() -> CellSpec {
65    CellSpec::new(Policy::Input, 0.0, 1.0, true)
66}
67pub fn event() -> CellSpec {
68    CellSpec::new(Policy::Event, 0.0, 1.0, true)
69}
70pub fn state(min: f64, max: f64) -> CellSpec {
71    CellSpec::new(Policy::State, min, max, false)
72}
73pub fn state_bit() -> CellSpec {
74    CellSpec::new(Policy::State, 0.0, 1.0, true)
75}
76
77#[derive(Clone, Debug)]
78struct DeclaredCell {
79    name: String,
80    spec: CellSpec,
81    next: Option<Wire>,
82}
83
84#[derive(Clone, Debug, Default)]
85pub struct SourceTable {
86    cells: Vec<DeclaredCell>,
87    circuit_created: bool,
88}
89
90impl SourceTable {
91    pub fn new() -> SourceTable {
92        SourceTable::default()
93    }
94
95    /// Declare one cell, returning its wire.
96    pub fn declare(&mut self, name: impl Into<String>, spec: CellSpec) -> Wire {
97        assert!(!self.circuit_created, "declare every cell before create_circuit");
98        let name = name.into();
99        assert!(self.cells.iter().all(|cell| cell.name != name), "cell {name} is declared twice");
100        self.cells.push(DeclaredCell { name, spec, next: None });
101        Wire::from_index(self.cells.len() - 1)
102    }
103
104    pub fn len(&self) -> usize {
105        self.cells.len()
106    }
107
108    pub fn is_empty(&self) -> bool {
109        self.cells.is_empty()
110    }
111
112    /// A circuit with every cell's range, grid and exact-bit declaration.
113    pub fn create_circuit(&mut self) -> Circuit {
114        self.circuit_created = true;
115        let mut circuit = Circuit::new(self.cells.len());
116        for (index, cell) in self.cells.iter().enumerate() {
117            let wire = Wire::from_index(index);
118            if cell.spec.exact_bit {
119                circuit.assume_bit(wire);
120                circuit.assume_grid(wire, 1.0);
121            } else {
122                circuit.assume_range(wire, cell.spec.range.min, cell.spec.range.max);
123            }
124            if let Some(grid) = cell.spec.grid {
125                circuit.assume_grid(wire, grid);
126            }
127        }
128        circuit
129    }
130
131    /// Pair a state cell with the wire that holds its next value.
132    pub fn next(&mut self, cell: Wire, value: Wire) {
133        let declared = &mut self.cells[cell.index()];
134        assert_eq!(declared.spec.policy, Policy::State, "{} is not a state cell", declared.name);
135        assert!(declared.next.is_none(), "{} already has a next value", declared.name);
136        declared.next = Some(value);
137    }
138
139    /// Check that every state cell has a next value.
140    pub fn finish(&self) {
141        for cell in &self.cells {
142            if cell.spec.policy == Policy::State {
143                assert!(cell.next.is_some(), "state cell {} has no next value", cell.name);
144            }
145        }
146    }
147
148    /// The wires the host needs after an evaluation: every state cell's next value.
149    pub fn next_wires(&self) -> Vec<Wire> {
150        self.cells.iter().filter_map(|cell| cell.next).collect()
151    }
152
153    pub fn initial(&self) -> Vec<f32> {
154        self.cells.iter().map(|cell| cell.spec.initial as f32).collect()
155    }
156
157    /// Check every cell's range, exact bit and grid.
158    pub fn validate(&self, source: &[f32]) -> Result<(), String> {
159        if source.len() != self.cells.len() {
160            return Err(format!("source table has {} cells, expected {}", source.len(), self.cells.len()));
161        }
162        for (cell, &value) in self.cells.iter().zip(source) {
163            let value = value as f64;
164            let range = cell.spec.range;
165            if !(value >= range.min && value <= range.max) {
166                return Err(format!("{} is {value}, outside {}..{}", cell.name, range.min, range.max));
167            }
168            if cell.spec.exact_bit && value != 0.0 && value != 1.0 {
169                return Err(format!("{} must be exactly 0 or 1, got {value}", cell.name));
170            }
171            if let Some(grid) = cell.spec.grid
172                && (value / grid).fract() != 0.0
173            {
174                return Err(format!("{} must be a multiple of {grid}, got {value}", cell.name));
175            }
176        }
177        Ok(())
178    }
179
180    /// The source table for the next evaluation: state cells take their next
181    /// value, event cells reset to 0, and the rest keep their value.
182    pub fn advance(&self, source: &[f32], values: &[f32]) -> Vec<f32> {
183        self.cells
184            .iter()
185            .zip(source)
186            .map(|(cell, &value)| match (cell.spec.policy, cell.next) {
187                (Policy::State, Some(next)) => values[next.index()],
188                (Policy::Event, _) => 0.0,
189                _ => value,
190            })
191            .collect()
192    }
193}