Repository navigation
Correct the multi-modal losses and the claims of the particle and parity frontends - #371
Conversation
…ity frontends CrossModalCycleLoss replaces its cycle term, which is zero by construction for a weight-tied core with a right-invertible frontend, by the cross-reconstruction error decode(b, z_a) - x_b (beta_cycle becomes beta_cross = 1.0), recombines a pre-split parity pair before the comparison, and raises a DimensionMismatch for x_a and x_b with different numbers of samples. The self-reconstruction forms of CharbonnierRelativeLoss skip the latent term. IsometricRolloutLoss has no default latent Hamiltonian: alpha_H > 0 requires H. The docstrings and two docs pages state the limits of the sorting, slot and parity frontends, and a new testset checks HRedSys with a reference that is not an equilibrium. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t lines The page's SymmetricSymplecticAutoencoder paragraph states the same distinct-coordinates qualifier as its opening paragraph. The CHANGELOG bullet names only the docstrings that state the frontends' limits, and a test comment says exactly when Docs.doc has no method. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Findings
|
| Layer / File(s) | Summary |
|---|---|
Reconstruction targets and Charbonnier loss src/loss/multimodal_losses.jl, test/architectures/multimodal_symplectic_autoencoder.jl, CHANGELOG.md |
Parity-split fields are recombined for reconstruction comparisons. The encode-decode CharbonnierRelativeLoss form computes reconstruction loss without a latent term. The external-target form retains latent consistency. |
Explicit Hamiltonian in rollout loss src/loss/multimodal_losses.jl, test/architectures/multimodal_symplectic_autoencoder.jl, KNOWN_ISSUES.md |
IsometricRolloutLoss requires H when α_H > 0. When α_H = 0, H can be nothing, and the loss skips energy evaluations. |
Cross-modal reconstruction src/loss/multimodal_losses.jl, test/architectures/multimodal_symplectic_autoencoder.jl, CHANGELOG.md |
CrossModalCycleLoss replaces latent cycle consistency with cross-reconstruction into mod_b. It uses β_cross with a default of 1.0 and checks encoded-state sizes. |
Frontend documentation
| Layer / File(s) | Summary |
|---|---|
Stiefel frontend structure docs/src/layers/stiefel_layer.md, src/architectures/multimodal_symplectic_autoencoder.jl, src/layers/bilinear_stiefel_layer.jl, src/layers/low_rank_stiefel_layer.jl, CHANGELOG.md |
Documentation limits canonical coordinates to the declared parity-coordinate structure. It states that the described linear frontends are not Poisson maps for the Vlasov bracket. |
Sorting and slot frontend limits docs/src/layers/symmetric_particle_layers.md, src/architectures/symmetric_symplectic_autoencoder.jl, src/layers/canonical_slot_layer.jl, src/layers/canonical_sort_layer.jl |
Documentation describes sorting and relative-mode norm discontinuities, distinct-coordinate conditions, fixed particle count, and stated system suitability. |
Reference-shift validation
| Layer / File(s) | Summary |
|---|---|
Non-equilibrium reference-shift test test/reduced_system/reference_shift_and_vjp.jl |
A new test compares shifted and translated unshifted reduced trajectories, decoded states, and reduced Hamiltonians across element types and pullback modes. |
Estimated code review effort: 3 (Moderate) | ~25 minutes
Change: Bug fix
Sequence Diagram(s)
sequenceDiagram
participant EncoderA
participant DecoderB
participant CrossModalCycleLoss
participant TargetB
EncoderA->>CrossModalCycleLoss: provide encoded state z_a
CrossModalCycleLoss->>DecoderB: decode z_a into mod_b
TargetB->>CrossModalCycleLoss: provide decoded-form target x_b
CrossModalCycleLoss->>CrossModalCycleLoss: compare output with target and add latent alignment
Merge Risk: 
Low · previous review 17b64
-
Mixed-type pre-split fields can cause reconstruction losses to fail. The issue is limited to those inputs and should be fixed or tracked.
Pre-merge checks: 
4
-
Full details: Title check
-
Explanation
The title names two changes: correcting the multi-modal losses and correcting the claims of the particle and parity frontends. The changeset matches both. It changes CrossModalCycleLoss, CharbonnierRelativeLoss and IsometricRolloutLoss, and it updates the docstrings and docs pages for the particle and parity frontends. The title is specific, but it is long at 82 characters and does not name the main behaviour change, which is the replacement of the cycle term with a cross-reconstruction term.
Full details: Description check
-
Explanation
The pull request description directly matches the changeset. It explains the multi-modal loss corrections, validation and API changes, added tests, frontend documentation limits, changelog updates, and reference-state coverage.
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
There was a problem hiding this comment.
Actionable comments posted: 2

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
3c9fd882-edad-4030-964e-84c9be5a3dea
Files selected for processing: 13-
CHANGELOG.mdKNOWN_ISSUES.mddocs/src/layers/stiefel_layer.mddocs/src/layers/symmetric_particle_layers.mdsrc/architectures/multimodal_symplectic_autoencoder.jlsrc/architectures/symmetric_symplectic_autoencoder.jlsrc/layers/bilinear_stiefel_layer.jlsrc/layers/canonical_slot_layer.jlsrc/layers/canonical_sort_layer.jlsrc/layers/low_rank_stiefel_layer.jlsrc/loss/multimodal_losses.jltest/architectures/multimodal_symplectic_autoencoder.jltest/reduced_system/reference_shift_and_vjp.jl
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.
-
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #371 +/- ##
==========================================
+ Coverage 94.37% 94.44% +0.07%
==========================================
Files 63 63
Lines 2719 2738 +19
==========================================
+ Hits 2566 2586 +20
+ Misses 153 152 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
michakraus
left a comment
There was a problem hiding this comment.
JuliaGNI/GeometricMachineLearning.jl PR #371 — Correct the multi-modal losses and the claims of the particle and parity frontends
Verdict: request changes (posted as a comment, because the reviewing account is the PR author)
Reviewed: 44bf1e77b6c8a9db505d3ffb2b35b229e4a359b7
CI: the three red jobs fail the same way on main at 7c92ea85 (run 41114599304, four hours earlier), and none of them reached the tests:
Downgrade - ubuntu-latest: a resolver failure.forcedeps check failed: Zygote resolved to 0.7.5 but lower bound is 0.7.0, the same for ChainRulesCore 1.25.1 / 1.0.0, NNlib 0.9.30 / 0.8.0 and KernelAbstractions 0.9.38 / 0.9.0. The four lines are the same in themainjob. The PR does not touchProject.toml,test/Project.tomlor.github/. It is not attributable to this diff.Julia preandJulia nightly: a precompile failure.UndefVarError: libLLVMExtra not defined in LLVM.API(LLVM 9.13.2, LLVMExtra_jll 0.0.47), thenMetal (skipped, GPUCompiler failed to precompile). The same error is in bothmainjobs. This gives no test signal.- All
Julia 1andJulia minjobs, Metal, Documentation, Doctests and Scripts are green.
Findings
| # | severity | file:line | claim | evidence |
|---|---|---|---|---|
| 1 | correctness-risk | src/loss/multimodal_losses.jl:149-150, :87; CHANGELOG.md:157 |
The premise of the change is false for the particle frontends. The CHANGELOG says the PR removes "the terms that are zero by construction". On a weight-tied core, ‖encode(b, decode(b, z)) − z‖ is not zero for CanonicalSortLayer or CanonicalSlotLayer with more than one slot, because the decoder does not map into the sorted chamber. The self forms of CharbonnierRelativeLoss drop the latent term for every core and every frontend, so λ_lat has no effect exactly where the term is non-zero. CodeRabbit's comment on :87 is correct, and this goes further than it. The simplest fix is to keep main's latent term in the self form (two lines): it is exactly zero where the premise holds. The other fix is to make the skip dispatch on a right-invertible frontend together with a weight-tied core. |
Probe P1 (weight-tied core 8→4, MersenneTwister(11), 200 random states each). The relative cycle term is non-zero in 200 of 200 draws for CanonicalSortLayer(4, 1) (median 0.025, max 1.13). It is non-zero in 200 of 200 for CanonicalSlotLayer(3, 2, 2) (median 0.080, max 1.01). It is ≤ 5.4e-16 for CanonicalSlotLayer(4, 4, 1). Probe P1b, λ_lat = 1, slot frontend: the self form gives 1.116, 0.906, 0.881, 1.079, and the external form with x̂ = decode(encode(x)) gives 1.153, 0.924, 0.914, 1.110. The repository states the same at src/architectures/symmetric_symplectic_autoencoder.jl:11: right-inversion holds for :sort and :slot "for ξ whose decoded state lies in the canonical chamber … the decoder does not map into this chamber in general". The test at test/architectures/multimodal_symplectic_autoencoder.jl:834-841 checks the zero-cycle claim on BilinearStiefelLayer only. |
| 2 | correctness-risk | CHANGELOG.md:157; PR body |
For the same reason, the rationale of CrossModalCycleLoss is false when mod_b is :sort or :slot: the cycle term carried a non-zero signal there. Replacing it with the cross term is a design decision and can stay. The text must not say that the removed term was zero: drop "which is zero for a weight-tied core with a right-invertible frontend", or limit it to the parity frontends. |
P1 above |
| 3 | correctness-risk | src/layers/bilinear_stiefel_layer.jl:33-36, src/layers/low_rank_stiefel_layer.jl:27-30, docs/src/layers/stiefel_layer.md:8-11 |
"with L²-orthonormal even and odd bases, the bracket linearised at f₀ gives a singular {Q, P}" is false as a general statement. The bracket is {Q_i, P_j} = ∫ f₀ [φe_i, φo_j]. For an x-independent f₀(v) and periodic x this factorises as M ⊗ G, with M = Φxᵀ D_x Φx antisymmetric. So it is singular for odd m_x and generically regular for even m_x. "Depends on f and is not the identity" holds; "singular" does not. |
Probe P3: Maxwellian f₀, Nx = Nv = 32, central differences, random orthonormal Φx and Φv, m_v = 2. The smallest singular value of {Q, P} is 3.2e-17 (m_x = 1), 0.018 (m_x = 2), 3.5e-17 (m_x = 3) and 0.0026 (m_x = 4). Three further draws with m_x = 2 give 0.037, 0.049 and 0.10. |
| 4 | correctness-risk | src/loss/multimodal_losses.jl:128-134, :38-41 |
The PR adds _reconstruction_target so that a decoded BilinearStiefelLayer field is not compared with [vec(F⁺); vec(F⁻)]. It applies it in the self form, the cross term and α_rec, but not in the external form loss(nn, m, x̂, x). That form still reaches the ndims(x.q) <= 2 branch of _frob_sq_diff(::AbstractMatrix, ::NamedTuple{(:q, :p)}), which is that wrong-layout comparison. The result is a silently different value with no error. The docstring asks for x̂ "of the same form as x", but nothing enforces it. Apply the target in the external form too, or delete the branch so that the mismatch raises an error. |
Probe P2 (BilinearStiefelLayer(4, 4, 2, 1), λ_lat = 0). loss(nn, :grid, F̂, F) = 0.969145, and loss(nn, :grid, F̂, (q = F⁺, p = F⁻)) = 0.974382 for the same field. The self forms agree (0.969145 for both). _frob_sq_diff(F̂, F) = 25.774, and _frob_sq_diff(F̂, (q = F⁺, p = F⁻)) = 26.053. |
| 5 | quality | src/loss/multimodal_losses.jl:180, :213-218 |
IsometricRolloutLoss(integrator), the call with every default, always raises an error, because the defaults α_H = 0.1 and H = nothing contradict each other. A signature whose defaults cannot be used together is a trap. Either default α_H to 0, so that a user who passes H sets the weight, or make H a required keyword. |
test/architectures/multimodal_symplectic_autoencoder.jl:301 asserts @test_throws re_iso_H IsometricRolloutLoss(step_id) |
| 6 | quality | src/loss/multimodal_losses.jl:343 |
CrossModalCycleLoss has no cycle term after this PR. Its docstring (:326) calls it an "alignment and cross-reconstruction loss". The name is unreleased (PR body: "No release has these names"), so a rename such as CrossModalLoss costs nothing now and needs a deprecation after 0.9.0. |
read :323-357 |
| 7 | nit | src/loss/multimodal_losses.jl:272, :286 |
CodeRabbit's comment on :275 is correct in substance. With α_H = 0 and a supplied H, H is still called at every step. A non-finite H then turns the loss into NaN, because 0 * Inf is NaN. A simpler fix than the branches it proposes: store iszero(α_H) ? nothing : H in the constructor, so that the existing nothing methods cover the case. |
Probe P4: α_H = 0 with a counting H gives the same value as no H (19.1607) after 3 calls of H. H = z -> Inf gives NaN. |
| 8 | quality | CHANGELOG.md:157 |
The new ### Changed bullet records a step inside one unreleased version ("the fixed ½‖ẑ‖² of #370", "lose the terms that are zero by construction"). A 0.9.0 reader never had the state of #370. The ### Added bullet at :153 already describes the end state. Fold the docstring-limit sentences into the Added bullets at :149 and :151, and drop :157. |
read CHANGELOG.md:149-157 |
| 9 | nit | src/loss/multimodal_losses.jl:361 |
The DimensionMismatch text says "different numbers of samples" for one sample against a batch of one, (2,) against (2, 1). The PR body lists this itself. "latent states of different sizes" is exact. |
PR body, "Unresolved" |
Verified and fine
- The
_check_eltypeand weight-validation paths, the batch-sum semantics and the pre-split recombination in the self form, the cross term andα_rec. Read, and backed by the tests attest/architectures/multimodal_symplectic_autoencoder.jl:355-508. P2 reproduces the agreement of the self forms. - The cross term against the dense oracle (
:764-832): the encoder and decoder are built from the leaves and explicit parity matrices. This is independent of the code under test. HRedSysaway from the fixed point (test/reduced_system/reference_shift_and_vjp.jl:510-575): the oracle translates the fields and the initial state outside the system, which is a different code path from the shift insideHRedSys. A non-vacuity check is present.- The
KNOWN_ISSUES.mdK41 line citations (:385,:391,:397,:254) match the head. - No
β_cycle,_eval_latent_Hor "canonical quadratic energy" is left outside the tests that assert their absence (grep oversrc,docs,README.md). - JuliaFormatter (
sciml) passes on all nine changed.jlfiles. closingIssuesReferencesis empty, and "Relates to #370" closes nothing.
Not checked
- The two changed test files were not run locally. The green
Julia 1andJulia minjobs ran them. - "a collisionless plasma, whose sorted snapshots are not low-rank" (
canonical_sort_layer.jl:39,canonical_slot_layer.jl:57,symmetric_symplectic_autoencoder.jl:9, page:14) is an empirical claim with no test or reference in the diff. - K41 stays open. A fix that drops the
ArrayOrNamedTupleannotations might be ambiguous with the genericNetworkLossmethods. This was not probed. - Type stability and allocations of the new helpers: not re-measured.
Probe script: P1-P4 are the functions probe_cycle, probe_charbonnier_slot, probe_external_split, probe_bracket and probe_alphaH_zero, run against the PR head in a fresh session.
…ings
- CharbonnierRelativeLoss: the form with an external x̂ recombines a
pre-split BilinearStiefelLayer pair, given as x or as x̂, as the self
form does. A full decoded field against a pre-split x gave a silently
different value. The docstring says that the self form skips the
latent term for every core and frontend, and that the term need not
be zero for CanonicalSortLayer and CanonicalSlotLayer.
- IsometricRolloutLoss: with α_H = 0 the constructor stores nothing in
place of a given H, so H is never called and an Inf value of H no
longer makes the loss NaN.
- CrossModalCycleLoss: the DimensionMismatch names different latent
sizes, not different sample counts, which was wrong for (2,) and (2, 1).
- BilinearStiefelLayer, LowRankStiefelLayer, stiefel_layer.md: the
linearised Vlasov bracket gives a {Q, P} that is not the identity and
can be singular; it is singular only for some bases.
- CHANGELOG: the Changed bullet folds into the Added bullets, without the
false claim that the dropped terms are zero by construction.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolution of the review findingsCommit 17b6450 addresses the review posted earlier on this PR (pullrequestreview-5721824136). Each finding, by its number in that table:
CodeRabbit's comment on the skip condition of the self form is finding 1, and it has the same status. Verification of the fix commit: The three red jobs of the previous head (Julia pre, Julia nightly, Downgrade) fail the same way on |
There was a problem hiding this comment.
Actionable comments posted: 1

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
6f2cca3a-c9ac-4cf1-b3f1-3b906eab1910
Files selected for processing: 6-
CHANGELOG.mddocs/src/layers/stiefel_layer.mdsrc/layers/bilinear_stiefel_layer.jlsrc/layers/low_rank_stiefel_layer.jlsrc/loss/multimodal_losses.jltest/architectures/multimodal_symplectic_autoencoder.jl
Files skipped from review as they are similar to previous changes: 3-
- src/layers/bilinear_stiefel_layer.jl
- src/layers/low_rank_stiefel_layer.jl
- docs/src/layers/stiefel_layer.md
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.
-
| _charbonnier_eval(loss, _reconstruction_target(model, modality, x̂), | ||
| _reconstruction_target(model, modality, x), z_hat, z)::T |
There was a problem hiding this comment.
Promote mixed parity components before recombining them.
For a Float64 network, a two-dimensional pre-split input can have Float64 q and Float32 p. The bilinear encoder accepts that input, but _parity_recombine requires both arrays to have the same element type. The external loss therefore throws a MethodError. (raw.githubusercontent.com)
Promote the components before recombination.
…dalLoss - CharbonnierRelativeLoss: the self form evaluates the latent term again, with x̂ = decode(encode(x)), so both forms evaluate one formula and λ_lat acts in both. The skipped term is not zero for CanonicalSortLayer or CanonicalSlotLayer frontends. - A pre-split BilinearStiefelLayer pair whose halves have different element types raises an ArgumentError naming both, in place of a MethodError from _parity_recombine (CodeRabbit comment 4431005164). - IsometricRolloutLoss: α_H defaults to 0.0, so IsometricRolloutLoss(integrator) constructs and has no energy term. - CrossModalCycleLoss is renamed CrossModalLoss; it has no cycle term. No release has either name. - KNOWN_ISSUES K41: the name and its line citations follow. - CHANGELOG: the unreleased bullet describes the end state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decisions on findings 1, 5 and 6, and CodeRabbit comment 4431005164Commit a0d1946 implements the author's decisions on the three findings that the previous comment left open:
CodeRabbit comment 4431005164 (a pre-split pair with With the new default, a passed
|
With the default α_H = 0, a passed H had no effect and was dropped without a word. The constructor now raises an ArgumentError that names α_H and H, and stores H as given otherwise. The docstring, the CHANGELOG bullet and the K41 line citations follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A negative zero satisfies iszero, so the constructor refuses a given H with it and accepts it without one. Two tests pin both cases. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Relates to #370. This PR closes nothing.
Summary
This PR corrects the multi-modal losses of #370 before 0.9.0 releases them, and it states the limits of the particle and parity frontends in their docstrings and docs pages.
CrossModalLoss(namedCrossModalCycleLossin Add MultiModalSymplecticAutoencoder, multimodal losses, and HRedSys reference shift and VJP #370) drops its cycle term‖encode(b, decode(b, z_a)) − z_a‖². That term is not zero by construction: with a weight-tied core it is zero for a right-invertible frontend such asBilinearStiefelLayer, and it was non-zero in every draw forCanonicalSortLayer(4, 1)andCanonicalSlotLayer(3, 2, 2). The cross-reconstruction errorβ_cross ‖decode(b, z_a) − x_b‖²replaces it, because it depends on the datax_b. The keywordβ_cycle = 0.1becomesβ_cross = 1.0, and a passedβ_cycleraises Julia'sMethodError. A negative orNaNweight raises anArgumentError.x_aandx_bwhose latent states have different sizes raise aDimensionMismatch.CharbonnierRelativeLossevaluate the latent term; the self-reconstruction form usesx̂ = decode(encode(x)), soλ_latacts in both.IsometricRolloutLosshas no default latent Hamiltonian, andα_Hdefaults to0.0, soIsometricRolloutLoss(integrator)has no energy term.α_H > 0requiresH, and the constructor raises anArgumentErrorwithout one. Withα_H = 0, the loss takes noH, and a givenHraises anArgumentError, because it would have no effect.α_recterm recombine a pre-splitBilinearStiefelLayerpair(q = F⁺, p = F⁻)into the full field before they compare it with the decoded field. A pre-split pair whose halves have different element types raises anArgumentErrorthat names both.CanonicalSortLayer,CanonicalSlotLayer,SymmetricSymplecticAutoencoder,BilinearStiefelLayer,LowRankStiefelLayerandMultiModalSymplecticAutoencoder, and the pagessymmetric_particle_layers.mdandstiefel_layer.md, state these limits: sorting is discontinuous where two particles cross, and it suits systems that keep their particle order; the slot layer is a linear projection for a fixed particle count; the(q, p)of the parity frontends is canonical only for the structure that they declare, not for the Vlasov bracket.HRedSyswith a reference state that is not an equilibrium against the unshifted system in shifted coordinates, in both pullback modes and both precisions.SymplecticAutoencoderas the control where it is not zero.No release has these names (
v0.8.0has nosrc/loss/multimodal_losses.jl), so nothing is deprecated.CHANGELOG.mddescribes these changes in the #370 bullet under[Unreleased].Review fixes
The review of
44bf1e77and the decisions on it change the head in three commits:17b64509: the form ofCharbonnierRelativeLosswith an externalx̂recombines a pre-split pair; withα_H = 0, a givenHis not stored or called; theDimensionMismatchtext names the latent sizes; the parity docstrings say that the linearised bracket gives a{Q, P}that is not the identity and can be singular.a0d19463: the self form ofCharbonnierRelativeLosshas the latent term again; a pre-split pair with halves of different element types raises anArgumentError;α_Hdefaults to0.0;CrossModalCycleLossis renamedCrossModalLoss, andKNOWN_ISSUES.mdK41 follows the name and its line citations.e800bdc7: withα_H = 0, a givenHraises anArgumentErrorthat namesα_HandH, in place of the silent drop of17b64509.test/architectures/multimodal_symplectic_autoencoder.jlpasses 373 of 373 ate800bdc7, Julia 1.13.3. The full suite was not run locally on these commits; CI runs it.The sections Tests, Pre-PR verification, Critic and Decisions below record the head
3cac7dafthat the critics judged. They use the nameCrossModalCycleLossand the skip of the self-form latent term as decided then; the Summary describes the head.Tests
3cac7daf, Julia 1.13.3: PASS. 109 testsets, 10731 passed, 2 broken (the two Metal testsets; the run had no GPU), 0 failed, 0 errored.3cac7daf, Julia 1.12.7 (the floor,--check-bounds=yes): PASS, with the same counts.44bf1e77changes text only after3cac7daf: one sentence ofdocs/src/layers/symmetric_particle_layers.md, one sentence ofCHANGELOG.mdand one test comment.Not checked locally: the docs build.
docs/make.jlcould not download the logo through the local network proxy (a TLS error), so the Documentation workflow of this PR is the docs check.test/quality/docs_make_checks_references.jlpasses, and the local reference check reports 0 unresolved references.Pre-PR verification
Fixed: nothing needed a fix. One nit comes from the diff and does not block: the new
DimensionMismatchtext says "different numbers of samples" for a single snapshot against a batch of one,(2,)against(2, 1).Unresolved: none.
Pre-existing, adjacent:
src/architectures/multimodal_symplectic_autoencoder.jl:297:CrossModalLosspasses its modality as a runtime field, sodecodecalls the inverse frontend through runtime dispatch over aUnion. The return type still infers.mainhas the same call, and this branch adds no new dispatch (JET.report_opt).Checked and clean:
decodewith and withoutmetric_factors. The gradient withα_H = 0and noHis finite. No caller of the removed names is left. The KNOWN_ISSUES line citations match the head.test_piraciespasses with the repository'streat_as_own = [Batch]. The new helpers are all on owned functions.code_typedandJET.report_opton the three losses, with full and pre-split inputs, inferFloat64. The one JET report is pre-existing.fatou lintfinds nothing. The hunks add no avoidable allocating construct.CHANGELOG.md..jlfiles conform.jldoctestin the changed pages or docstrings. The new@reftargets are in@docsblocks.Not checked: the docs build (the Documentation workflow checks it); allocation figures (no claim or assertion to test); the type-stability checks on the 1.12 floor.
Release: no bump needed. The renamed keyword and the new error touch API that no release has.
Unresolved
DimensionMismatchwording above. The check and its message are the decided edge; only the wording is loose for a one-sample batch against a single sample. Fixed in17b64509.Critic
Two independent critics judged the head
3cac7daf. Both returned PASS.Critic 1a
Verdict: PASS. Mutants: 22 CAUGHT, 0 SURVIVED.
β_cycleand the cycle term gonemod_a===mod_b)multimodal_losses.jl:367-381; mutants J1, D5, D6;probe_edges.jl:323-342; J10 docstring testsetβ_cross=1.0, anchored refusal of −0.1 and NaNβ_align=NaN,-0.0and Int weightsβ_cycleraises MethodErrorx_brecombined, single and batchx_b, rel. diff 2.3e-17 (F64)@inferred; FD test withβ_cross=0.2IsometricRolloutLoss: no default H, anchored ArgumentError;α_H=0needs no H;α_recuses the target; docstring gives the reasonα_H=-0.0(accepted),Inf(H message),NaN(weight message)_eval_latent_H(::Nothing)presentHRedSysaway from the fixed point, both modessrc docs/src README.mdfor the stale phrases → none left; grepenergyinreduced_system.jlanddocs/src→ no reference-state claim; J10a–dtest/quality/*Documenter.yml)[Unreleased]CHANGELOG.md:153,157Critic 1b
Verdict: PASS. Mutants: 18 CAUGHT, 0 SURVIVED, 0 UNKNOWN, 0 INVALID, of 18.
β_cross, default 1.0, noβ_cyclesrc/loss/multimodal_losses.jl:343-381; mutants J1 and J9pins.jl: the multimodal file FAILS on base (848 pass, 28 fail, 6 error of 882 for the two files)@test_throws MethodError … β_cycle = 0.1)β_crossraises the anchoredArgumentErrorβ_cross ∈ {-0.0, Inf, NaN, 1f0, big(2)}andβ_align = NaNbig(2)):323-342; J10 testsetDimensionMismatch, sum over the batch, three receivers × pair formprobe_edges.jl(metric factors with Rv of size Nv/2 and Nv, batched pairs, Zygote, low-rankbin 2D and 3D)@inferredon all six self-form receivers in the probe@inferredholds for the self-form receivers, Float32 and Float64IsometricRolloutLoss:α_H > 0requiresH;α_H = 0needs noneα_Hα_H ∈ {-0.0, nextfloat(0.0), NaN, Inf, 0.1f0, big(0)}nextfloat(0.0)andInfraise the H message, NaN raises the weight message first, and -0.0 andbig(0)construct withH = nothing; Zygote works withα_H = 0(probe)α_reccompares through the recombined targetT in REAL_ELTYPESmultimodal_symplectic_autoencoder.jl:757-850; J1β_crosskeyword)HRedSysaway from the fixed point, both modes, both precisionspins.jlreduced_system.jlis unchanged):jacobianrows, see Other 1)grep -rnoversrc docs/srcfor the stale phrases,(Proposition|Theorem|Corollary|Section) [0-9],\label,energy\labeloutsidecustom.sty; no energy-match claim for the reference staterun-tests.jl <wt> affected(71 files, quality included); named run of the two test files andtest/quality/aqua.jlCHANGELOG.mddiffeltypeasserts, no untyped literal on the compute pathT(loss.ε)andT(β)git diff --statKNOWN_ISSUES.md(K41 line citations). The rule for line citations requires this editjulia-methods.jlon GMLsrc/loss/multimodal_losses.jlTwo text-only findings of critic 1a are fixed in
44bf1e77: the page sentence "Only:sortand:slotgive a permutation-invariant latent state" now carries the distinct-coordinates qualifier, and the CHANGELOG bullet names only the docstrings that state the limits. The other findings of both critics are non-blocking remarks on size and wording.Known issues added
None. The branch adds no entry to
KNOWN_ISSUES.md. It updates the line citations of the existing entry K41 (src/loss/multimodal_losses.jl:385,:391,:397and:254), because the file's line numbers move.Decisions taken without the user
The user decided three points: the names of #370 go into GML 0.9.0; this PR lands before the 0.9.0 release, so its changes need no deprecation; and clause 1 takes the cross-reconstruction term.
The advisor decided the edges below (advisor, 2026-10-30, medium). Verbatim:
Three points of the head differ from the edges as the advisor wrote them. The plan records each as a correction, and no clause changes:
:jacobianrows of the newHRedSystestset only, not in both modes. Under:vjp, the Jacobian path feeds only Newton's Jacobian, so a wrong Jacobian changes the iteration count and not the converged step. J3b is caught by the:vjprows. Every named mutant is caught.SymplecticAutoencoder(4, 2)with its default keywords raises "AssertionError: The number of decoder blocks is too big!".DimensionMismatchof the sample-count check also fires for a one-sample batchx_aof size(d, M, 1)against a singlex_b, although both hold one sample. The check is the decided edge; only its wording is loose.Every clause of the part is met as written, except clause 7's docs build, which this PR's Documentation workflow checks.
🤖 Generated with Claude Code