Rank-4 tensor_inv in scalar-arg diff visitor (#287) - #291
Merged
Conversation
The Magnus formula `d(C^{-1})/ds = -C^{-1} : (dC/ds) : C^{-1}` extends
naturally from rank 2 to rank 4 in the scalar-arg setting:
rank 2: -invA_{im} · dA_{mn} · invA_{nj}
two single-index contractions on (2)↔(1)
rank 4: -invA_{ijmn} · dA_{mnpq} · invA_{pqkl}
two two-index contractions on (3,4)↔(1,2)
Unlike the tensor-arg visitor — which has sym/skew/Magnus kernel
variants because differentiating w.r.t. a rank-2 X expands the rank —
the scalar-arg derivative dA/ds has the SAME rank as A, so the same
Magnus formula applies independent of algebraic class. The evaluator
dispatches on annotation at evaluation time via the inner tensor_inv's
space tag; the symbolic AST is the same either way.
Rank-2 path unchanged. Rank != 2 and != 4 keep the not_implemented
throw — the inv() factory rejects them already, so the visitor's guard
catches only direct `make_expression<tensor_inv>` constructions.
3 new tests in TensorDiffWrtScalarTest:
- InvRank4ZeroDerivativeWhenSIndependent — pass-5 singleton-aware
guard still fires (zero-suppression on s-independent inner).
- InvRank4ExercisedDirectly — non-zero diff path, rank/dim lock-in.
- InvRank3ThrowsNotImplemented — belt-and-braces guard.
The companion deferred case in #287 (non-integer / non-constant
tensor_pow exponent) remains blocked on tensor log() from #227.
Full suite: 1321/1321 passing (+3 from prior 1318).
Signed-off-by: petlenz <peterlenz89.pl@gmail.com>
Add the structural hash-equality lock-in the reviewer flagged as a
MEDIUM gap (`InvRank4StructuralLockIn`): rebuild the expected Magnus
formula `-invA : dA : invA` by hand and assert hash equality with the
visitor's output. This protects against silent reordering of the
contraction layout — e.g. swapping `{3,4}↔{1,2}` to `{1,2}↔{3,4}`,
both legal inner-product calls for unsymmetric inputs but producing a
structurally different AST. Numerical eval is intentionally NOT added
here: rank-4 `tmech::inv` requires the post-`db5d8aa` rewrite, which
is being enforced by parallel work in #289; once #289 lands the rank-4
inv eval becomes safe to lock numerically and we can add a separate
end-to-end test then.
The rank-3 wrapper-silence finding from the reviewer (`tensor_inv`
wrapper ctor accepts any rank, factory rejects only rank ∉ {2,4})
is filed as separate issue #292 — out of scope for this PR.
Full suite: 1322/1322 passing (+1 from prior 1321).
Signed-off-by: petlenz <peterlenz89.pl@gmail.com>
Signed-off-by: petlenz <peterlenz89.pl@gmail.com>
petlenz
force-pushed
the
287-scalar-arg-diff-rank4-inv
branch
from
June 10, 2026 17:41
6c3f75c to
59551f2
Compare
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
Closes part of #287. The scalar-arg diff visitor (
tensor_differentiation_wrt_scalar) now handles rank-4tensor_invvia the symbolic Magnus formula:```
d(C^{-1})/ds = -C^{-1} : (dC/ds) : C^{-1}
```
The formula's shape doesn't change between rank 2 and rank 4 — only the contraction layout (1-index ↔ 2-index). Unlike the tensor-arg visitor, which needs separate sym/skew/Magnus kernel variants because differentiating w.r.t. a rank-2 X expands the rank, the scalar-arg derivative
dA/dshas the same rank asA, so the same formula applies regardless of algebraic class. The evaluator already dispatches on annotation at evaluation time via the innertensor_inv's space tag.What's NOT in this PR
The companion
tensor_powcase in #287 (non-integer / non-constant exponent) requireslog(A)for a tensorA, which is on the #227 wishlist. The throw remains; comment in the visitor body sharpened to point at #227.Test plan
TensorDiffWrtScalarTest.InvRank4ZeroDerivativeWhenSIndependent— singleton-aware zero-suppression still fires (parity with rank 2).TensorDiffWrtScalarTest.InvRank4ExercisedDirectly— non-zero diff path, rank/dim lock-in viamake_expression<tensor_inv>(s * D)to bypass theinv(α·A) → inv(A)/αfactory fold.TensorDiffWrtScalarTest.InvRank3ThrowsNotImplemented— belt-and-braces guard for directmake_expression<tensor_inv>constructions at unsupported rank.No numerical lock-in is added: rank-4
tmech::invrequires the post-Sep-2025inverse_wrapper_baserewrite which is being enforced by #289 (parallel work) — evaluating rank-4 inv against the stale system tmech would NaN. The tests verify structure (rank, dim, non-zero); end-to-end numerical verification rides on #289.