Expand description
Exact programs as neural nets: a Rust port of the core of handwired.
A program is a fixed list of f32 operations, mostly weighted sums and products with a ReLU, written with builder code that runs once. Because it uses only these primitives, it compiles into layers of sparse matrix multiplications and stays differentiable, while computing exactly what the equivalent ordinary code computes.
use handwired_rs::{emit_compare, Circuit, Relation};
// const result = (a > 4) ? 10 : 12, for an integer a within 0..100.
let mut circuit = Circuit::new(1);
let a = circuit.source(0);
circuit.assume_range(a, 0.0, 100.0);
circuit.assume_grid(a, 1.0);
let four = circuit.constant(4.0);
let above_four = emit_compare(&mut circuit, a, Relation::Greater, four);
let result = circuit.affine(above_four * -2.0 + 12.0);
for value in 0..=100 {
let values = circuit.evaluate_reference(&[value as f32]).unwrap();
assert_eq!(values[result.index()], if value > 4 { 10.0 } else { 12.0 });
}Builder methods panic when a contract is violated, e.g. a comparison on an
operand without a finite declared range, since that is a bug in the
program being built. Evaluation returns Error for bad input data.
Modules§
- dsl
- The runtime half of
#[handwired].
Structs§
- Backprop
Options - Candidate
Table - Cell
Spec - Circuit
- Compare
Options - Index
Read - Index
Read Row - Lin
- An affine form:
bias + Σ coefficient × wire, with terms in the order they were written. Ordinary builder methods sort and merge terms; the rounding-preserving ones, e.g.Circuit::round_affine, keep this order. - Operation
- Range
- The range of values a wire may hold. An open side is infinite.
- Source
Table - Tensor
Executor - Reusable buffers for running one program many times.
- Tensor
Layer - Tensor
Program - Wire
- The position of a source cell or of an operation’s result.
Enums§
- Boolean
Gradient - Gradient rule at a boolean atom, which is a step function.
- Error
- Index
Gradient - Gradient rule for the index of an index read, which is a step function.
- Macro
- The builder operation a wire came from, so the backward pass can differentiate the whole product or select instead of its ReLUs one by one.
- OpKind
- OutOf
Range - What an index read returns when floor(index) is outside the table.
- Policy
- What happens to a cell around an evaluation.
- Relation
- Rounding
Point Kind - Why an operation’s FP32 evaluation, term order or operand wires carry meaning. See handwired’s docs/execution-model.md, “Rounding points”.
Functions§
- constant
- emit_
and bits.all():relu(sum - (n - 1)), exact for exact bits.- emit_
clamp_ exact min(max(value, minimum), maximum), as exactly one of the operands.- emit_
compare [a relation b]as an exact bit, equal to the native f32 comparison for every pair of values within the declared ranges. Both operands need finite declared ranges. When both lie on grids, the bit is built from the integerD = (a - b) / gridin 2 or 3 operations; otherwise a sign cascade lifts every nonzerof(a - b), down to 2^-149, to at least 1.- emit_
compare_ with - emit_
maximum max(a, b)rebuilt through a rounded difference:b + relu(a - b). Not always exactly one of the operands; seeemit_maximum_exact.- emit_
maximum_ exact max(a, b)as exactly one of the operands. Ties returna.- emit_
minimum min(a, b)rebuilt through a rounded difference:a - relu(a - b), e.g. 0.29998779 for min(f(1000.37), f(0.3)). Seeemit_minimum_exact.- emit_
minimum_ exact min(a, b)as exactly one of the operands, e.g. f(0.3) for min(f(1000.37), f(0.3)). Ties returna. Needs finite declared ranges, likeemit_compare.- emit_
not !bitfor an exact bit.- emit_or
bits.any():1 - relu(1 - sum), exact for exact bits.- emit_
relu max(0, value).- emit_
round Math.round(x)for |x| below 2^22: FP32 values within 2^23..2^24 are whole numbers, so adding and then subtracting 2^23 + 2^22 rounds (ties to even). Both steps are marked rounding points.- emit_
select gate ? on : offfor a gate of exactly 0 or 1. Both sides always compute.- event
- input
- input_
bit - state
- state_
bit
Type Aliases§
- Term
- One term of an affine form: a wire and its coefficient. Coefficients are kept in f64 like handwired’s, and rounded to f32 when evaluated.