Files
Aura/crates/aura-engine/tests/cumsum_e2e.rs
T
claude c8dc57e2aa feat(std): CumSum and When cells; roster complete at 33
Second half of the #281 clock-enable set. CumSum is the generic
run-length accumulator with O(1) Neumaier-compensated state — the
branching Neumaier variant, not Sma's plain Kahan, so it stays exact
when a large running total absorbs a tiny sample (the standalone
accumulator has no windowed-magnitude guarantee; module doc explains
why the helper is not reused). When is the clock-enable driver: forwards
value iff gate, else a quiet cycle (eval->None), so every stateful
reducer composes gated unmodified — SMA(When(x,g),N) is the gated SMA.

Rosters CumSum and When (both count pins 31->33) and ships two engine
E2E fixtures: When forwarding/quiet-downstream through the real
GraphBuilder->compile->run seam, and a quiet When leg suppressing a
whole Resample Barrier(0) group (the deliberate stall semantics).

refs #281
2026-07-17 20:39:56 +02:00

55 lines
2.6 KiB
Rust

//! End-to-end coverage for the `CumSum` standard cell (#281): the generic
//! running-total accumulator.
//!
//! The in-module tests in `aura-std/src/cumsum.rs` drive `eval` directly —
//! they never go through a `PrimitiveBuilder` schema, a `GraphBuilder`-resolved
//! wiring, or a compiled+bootstrapped `FlatGraph`. That is the real seam a
//! data-authored op-script or a Rust builder actually exercises (C24); this
//! file proves `CumSum` is reachable from the public `aura_engine`/`aura_std`
//! construction surface, wires cleanly by declared port name/kind, and
//! accumulates the right running total once actually driven cycle-by-cycle
//! through a real `Harness::run` — the same bar the Sign/Select pair set in
//! `select_sign_e2e.rs`.
use std::sync::mpsc;
use aura_core::{Firing, Scalar, ScalarKind, Timestamp};
use aura_engine::{GraphBuilder, VecSource};
use aura_std::{CumSum, Recorder};
/// A deterministic tick stream, no timestamps/randomness beyond a fixed
/// literal sequence.
fn ticks(values: &[f64]) -> Vec<(Timestamp, Scalar)> {
values.iter().enumerate().map(|(i, &v)| (Timestamp(i as i64 + 1), Scalar::f64(v))).collect()
}
/// Property: **`CumSum` correctly registers its `PrimitiveBuilder` schema
/// (one `f64` input, one `f64` output) and dispatches through a
/// compiled+bootstrapped `FlatGraph`, accumulating the running total across
/// real engine cycles** — not merely via a direct `eval` call (the in-module
/// `cumsum.rs` unit tests' seam). A schema/wiring regression (wrong port
/// kind, wrong output arity) would fail at `GraphBuilder::build` or
/// `compile_with_params`, before a single cycle runs; a dispatch or
/// state-carry regression would show up in the recorded running totals below.
#[test]
fn cumsum_dispatches_through_a_compiled_graph_and_accumulates() {
let (tx, rx) = mpsc::channel();
let mut g = GraphBuilder::new("root");
let series = g.source_role("series", ScalarKind::F64);
let cumsum = g.add(CumSum::builder());
let rec = g.add(Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx));
g.feed(series, [cumsum.input("series")]);
g.connect(cumsum.output("value"), rec.input("col[0]"));
let mut h = g.build().expect("resolves by name").bootstrap_with_params(vec![]).expect("bootstraps");
h.run(vec![Box::new(VecSource::new(ticks(&[2.0, -0.5, 3.5, 0.0])))]);
let trace: Vec<f64> = rx.try_iter().map(|(_, row)| row[0].as_f64()).collect();
assert_eq!(
trace,
vec![2.0, 1.5, 5.0, 5.0],
"running total accumulated across real engine cycles, dispatched through a compiled FlatGraph"
);
}