Use case
Every constitutive tangent: S = 2 ∂ψ/∂C, ℂ = 2 ∂S/∂C with C symmetric, or σ = ∂W/∂ε, ℂ = ∂σ/∂ε with ε symmetric. Codegen needs to know the result is symmetric (rank 2) / minor-and-major symmetric (rank 4) to emit Voigt/Mandel storage and skip redundant components, and the expression should be small enough to emit.
What happens today (probed on main, dim 3)
The derivative w.r.t. a symmetric tensor IS symmetrized (a P_sym{4} projector is inserted), and the numbers are right — but the result is neither simplified nor annotated:
Expression, C/Eps assumed Symmetric{} |
Result |
is_symmetric |
diff(dot_product(C,{1,2},B,{1,2}), C) |
inner(P_sym{4}{1, 2}, B, {1, 2}) — this is sym(B) |
0 |
diff(trace(C*B), C) |
I:permute_indices(inner(P_sym{4}{2}, B, {1}), {1, 3, 4, 2}) |
0 |
diff(trace(C*C), C) |
I:(outer(I,{1,3},C,{4,2}):P_sym{4}+outer(C,{1,3},I,{4,2}):P_sym{4}) — this is 2C |
0 |
diff(norm(dev(sg)), sg) |
pow(norm(dev(sg)),-1)*dev(sg):dev(P_sym{4}) |
0 (and is_deviatoric 0) |
diff(C4:Eps, Eps), C4 MinorMajor |
C4:P_sym{4} |
is_minor_major 0 |
Neo-Hooke ℂ = 2 ∂S/∂C |
1036 chars |
minor/major/minor_major all 0, numerically both symmetries hold to 1e-16 |
Compare diff(dot(C), C) → 2*Cs with is_symmetric = 1: the dot path has a dedicated rule; the general path does not.
Two gaps, one root: the projector contractions the differentiator emits are written with explicit index sequences (inner(P_sym,{1,2},X,{1,2}), X:P_sym, C4:P_sym, dev(P_sym)), which the projector algebra does not recognise as the canonical sym(X) / dev(X) forms it already knows how to fold and tag.
Proposal
- Normalize projector contractions:
inner(P{1,2}, X, {1,2}), X:P (contract on P's leading pair; by major symmetry equal), P:X, and permute-wrapped variants → the canonical inner_product(P, {3,4}, X, {1,2}) form, so projector_algebra.h's existing rules (P_sym : sym-space → X, P:P, dev(P_sym) → P_dev, tags) fire. inner(P_sym{4}{1,2}, B, {1,2}) then becomes sym(B); dev(sg):dev(P_sym{4}) becomes dev(sg).
- Tag derivative results: a rank-2 derivative w.r.t. a
Symmetric argument carries Symmetric; a rank-4 derivative of a symmetric-valued expression w.r.t. a symmetric argument carries MinorMajor (minor from the symmetrization on both index pairs, major when it is a second derivative of a scalar — ℂ = ∂²ψ/∂C²). This falls out of (1) for the folded forms; for the unfolded remainder it needs the rule "X : P_sym on the argument's pair ⇒ minor-symmetric on that pair".
- Lock-in: the six probes above, asserting both the folded print and the tag; numeric symmetry check on the Neo-Hooke tangent.
Scope
Medium. (1) is a simplifier rule set in projector_algebra.h / the inner-product factory; (2) touches tensor_differentiation result annotation. Depends on #455 for a mechanical rank guard (rank-4 nodes carrying rank-2 tags is what P_sym{4} already does by design), related to #396 (annotation derivations) and #276 (rank-4 inverse index pair — needs the same MinorMajor tag on tangents).
Signed-off-by: petlenz [email protected]
Use case
Every constitutive tangent:
S = 2 ∂ψ/∂C,ℂ = 2 ∂S/∂CwithCsymmetric, orσ = ∂W/∂ε,ℂ = ∂σ/∂εwithεsymmetric. Codegen needs to know the result is symmetric (rank 2) / minor-and-major symmetric (rank 4) to emit Voigt/Mandel storage and skip redundant components, and the expression should be small enough to emit.What happens today (probed on main, dim 3)
The derivative w.r.t. a symmetric tensor IS symmetrized (a
P_sym{4}projector is inserted), and the numbers are right — but the result is neither simplified nor annotated:C/EpsassumedSymmetric{}is_symmetricdiff(dot_product(C,{1,2},B,{1,2}), C)inner(P_sym{4}{1, 2}, B, {1, 2})— this issym(B)diff(trace(C*B), C)I:permute_indices(inner(P_sym{4}{2}, B, {1}), {1, 3, 4, 2})diff(trace(C*C), C)I:(outer(I,{1,3},C,{4,2}):P_sym{4}+outer(C,{1,3},I,{4,2}):P_sym{4})— this is2Cdiff(norm(dev(sg)), sg)pow(norm(dev(sg)),-1)*dev(sg):dev(P_sym{4})is_deviatoric0)diff(C4:Eps, Eps),C4MinorMajorC4:P_sym{4}is_minor_major0ℂ = 2 ∂S/∂CCompare
diff(dot(C), C)→2*Cswithis_symmetric = 1: thedotpath has a dedicated rule; the general path does not.Two gaps, one root: the projector contractions the differentiator emits are written with explicit index sequences (
inner(P_sym,{1,2},X,{1,2}),X:P_sym,C4:P_sym,dev(P_sym)), which the projector algebra does not recognise as the canonicalsym(X)/dev(X)forms it already knows how to fold and tag.Proposal
inner(P{1,2}, X, {1,2}),X:P(contract on P's leading pair; by major symmetry equal),P:X, andpermute-wrapped variants → the canonicalinner_product(P, {3,4}, X, {1,2})form, soprojector_algebra.h's existing rules (P_sym : sym-space → X,P:P,dev(P_sym)→P_dev, tags) fire.inner(P_sym{4}{1,2}, B, {1,2})then becomessym(B);dev(sg):dev(P_sym{4})becomesdev(sg).Symmetricargument carriesSymmetric; a rank-4 derivative of a symmetric-valued expression w.r.t. a symmetric argument carriesMinorMajor(minor from the symmetrization on both index pairs, major when it is a second derivative of a scalar —ℂ = ∂²ψ/∂C²). This falls out of (1) for the folded forms; for the unfolded remainder it needs the rule "X : P_symon the argument's pair ⇒ minor-symmetric on that pair".Scope
Medium. (1) is a simplifier rule set in
projector_algebra.h/ the inner-product factory; (2) touchestensor_differentiationresult annotation. Depends on #455 for a mechanical rank guard (rank-4 nodes carrying rank-2 tags is whatP_sym{4}already does by design), related to #396 (annotation derivations) and #276 (rank-4 inverse index pair — needs the same MinorMajor tag on tangents).Signed-off-by: petlenz [email protected]