Skip to main content

Crate handwired_rs

Crate handwired_rs 

Source
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].
timing
Host timing for springs whose step matrix the host writes into source cells, ported from handwired’s host-timing.ts.

Structs§

AnimatedObject
AnimatedObjectOptions
AnimatedSpring
One spring of an emit_animated_object.
BackpropOptions
BatchExecutor
Reusable buffers for running one program on batches of records.
CandidateTable
CellSpec
Circuit
CompareOptions
Comparison
A comparison bit’s test: d > 0, d >= 0 or d == 0 for the difference d = bias + Σ coefficient × operand.
CompileOptions
GatherCompute
Resolves a one-hot group’s selected position once per evaluation, at the first layer that reads the group: Σ position × bit and Σ bit, exact for 0/1 bits.
GatherRead
A lowered masked sum: the row’s bias plus the selected candidate, which is first multiplied by the group’s sum of bits when the group allows all zeros.
IndexRead
IndexReadRow
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.
OneHotGroup
Exact bits of which exactly one is 1, or, with allow_all_zero, at most one. A promise from the caller, like a range, checkable with Circuit::validate_one_hot_groups. Compilation lowers a sum of products of the group’s members with candidate values to a gather: a read of the selected candidate.
Operation
Range
The range of values a wire may hold. An open side is infinite.
SourceTable
SpringState
A spring’s next state after four explicit semi-implicit Euler steps, with every constant a wire.
TensorExecutor
Reusable buffers for running one program many times.
TensorLayer
TensorProgram
Wire
The position of a source cell or of an operation’s result.

Enums§

BooleanGradient
Gradient rule at a boolean atom, which is a step function.
ComparisonGradient
Gradient rule for the bit an emit_compare returns, a step function of its operands.
Error
IndexGradient
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
OutOfRange
What an index read returns when floor(index) is outside the table.
Policy
What happens to a cell around an evaluation.
Relation
RoundingPointKind
Why an operation’s FP32 evaluation, term order or operand wires carry meaning. See handwired’s docs/execution-model.md, “Rounding points”.

Constants§

LANES
Records evaluated together.

Functions§

brent_kung_prefix
Brent-Kung (1982): an upward pass combines neighbors, then pairs of pairs, and so on, which completes the prefixes of the first 2, 4, 8, … items. A downward pass then completes every other prefix by combining one completed prefix with one smaller block. For k items, a power of two and at least 4, that’s 2k - 2 - log2(k) combines, and a longest chain of 2 × log2(k) - 2.
constant
emit_and
bits.all(): relu(sum - (n - 1)), exact for exact bits.
emit_animated_object
Springs of one animated object, e.g. a window’s x, y, width, height and scale, stepped by their matrices and settled together with one exact bit. Each spring stores its displacement from its destination rather than a position, so FP32 rounds the state at the size of the displacement, finely near rest even far from the origin. When the object settles or snaps, every displacement and velocity becomes 0, up to the sign of zero.
emit_apply_spring_matrix
One spring’s displacement from its destination and its velocity after every step the matrix describes, e.g. all of one evaluation’s 4 ms steps at once. The matrix cells need finite declared ranges, e.g. ±spring_matrix_bound, so a host that writes an overflowing matrix fails the source range check. Each product goes through multiply, so the backward pass differentiates whole products. Cost: 14 operations in 3 layers.
emit_argmax
The highest of scores, each declared within 0..1, as a one-hot selection and the winning score. unit must be declared exactly 1. Up a balanced tree, each pair compares its scores with a boolean atom and keeps the larger one; down, the unit follows the comparison bits, so exactly one output is 1 and the selection is declared as a one-hot group. Exact ties keep the left score, and so does a right score larger by less than about 9e-8 (for some scores up to 2.4e-7), a band that isn’t transitive, so the tree can pick differently from a loop. Needs an executor that keeps FP32 rounding at the atoms. O(n) operations in O(log n) layers.
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 integer D = (a - b) / grid in 2 or 3 operations; otherwise a sign cascade lifts every nonzero f(a - b), down to 2^-149, to at least 1.
emit_compare_with
emit_first_active
The first of candidates that is 1, gated by active, as a selection: up a balanced tree, each pair keeps the emit_maximum of its masses; down, a node’s route r gives r × left maximum to its left child and the rest to its right child, starting from active × root maximum. An exact one-hot, or all zeros, when the gate and every candidate are exact bits; fractional candidates split the route, e.g. [0.5, 0] sends 0.25 to each output. The selection is declared as a one-hot group (allowing all zeros) only when the gate and every candidate are proven bits (see is_proven_bit). O(n) operations in O(log n) layers.
emit_maximum
max(a, b) rebuilt through a rounded difference: b + relu(a - b). Not always exactly one of the operands; see emit_maximum_exact.
emit_maximum_exact
max(a, b) as exactly one of the operands. Ties return a.
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)). See emit_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 return a. Needs finite declared ranges, like emit_compare.
emit_not
!bit for 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 : off for a gate of exactly 0 or 1. Both sides always compute.
emit_spring
Four steps of velocity += (-stiffness × (position - destination) - damping × velocity) × timestep; position += velocity × timestep, then a soft settle: active is clamp01(relu(|position - destination| - settle_distance) × distance_scale + relu(|velocity| - settle_speed) × speed_scale), the position moves to the destination as active goes to 0, and the velocity scales by it. unit must hold 1. emit_animated_object is the exact, matrix-stepped form.
event
input
input_bit
is_proven_bit
Whether a wire provably holds exactly 0 or 1: its declared range is a single 0 or 1, a boolean atom made it, emit_compare returned it, it belongs to a declared one-hot group, or it’s an index read whose reachable candidates are all proven bits (a side that reads zero adds +0, also a bit). A 0..1 range alone isn’t enough.
sklansky_prefix
Sklansky (1960): for blocks of 2, 4, 8, … items, combine the last prefix of the block’s left half into every prefix of its right half. For k items, a power of two, that’s (k / 2) × log2(k) combines, and a longest chain of log2(k).
state
state_bit

Type Aliases§

SpringMatrixWires
The 4 cells of a spring matrix that the host writes before each evaluation, e.g. spring_matrix_power(spring_step_matrix(constants, 4.0), steps) from crate::timing: displacement' = m[0] × displacement + m[1] × velocity, velocity' = m[2] × displacement + m[3] × velocity.
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.

Attribute Macros§

handwired