Slice 17A — FeatureSpec heterogeneous input plumbing - #16
Merged
Conversation
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>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
FeatureSpec(scalar_dim + per-blockQuatBlockwithSolverSpace ∈ {Full, Swing, Twist, SwingTwist}) and two newsolver::fit()overloads composing cmt-style per-block distance columns.fit(C, Y, opts, λ)withfeature_specoverlay — legacy function body is zero-touch (Slice 11 G2/G3 precedent).FitResultgains 4 ABI-additive tail fields (feature_spec,quat_features,feature_norms,distance_norm);sample_radiideferred to Slice 17F per plan Decision 4.FitResultcomparison — zero ULP, noEXPECT_DOUBLE_EQ).Validation
ctest --test-dir build(kernel)ctest --test-dir build-adapter(aggregate)bench_predictN=1000 Gaussian kd-tree pathTest plan
17A-SCALAR-ORACLEhard gate: 6 tests across{Gaussian, ThinPlateSpline, Cubic+poly, LambdaAuto}+INSUFFICIENT_SAMPLES+solver_pathpreservationbench_predictregression 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
scripts/gen_cmt_fixture.mel+ checked-in JSON snapshots binding rbfmax output to cmt's Maya-sidelinearRegressionSolver::setFeaturesat tol ≤ 1e-10. Cannot close in 17A (Maya-free CI invariant).Design decisions (in-plan)
fit(C, Y, opts, λ)body zero-touch; new overload delegates explicitly withfeature_specoverlay (Decision 3).sample_radiiNOT added in 17A (Decision 4 — added in 17F with proper name, no slice suffix).build_composite_distance_matrixin anonymous namespace (Step 3.4 constraint Slice 04 — kd-tree spatial index (v0.4.0) #1); not exposed until 17E justifies public API.(MᵀM + λI)via existingsolve_symmetric_system; one-hot θ switch deferred to 17E.fr.weights.rows() == colsin hetero path (not N). Documented divergence; hetero predict path lands in 17E.🤖 Generated with Claude Code