mirror of
https://github.com/ruvnet/RuView
synced 2026-07-22 17:23:19 +00:00
1d12e8831a
* refactor(train): ADR-155 M2 §8 — de-magic train non-tch tuning constants + boundary tests Lift bare numeric literals used as thresholds / guard epsilons in the non-tch (host-verifiable) train surface into named, documented consts and pin each set with a *_consts_unchanged_from_literals test. Values are bit-identical to the prior inline literals — cleanup, no behaviour change. De-magicked (const + pin test): - metrics_core.rs: VISIBILITY_THRESHOLD (0.5), MIN_REFERENCE_EXTENT (1e-6), OKS_FALLBACK_SIGMA (0.07) - ruview_metrics.rs: NUM_KEYPOINTS (17), VISIBILITY_THRESHOLD (0.5), PCK_THRESHOLD (0.2), MIN_BBOX_DIAG (1e-3), MIN_DURATION_MINUTES (1e-6) - subcarrier.rs: SPARSE_BASIS_SIGMA (0.15), SPARSE_BASIS_THRESHOLD (1e-4), SPARSE_REGULARIZATION_LAMBDA (0.1), SPARSE_COO_PRUNE_EPS (1e-8), SPARSE_SOLVER_TOL (1e-5 f64), SPARSE_SOLVER_MAX_ITERS (500) - eval.rs: MIN_POSITIVE_MPJPE (1e-10) - domain.rs: LAYER_NORM_EPS (1e-5) - virtual_aug.rs: BOX_MULLER_U1_FLOOR (1e-10), MIN_ROOM_SCALE (1e-10) Boundary / characterization tests (pin CURRENT behaviour): - visibility_threshold_boundary_is_inclusive (>= 0.5 at the edge) - degenerate_extent_below_floor_is_unscoreable ((0,0,0.0)/0.0, not perfect) - tracking_zero_duration_does_not_divide_by_zero - oks_short_array_is_bounded_at_keypoint_count (16 rows, no panic) - compute_interp_weights_single_target_is_index_zero (target_sc==1) - sparse_interp_single_target_is_finite - domain_gap_infinite_when_in_domain_perfect_but_cross_nonzero - domain_gap_unity_when_everything_perfect - augment_frame_zero_room_scale_passes_amplitude_finite Doc-only (no behaviour change): - rapid_adapt.rs: correct module-doc O(eps) -> O(eps^2) for central differences - geometry.rs: add # Panics to DeepSets::encode (documents existing assert!) train --no-default-features: 191 lib (was 176), 303 total (was 288), 0 failed. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(nn): ADR-155 M2 §3 — pure-Rust LinearHead::try_new input guard + de-magic softplus threshold ADR-155 §3 found rf_encoder.rs has no adversarial checkpoint-deserialization assert — its assert_eq!s in LinearHead::new are construction-time API contracts on programmer-supplied vectors. This adds the honest, in-scope improvement the M2 task allows: a pure-Rust *fallible* constructor so weights from an untrusted / deserialized checkpoint can be shape-validated without panicking. - Add RfHeadError (WeightShape / BiasShape / VarWeightShape) + Display + Error. - Add LinearHead::try_new returning Result<Self, RfHeadError>; on success the head is byte-identical to LinearHead::new. new() is unchanged (still asserts; now documents # Panics and points to try_new) — no behaviour change for existing callers. - De-magic softplus's bare 20.0 overflow threshold into SOFTPLUS_LINEAR_THRESHOLD (value unchanged) + pin test. Tests: try_new_accepts_valid_and_rejects_each_bad_shape (valid == new forward; each bad shape → typed error), softplus_threshold_unchanged_from_literal. nn --no-default-features lib: 37 passed (was 35), 0 failed. Co-Authored-By: claude-flow <ruv@ruv.net> * perf(nn): ADR-155 M2 §4 — native-conv bench-first → MEASURED-INCONCLUSIVE (no perf change shipped) The §8 "native-conv naive-loop rewrite" backlog item: DensePoseHead:: apply_conv_layer is a pure-Rust 6-nested-loop conv (benchable on this host, not tch/ort-gated). Bench-first per the §0 PROOF discipline. - Add committed criterion bench benches/native_conv_bench.rs measuring forward() through the naive conv on representative single-layer configs (--no-default- features; no ort download). - Prototyped a bit-identical range-clamped variant (hoist the per-tap in-bounds branch by pre-clamping kh/kw ranges; same ic→kh→kw MAC order ⇒ bit-identical). MEASURED before/after on this host: ~35% faster on padding-heavy small-channel maps (4.40→2.84 ms) but a ~3% *regression* on channel-heavy maps (11.09→11.48 ms), all inside a ±20% run-to-run noise floor. Verdict: INCONCLUSIVE — the benefit is not robustly positive, so the rewrite is NOT shipped and NOT a fabricated speedup. Reverted to the naive loop; honestly deferred (ADR-155 §8). - Add native_conv_matches_reference: a hand-computed characterization anchor (1×1 = scalar MAC; same-padded 3×3 ones = truncated-window sums 9/6/4) pinning CURRENT conv behaviour for any future rewrite. nn --no-default-features lib: 38 passed (was 37), 0 failed. No behaviour change. Co-Authored-By: claude-flow <ruv@ruv.net> * docs(adr-155): M2 §8.2 — enumerated host-verifiable P3 backlog clearance + CHANGELOG Replace the §8 bulk "~40 lower-severity findings" line with the real, enumerated M2 resolution (§8.2): 7 de-magicked (const + pin == prior literal), 9 boundary tests, 1 input guard (rf_encoder try_new), 2 doc-only, 1 perf bench-first MEASURED-INCONCLUSIVE (not shipped). Mark native-conv + rf_encoder RESOLVED; state which §8 items stay data-gated (GraphPose-Fi/INT4/CSI-JEPA) or tch-gated (proof/trainer/model panic sites, metrics *_v2 dead code) and ONNX read-lock upstream-gated — blocked, not dropped. Declare the non-tch-verifiable subset of §8 cleared. Validation: train --no-default-features 303 passed (was 288); nn lib 38 (was 35); workspace --no-default-features 3,293 passed, 0 failed; Python proof VERDICT PASS, hash f8e76f21…46f7a UNCHANGED bit-exact. Co-Authored-By: claude-flow <ruv@ruv.net>
80 lines
3.2 KiB
Rust
80 lines
3.2 KiB
Rust
//! ADR-155 M2 §4 — native (pure-Rust) DensePose conv benchmark.
|
|
//!
|
|
//! `DensePoseHead::apply_conv_layer` is a pure-Rust naive 6-nested-loop
|
|
//! convolution (the §8 "native-conv naive-loop" backlog item). This bench
|
|
//! measures `forward()` (which runs the shared-conv + segmentation + UV conv
|
|
//! stacks through that naive loop) on a representative single-layer config so a
|
|
//! perf claim can be made (or refused) with a MEASURED before/after — never a
|
|
//! fabricated number.
|
|
//!
|
|
//! Reproduce:
|
|
//! cargo bench -p wifi-densepose-nn --no-default-features --bench native_conv_bench
|
|
//!
|
|
//! The bench is `--no-default-features` (no `onnx`/`ort` download needed): the
|
|
//! conv path is pure-Rust and benchable on any host.
|
|
|
|
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
|
|
use ndarray::{Array1, Array4};
|
|
use std::hint::black_box;
|
|
use wifi_densepose_nn::densepose::{ConvLayerWeights, DensePoseWeights};
|
|
use wifi_densepose_nn::{DensePoseConfig, DensePoseHead, Tensor};
|
|
|
|
/// Build a single same-padding conv layer `in_ch -> out_ch`, kernel `k`, with a
|
|
/// bias (no batch-norm) — deterministic, small, representative of one stage.
|
|
fn conv_layer(in_ch: usize, out_ch: usize, k: usize) -> ConvLayerWeights {
|
|
let weight = Array4::from_shape_fn((out_ch, in_ch, k, k), |(o, i, kh, kw)| {
|
|
// Deterministic, bounded weights.
|
|
((o + i + kh + kw) as f32 * 0.013).sin()
|
|
});
|
|
ConvLayerWeights {
|
|
weight,
|
|
bias: Some(Array1::from_shape_fn(out_ch, |o| o as f32 * 0.01)),
|
|
bn_gamma: None,
|
|
bn_beta: None,
|
|
bn_mean: None,
|
|
bn_var: None,
|
|
}
|
|
}
|
|
|
|
/// A head whose shared-conv stack is one `ch->ch` conv, with empty seg/uv heads,
|
|
/// so the bench isolates a single conv-layer cost.
|
|
fn single_conv_head(ch: usize, k: usize) -> DensePoseHead {
|
|
let mut config = DensePoseConfig::new(ch, 1, 2);
|
|
config.kernel_size = k;
|
|
config.padding = k / 2; // same padding
|
|
config.hidden_channels = vec![ch];
|
|
let weights = DensePoseWeights {
|
|
shared_conv: vec![conv_layer(ch, ch, k)],
|
|
segmentation_head: vec![],
|
|
uv_head: vec![],
|
|
};
|
|
DensePoseHead::with_weights(config, weights).expect("valid head")
|
|
}
|
|
|
|
fn bench_native_conv(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("native_conv");
|
|
// (channels, spatial, kernel) — a modest map and a larger one.
|
|
for &(ch, hw, k) in &[(16usize, 32usize, 3usize), (32, 32, 3)] {
|
|
let head = single_conv_head(ch, k);
|
|
let input = Tensor::Float4D(Array4::from_shape_fn((1, ch, hw, hw), |(_, c, y, x)| {
|
|
((c + y + x) as f32 * 0.001).cos()
|
|
}));
|
|
// Throughput in output elements processed.
|
|
group.throughput(Throughput::Elements((ch * hw * hw) as u64));
|
|
group.bench_with_input(
|
|
BenchmarkId::from_parameter(format!("ch{ch}_hw{hw}_k{k}")),
|
|
&input,
|
|
|bencher, inp| {
|
|
bencher.iter(|| {
|
|
let out = head.forward(black_box(inp)).expect("forward ok");
|
|
black_box(out);
|
|
});
|
|
},
|
|
);
|
|
}
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(benches, bench_native_conv);
|
|
criterion_main!(benches);
|