test(aura-engine): mirror param_space against compile() under nesting (#34)
The cycle-0015 E2E guard param_space_mirrors_compiled_flat_node_param_order pins the load-bearing C23/#31 invariant — Blueprint::param_space() slot order mirrors compile()'s flat-node param order kind-by-slot — but only on the single-level composite_sma_cross_harness. collect_params (blueprint.rs:217) duplicates lower_items' depth-first traversal rather than sharing it (kept a parallel read-only projection so compile/inline_composite stay untouched and the compilat bit-identical, C9/C23), so the two orders must stay in lockstep forever. No mirror test ever compiled a composite-inside-a-composite: a future inliner reorder that desynced the projections only under nesting could pass the single-level mirror and the isolated nested param_space order test, yet break the slot-by-slot premise #31's binding rests on. Adds param_space_mirrors_compiled_flat_node_param_order_under_nesting: compiles a strategy -> { fast_slow -> [Sma, Sma, Sub], LinComb } nest and asserts param_space() equals the compiled flat-node param order kind-by-slot, pinning the concrete [I64, I64, F64, F64] shape. Green immediately — coverage-hardening, no desync in the current tree. Test-only; collect_params/lower_items/compile untouched. Verified: cargo test -p aura-engine green (55 tests), clippy --all-targets -D warnings clean. closes #34
This commit is contained in:
@@ -872,6 +872,69 @@ mod tests {
|
||||
assert_eq!(space[2].kind, ScalarKind::F64);
|
||||
}
|
||||
|
||||
/// E2E (issue #34): the C23/#31 mirror invariant *under composite nesting*.
|
||||
/// `param_space()` (via `collect_params`) duplicates `lower_items`' depth-
|
||||
/// first traversal rather than sharing it, so the two orders must stay in
|
||||
/// lockstep. The single-level mirror test
|
||||
/// (`param_space_mirrors_compiled_flat_node_param_order`) never compiles a
|
||||
/// composite whose interior holds *another* composite; the nested
|
||||
/// `param_space` order test never compiles. This closes that gap: it
|
||||
/// compiles a `strategy → { fast_slow → [Sma, Sma, Sub], LinComb }` nest and
|
||||
/// asserts the aggregated space lines up, kind-by-slot, with the compiled
|
||||
/// flat-node param order — so a future inliner reorder that desynced the two
|
||||
/// projections *only under nesting* would fail here, not slip through.
|
||||
#[test]
|
||||
fn param_space_mirrors_compiled_flat_node_param_order_under_nesting() {
|
||||
use aura_std::{LinComb, Sma, Sub};
|
||||
// inner composite "fast_slow": two SMAs + a Sub (same nest as the
|
||||
// isolated path-qualification test above)
|
||||
let fast_slow = Composite::new(
|
||||
"fast_slow",
|
||||
vec![Sma::new(2).into(), Sma::new(4).into(), Sub::new().into()],
|
||||
vec![
|
||||
Edge { from: 0, to: 2, slot: 0, from_field: 0 },
|
||||
Edge { from: 1, to: 2, slot: 1, from_field: 0 },
|
||||
],
|
||||
vec![vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }]],
|
||||
OutPort { node: 2, field: 0 },
|
||||
);
|
||||
// outer composite "strategy": the inner composite + a LinComb([1,-1])
|
||||
let strategy = Composite::new(
|
||||
"strategy",
|
||||
vec![BlueprintNode::Composite(fast_slow), LinComb::new(vec![1.0, -1.0]).into()],
|
||||
vec![],
|
||||
vec![vec![Target { node: 0, slot: 0 }]],
|
||||
OutPort { node: 0, field: 0 },
|
||||
);
|
||||
let bp = Blueprint::new(vec![BlueprintNode::Composite(strategy)], vec![], vec![]);
|
||||
|
||||
// the aggregated, path-qualified projection (borrows; take it first since
|
||||
// compile() consumes self — same ordering as the single-level mirror test)
|
||||
let space = bp.param_space();
|
||||
|
||||
// the same blueprint, compiled to its flat node array; each flat node's
|
||||
// own declared params, concatenated in flat-node order
|
||||
let (flat_nodes, _sources, _edges) = bp.compile().expect("nested composite compiles");
|
||||
let from_compilat: Vec<ParamSpec> =
|
||||
flat_nodes.iter().flat_map(|n| n.schema().params).collect();
|
||||
|
||||
// same count, same per-slot kind, same order — the nested projection
|
||||
// mirrors the compilation (names differ: param_space path-qualifies, the
|
||||
// raw node does not, so compare on the load-bearing axis, kind-by-slot)
|
||||
assert_eq!(space.len(), from_compilat.len(), "param count must match the compilat");
|
||||
assert_eq!(
|
||||
space.iter().map(|p| p.kind).collect::<Vec<_>>(),
|
||||
from_compilat.iter().map(|p| p.kind).collect::<Vec<_>>(),
|
||||
"per-slot param kinds must line up with the compiled flat-node order, under nesting",
|
||||
);
|
||||
// pin the concrete shape: two Sma lengths (I64), Sub none, two LinComb
|
||||
// weights (F64)
|
||||
assert_eq!(
|
||||
space.iter().map(|p| p.kind).collect::<Vec<_>>(),
|
||||
[ScalarKind::I64, ScalarKind::I64, ScalarKind::F64, ScalarKind::F64],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_leaf_params_are_unqualified() {
|
||||
use aura_std::Sma;
|
||||
|
||||
Reference in New Issue
Block a user