Skip to content

Slice 17A — FeatureSpec heterogeneous input plumbing - #16

Merged
891458249 merged 2 commits into
mainfrom
slice-17a-feature-spec-heterogeneous-fit
Apr 24, 2026
Merged

Slice 17A — FeatureSpec heterogeneous input plumbing#16
891458249 merged 2 commits into
mainfrom
slice-17a-feature-spec-heterogeneous-fit

Conversation

@891458249

Copy link
Copy Markdown
Owner

Summary

  • Introduces FeatureSpec (scalar_dim + per-block QuatBlock with SolverSpace ∈ {Full, Swing, Twist, SwingTwist}) and two new solver::fit() overloads composing cmt-style per-block distance columns.
  • Scalar-only dispatch explicitly delegates to legacy fit(C, Y, opts, λ) with feature_spec overlay — legacy function body is zero-touch (Slice 11 G2/G3 precedent).
  • FitResult gains 4 ABI-additive tail fields (feature_spec, quat_features, feature_norms, distance_norm); sample_radii deferred to Slice 17F per plan Decision 4.
  • 20 new tests across 4 groups, including the 17A-SCALAR-ORACLE hard gate (6 tests, 14-field byte-identical FitResult comparison — zero ULP, no EXPECT_DOUBLE_EQ).

Validation

Gate Result
ctest --test-dir build (kernel) 159/159 PASS, 0 17A-related SKIP
ctest --test-dir build-adapter (aggregate) 200/200 PASS — adapter 41 unchanged
bench_predict N=1000 Gaussian kd-tree path 1.44 μs / iter ≪ 5 μs regression gate
Build warnings / errors 0 / 0 (MSVC 19.44.35223 Release C++11)

Test plan

  • Phase 1 kernel ctest regression (139 legacy tests unchanged)
  • 17A-SCALAR-ORACLE hard gate: 6 tests across {Gaussian, ThinPlateSpline, Cubic+poly, LambdaAuto} + INSUFFICIENT_SAMPLES + solver_path preservation
  • Group C — quat-only fits (Full interpolation, SwingTwist column count, validation rejection)
  • Group D — hybrid scalar+quat fit + residual_norm gate + compile-time noexcept traits
  • Adapter ctest regression (41 tests, zero delta)
  • bench_predict regression on scalar hot path (kd-tree 1.44 μs; 17A doesn't touch any predict code path)

Pre-dispatch audit trail

See DEVLOG.md §A for the Slice 17A pre-dispatch review report (Rules 1–5 compliance, 3 drifts fixed, 4 decisions logged, T-30 tech-debt opened). See §B–F for implementation detail, validation evidence, and 2 in-flight F-stops (Rule 4 methodology).

Tech-debt delta

  • T-30 (opened, blocks 17B acceptance) — cmt binary parity fixture generator scripts/gen_cmt_fixture.mel + checked-in JSON snapshots binding rbfmax output to cmt's Maya-side linearRegressionSolver::setFeatures at tol ≤ 1e-10. Cannot close in 17A (Maya-free CI invariant).

Design decisions (in-plan)

  • Legacy fit(C, Y, opts, λ) body zero-touch; new overload delegates explicitly with feature_spec overlay (Decision 3).
  • sample_radii NOT added in 17A (Decision 4 — added in 17F with proper name, no slice suffix).
  • build_composite_distance_matrix in anonymous namespace (Step 3.4 constraint Slice 04 — kd-tree spatial index (v0.4.0) #1); not exposed until 17E justifies public API.
  • Full mode = 1 col per pose, Swing/Twist/SwingTwist = 2 col per pose (Drift Slice 06 — ScratchPool zero-alloc predict (v0.6.0) #3 resolution; cmt source does not cover Full).
  • Ridge-solve on (MᵀM + λI) via existing solve_symmetric_system; one-hot θ switch deferred to 17E.
  • fr.weights.rows() == cols in hetero path (not N). Documented divergence; hetero predict path lands in 17E.

🤖 Generated with Claude Code

d891458249-rgb and others added 2 commits April 24, 2026 13:50
Phase 2A.5 kernel-layer extension: introduce FeatureSpec (scalar_dim +
quat_blocks with SolverSpace Full/Swing/Twist/SwingTwist) and two new
solver::fit() overloads composing cmt-style per-block distance columns.
Scalar-only dispatch explicitly delegates to legacy fit() with
feature_spec overlay — legacy function body zero-touch per Slice 11
G2/G3 precedent.

Kernel additions (all noexcept, C++11 floor preserved):
* kernel/include/rbfmax/feature_spec.hpp — new header-only type surface
  (SolverSpace, QuatBlock, FeatureSpec; Full=1 col/pose, Swing/Twist/
  SwingTwist=2 col/pose).
* kernel/include/rbfmax/solver.hpp — FitResult gains 4 ABI-additive tail
  fields (feature_spec, quat_features, feature_norms, distance_norm);
  2 new fit() overloads accepting scalar_centers + quat_features +
  FeatureSpec. sample_radii deferred to 17F per plan Decision 4.
* kernel/src/solver.cpp — validate_composite_inputs +
  build_composite_distance_matrix (anonymous namespace per Step 3.4
  constraint #1) implements cmt setFeatures steps 1-6 (per-column L2,
  Frobenius scalar-block norm, RBF, per-block quat distances with
  decompose_swing_twist for Swing/Twist/SwingTwist modes and
  quaternion_geodesic_distance for Full). Per-sample adaptive radius
  tracked for Full mode as well (17F hook-in ready). Ridge-solve on
  (MᵀM + λI) via existing solve_symmetric_system; one-hot θ deferred
  to 17E.

Tests (20 new, 4 groups):
* Group A (8) — FeatureSpec / QuatBlock type contract.
* Group B (6, 17A-SCALAR-ORACLE hard gate) — byte-identical FitResult
  across {Gaussian, TPS, Cubic+poly, LambdaAuto} + status/solver_path
  preservation. 14-field assertion: 10 pre-17A fields via std::memcmp
  on Eigen storage + std::memcpy→uint64_t for scalars (zero ULP, no
  EXPECT_DOUBLE_EQ); 4 tail fields asserted default-constructed except
  feature_spec which reflects the caller's spec (承诺等式).
* Group C (3) — Full-mode interpolation on axis-angle-exact fixture,
  SwingTwist column-count invariant (2N), block-count mismatch rejection.
* Group D (3) — Hybrid scalar+quat fit success, residual_norm < 1e-9
  interpolation gate, compile-time noexcept traits on FitResult/
  FeatureSpec/QuatBlock.

Fixtures (NOT cmt binary parity — independent re-implementation per
plan section C; true cmt binary parity is T-30, scheduled for 17B):
* tests/fixtures/cmt_parity_17A_quat_only_full.json — N=4 axis-angle
  exact quats (identity, 90°Y, 180°Y, 45°Y).
* tests/fixtures/cmt_parity_17A_hybrid.json — N=3, scalar_dim=1,
  SwingTwist axis=Y.

Validation (Windows 11, MSVC 19.44.35223, Release, C++11):
* ctest --test-dir build: 159/159 PASS, 0 17A-related SKIP (139 Phase 1
  legacy + 20 new Slice 17A).
* ctest --test-dir build-adapter: 200/200 PASS (adapter 41 tests
  unchanged — zero regression).
* bench_predict N=1000 Gaussian kd-tree path: 1.44 μs / iter (≪ 5 μs
  regression gate).

F-stops resolved in-flight:
* F1 — Full-mode residual 8.79e-9 at λ=1e-8 exceeded 1e-9 gate. Root
  cause: ridge residual scales as O(λ) for well-conditioned systems.
  Fix: λ=1e-10, residual drops ≪ 1e-9. Matches Rule 4 assumption-
  elimination methodology.
* F2 — noexcept(solver::predict(fr, x)) static_assert failed on MSVC
  due to Eigen::Ref construction not being noexcept in expression form.
  Fix: replaced with std::is_nothrow_*_constructible traits on
  FitResult / FeatureSpec / QuatBlock — tightest portable C++11 signal.
  Canonical noexcept contract remains the keyword on solver::predict.

Tech-debt delta:
* T-30 (open) — cmt binary parity fixture generator blocks 17B
  acceptance. Cannot close in 17A (Maya-free CI invariant).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #16 CI surfaced three Ubuntu / GCC 11 Release -Werror diagnostics
that MSVC does not flag:

- missing-declarations on make_quat_fixture_full_N4 and
  make_quat_fixture_swingtwist_N3 (file-scope in rbfmax:: without
  a prior decl). Wrap both in an anonymous namespace.
- unused-function on vector_bytes_equal (defensive helper with no
  caller — 17A's 14-field oracle has no VectorX byte comparison).
  Delete with an explanatory comment.

Test-only; zero kernel / maya-node touch; legacy fit() body still
0-delta from f568728. Local MSVC Release: ctest 159/159 PASS.

DEVLOG §G appended to the existing Slice 17A section (same slice,
follow-up commit on the open PR #16).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@891458249
891458249 merged commit 2a3aa0e into main Apr 24, 2026
4 checks passed
@891458249
891458249 deleted the slice-17a-feature-spec-heterogeneous-fit branch April 24, 2026 06:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants