Skip to content

fix(mixed-mrf): port full-space Jacobian + Pfaffian split to Kyy block#104

Merged
MaartenMarsman merged 2 commits into
mainfrom
fix/mixed-mrf-pfaffian
May 15, 2026
Merged

fix(mixed-mrf): port full-space Jacobian + Pfaffian split to Kyy block#104
MaartenMarsman merged 2 commits into
mainfrom
fix/mixed-mrf-pfaffian

Conversation

@MaartenMarsman
Copy link
Copy Markdown
Collaborator

Summary

Completes the Fix 2 port from GGMGradientEngine to the mixed-MRF Kyy block. Replaces the previous Roverato-style Cholesky Jacobian log|det J| = q log 2 + sum_j (deg_higher_yy(j) + 2) psi_j with the graph-agnostic split plus a per-column constraint correction:
ldj = q log 2 + sum_j (q + 1 - j) psi_j
pfaffian = 0.5 * sum_qq log det(A_qq diag(M_qq^{-1}) A_qq^T)
logp += ldj - pfaffian

  • For the full Kyy graph all A_qq are empty and the Pfaffian collapses to zero, so the new and old formulas agree there.
  • For sparse Kyy the previous formula did not represent any RATTLE measure correction; the new one is the correct manifold-marginal under any diagonal mass.

Applied to both gradient paths in mixed_mrf_gradient.cpp. Theta-space uses identity mass (Pfaffian = 0 on full Kyy, which is the only regime the dispatcher routes here). Full-space uses this->inv_mass_ (empty ⇒ identity) and accumulates the matching adjoint into R_bar via two triangular solves through chol(G_qq).

Mirrors the GGM Fix 2 pattern in ggm_gradient.cpp line-for-line.

Test plan

  • devtools::test() passes (no regressions in the existing 6989 tests)
  • Finite-difference gradient check on mixed_test_logp_and_gradient_full agrees with analytic gradient (max relative error < 1e-5 across 4 configurations: full Kyy, sparse Kyy with 1 or 2 excluded edges, identity and random diagonal mass) — new tests in test-mixed-gradient-pfaffian.R
  • SBC under model_type = "mixed" with sparse Kyy

Completes the Fix 2 port from GGMGradientEngine to the mixed-MRF Kyy
block. Replaces the previous Roverato-style Cholesky Jacobian
    log|det J| = q log 2 + sum_j (deg_higher_yy(j) + 2) psi_j
with the graph-agnostic split plus a per-column constraint correction:
    ldj      = q log 2 + sum_j (q + 1 - j) psi_j
    pfaffian = 0.5 * sum_qq log det(A_qq diag(M_qq^{-1}) A_qq^T)
    logp    += ldj - pfaffian
For the full Kyy graph all A_qq are empty and the Pfaffian collapses to
zero, so the new and old formulas agree there. For sparse Kyy the
previous formula did not represent any RATTLE measure correction; the
new one is the correct manifold-marginal under any diagonal mass.

Applied to both gradient paths:
- logp_and_gradient (theta-space): identity mass; Pfaffian = 0 on full
  Kyy, which is the only regime the dispatcher routes here.
- logp_and_gradient_full (full-space): mass-weighted Pfaffian using
  this->inv_mass_ (empty => identity). Backward pass adds the matching
  adjoint to R_bar via two triangular solves through chol(G_qq).

ensure_constraint_structure() is now called at the top of each gradient
function so chol_constraint_structure_.columns[col].excluded_indices is
available when building per-column A_qq. The projection helpers
(MixedMRFModel::project_position/_momentum) already built mass-weighted
A_qq matrices for RATTLE; the new gradient code consumes the same
constraint structure.

No regressions in `devtools::test()` (6989 pass, 0 fail).
Adds tests/testthat/test-mixed-gradient-pfaffian.R covering the new
Cholesky-to-K + Pfaffian split in MixedMRFModel::logp_and_gradient_full.
Four cases at max relative error < 1e-5:
  - full Kyy graph (refactor sanity; Pfaffian = 0)
  - sparse Kyy, 1 excluded edge, identity mass
  - sparse Kyy, 1 excluded edge, random diagonal mass
  - sparse Kyy, 2 excluded edges, random diagonal mass (multi-column)

Plumbs an optional inv_mass_diag arg through mixed_test_logp_and_gradient_full
so the integrator's mass-weighted Pfaffian can be exercised from R; the
arg falls back to identity in the gradient engine when NULL.
RcppExports regenerated via Rcpp::compileAttributes().
@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

❌ Patch coverage is 86.27451% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.49%. Comparing base (5988230) to head (119a4cd).

Files with missing lines Patch % Lines
src/models/mixed/mixed_mrf_gradient.cpp 86.00% 14 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #104      +/-   ##
==========================================
- Coverage   86.51%   86.49%   -0.03%     
==========================================
  Files          77       77              
  Lines       13269    13356      +87     
==========================================
+ Hits        11480    11552      +72     
- Misses       1789     1804      +15     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@MaartenMarsman MaartenMarsman marked this pull request as ready for review May 15, 2026 06:37
@MaartenMarsman MaartenMarsman merged commit a4a4b2c into main May 15, 2026
8 of 10 checks passed
@MaartenMarsman MaartenMarsman deleted the fix/mixed-mrf-pfaffian branch May 15, 2026 06:37
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.

1 participant