feat(aura-engine): sweep named-binding — zip_params + SweepFamily.named_params (C12 #57)
Thread a derived named view of a sweep point so consumers stop hand-zipping param_space() names onto the bare &[Scalar]. The free function zip_params(space, point) -> Vec<(String, Scalar)> in aura-core is the one shared projection; GridSpace retains the ParamSpec list it already receives in new() (it was validated then discarded); SweepFamily carries it (stamped once in sweep_with_threads) and exposes named_params(i). The three CLI hand-zip sites collapse to one zip_params call each. The run-closure signature `Fn(&[Scalar]) -> RunReport` is byte-unchanged — the named view is a derived projection (C23: slot is identity, name is derived), not a closure-currency change — so #52's RandomSpace plugs into the same sweep() execution layer with zero signature reconciliation (#71 firewall held). sim_optimal_manifest now takes typed Vec<(String, Scalar)> and does the lossy f64 collapse internally (one place; the manifest's f64-precursor field owns its own lossiness — the typed-manifest upgrade stays the deferred typed-param-space item, a non-goal here). All eight call sites migrated: three sweep closures pass zip_params, five hand-listing callers (run_sample, run_sample_real, mc_family, run_macd, run_sample_seeded) pass Scalar::F64 literals. Behaviour-preserving: the collapse output is identical to the old per-site zip, so aura sweep / walkforward / run output is byte-identical; SweepFamily.space threaded into the aura-registry optimize test literal. Verified by the orchestrator: cargo test --workspace green (4 new tests: zip_params x2, sweep_family_carries_param_space, family_named_params_round_trips), cargo clippy --workspace --all-targets -D warnings clean. (rust-analyzer emitted stale mid-edit diagnostics; the real cargo build/test is clean.) refs #57
This commit is contained in:
@@ -39,5 +39,7 @@ pub use any::AnyColumn;
|
||||
pub use column::{Column, Window};
|
||||
pub use ctx::Ctx;
|
||||
pub use error::KindMismatch;
|
||||
pub use node::{FieldSpec, Firing, Node, NodeSchema, ParamSpec, PortSpec, PrimitiveBuilder};
|
||||
pub use node::{
|
||||
zip_params, FieldSpec, Firing, Node, NodeSchema, ParamSpec, PortSpec, PrimitiveBuilder,
|
||||
};
|
||||
pub use scalar::{Scalar, ScalarKind, Timestamp};
|
||||
|
||||
@@ -70,6 +70,14 @@ pub struct ParamSpec {
|
||||
pub kind: ScalarKind,
|
||||
}
|
||||
|
||||
/// Pair each param-space name with the co-indexed positional value — the
|
||||
/// inverse of positional binding (C23): names are a derived view, not identity.
|
||||
/// `space` and `point` are co-indexed in `param_space()` slot order; a length
|
||||
/// mismatch (contract violation) truncates to the shorter, never panics.
|
||||
pub fn zip_params(space: &[ParamSpec], point: &[Scalar]) -> Vec<(String, Scalar)> {
|
||||
space.iter().zip(point).map(|(ps, v)| (ps.name.clone(), *v)).collect()
|
||||
}
|
||||
|
||||
/// One param bound to a structural constant by [`PrimitiveBuilder::bind`] — a
|
||||
/// render/debug surface only (C23), dropped at lowering. `pos` is the slot's
|
||||
/// position in the node's ORIGINAL (pre-bind) param list, so the model serializer
|
||||
@@ -522,3 +530,37 @@ mod tests {
|
||||
let _ = b.bind("length", Scalar::F64(2.0)); // F64 value for an I64 slot
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod zip_params_tests {
|
||||
use super::{zip_params, ParamSpec};
|
||||
use crate::{Scalar, ScalarKind};
|
||||
|
||||
#[test]
|
||||
fn zip_params_pairs_names_with_values_in_slot_order() {
|
||||
let space = vec![
|
||||
ParamSpec { name: "fast".into(), kind: ScalarKind::I64 },
|
||||
ParamSpec { name: "scale".into(), kind: ScalarKind::F64 },
|
||||
];
|
||||
let point = vec![Scalar::I64(2), Scalar::F64(0.5)];
|
||||
assert_eq!(
|
||||
zip_params(&space, &point),
|
||||
vec![
|
||||
("fast".to_string(), Scalar::I64(2)),
|
||||
("scale".to_string(), Scalar::F64(0.5)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zip_params_matches_hand_zip() {
|
||||
let space = vec![
|
||||
ParamSpec { name: "a".into(), kind: ScalarKind::I64 },
|
||||
ParamSpec { name: "b".into(), kind: ScalarKind::I64 },
|
||||
];
|
||||
let point = vec![Scalar::I64(7), Scalar::I64(9)];
|
||||
let hand: Vec<(String, Scalar)> =
|
||||
space.iter().zip(&point).map(|(ps, v)| (ps.name.clone(), *v)).collect();
|
||||
assert_eq!(zip_params(&space, &point), hand);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user