Skip to main content

handwired_rs/
lib.rs

1//! Exact programs as neural nets: a Rust port of the core of
2//! [handwired](https://github.com/chenglou/handwired).
3//!
4//! A program is a fixed list of f32 operations, mostly weighted sums and
5//! products with a ReLU, written with builder code that runs once. Because it
6//! uses only these primitives, it compiles into layers of sparse matrix
7//! multiplications and stays differentiable, while computing exactly what the
8//! equivalent ordinary code computes.
9//!
10//! ```
11//! use handwired_rs::{emit_compare, Circuit, Relation};
12//!
13//! // const result = (a > 4) ? 10 : 12, for an integer a within 0..100.
14//! let mut circuit = Circuit::new(1);
15//! let a = circuit.source(0);
16//! circuit.assume_range(a, 0.0, 100.0);
17//! circuit.assume_grid(a, 1.0);
18//! let four = circuit.constant(4.0);
19//! let above_four = emit_compare(&mut circuit, a, Relation::Greater, four);
20//! let result = circuit.affine(above_four * -2.0 + 12.0);
21//!
22//! for value in 0..=100 {
23//!     let values = circuit.evaluate_reference(&[value as f32]).unwrap();
24//!     assert_eq!(values[result.index()], if value > 4 { 10.0 } else { 12.0 });
25//! }
26//! ```
27//!
28//! Builder methods panic when a contract is violated, e.g. a comparison on an
29//! operand without a finite declared range, since that is a bug in the
30//! program being built. Evaluation returns [`Error`] for bad input data.
31
32mod batch;
33mod cells;
34mod circuit;
35pub mod dsl;
36mod emit;
37mod gather;
38mod inline;
39mod numeric;
40mod prefix;
41mod springs;
42mod tensor;
43pub mod timing;
44mod wire;
45
46pub use batch::{BatchExecutor, LANES};
47pub use cells::{CellSpec, Policy, SourceTable, constant, event, input, input_bit, state, state_bit};
48pub use circuit::{
49    BackpropOptions, BooleanGradient, CandidateTable, Circuit, Comparison, ComparisonGradient, IndexGradient,
50    IndexRead, Macro, OneHotGroup, OpKind, Operation, OutOfRange, RoundingPointKind,
51};
52pub use emit::{
53    CompareOptions, Relation, emit_and, emit_argmax, emit_clamp_exact, emit_compare, emit_compare_with,
54    emit_first_active, emit_maximum, emit_maximum_exact, emit_minimum, emit_minimum_exact, emit_not, emit_or,
55    emit_relu, emit_round, emit_select, is_proven_bit,
56};
57pub use handwired_rs_macros::handwired;
58pub use numeric::Range;
59pub use prefix::{brent_kung_prefix, sklansky_prefix};
60pub use springs::{
61    AnimatedObject, AnimatedObjectOptions, AnimatedSpring, SpringMatrixWires, SpringState, emit_animated_object,
62    emit_apply_spring_matrix, emit_spring,
63};
64pub use tensor::{CompileOptions, GatherCompute, GatherRead, IndexReadRow, TensorExecutor, TensorLayer, TensorProgram};
65pub use wire::{Lin, Term, Wire};
66
67#[derive(Clone, Debug, PartialEq)]
68pub enum Error {
69    SourceLength {
70        expected: usize,
71        actual: usize,
72    },
73    SourceOutOfRange {
74        wire: Wire,
75        value: f64,
76        range: Range,
77    },
78    /// A nonzero gradient reached a boolean atom without `BooleanGradient::StraightThrough`.
79    BooleanGradient {
80        wire: Wire,
81    },
82    /// A nonzero gradient reached an index read without an `IndexGradient` rule.
83    IndexGradient {
84        wire: Wire,
85    },
86}
87
88impl std::fmt::Display for Error {
89    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90        match self {
91            Error::SourceLength { expected, actual } => {
92                write!(formatter, "source table has {actual} cells, expected {expected}")
93            }
94            Error::SourceOutOfRange { wire, value, range } => write!(
95                formatter,
96                "source wire {} is {value}, outside its declared range {}..{}",
97                wire.index(),
98                range.min,
99                range.max
100            ),
101            Error::BooleanGradient { wire } => {
102                write!(formatter, "cannot backpropagate through the boolean atom at wire {}", wire.index())
103            }
104            Error::IndexGradient { wire } => {
105                write!(formatter, "cannot backpropagate into the index of the index read at wire {}", wire.index())
106            }
107        }
108    }
109}
110
111impl std::error::Error for Error {}