Skip to content

Derive symmetric + PSD for Gram forms trans(F)*F and F*trans(F) #389

Description

@petlenz

Summary

trans(F)*F (right Cauchy-Green) and F*trans(F) (left Cauchy-Green / Finger) are symmetric and positive-semidefinite for any F — no assumption on F required. The CAS currently derives neither annotation.

This is the highest-value case: C = FᵀF is the workhorse strain measure in the finite-strain / spectral pipeline, and downstream sym()-projector and eigen simplifications key off the symmetric annotation.

Math

  • Symmetric: (FᵀF)ᵀ = Fᵀ(Fᵀ)ᵀ = FᵀF. Likewise (FFᵀ)ᵀ = FFᵀ.
  • PSD: xᵀ(FᵀF)x = (Fx)ᵀ(Fx) = ‖Fx‖² ≥ 0. (Positive-definite iff F is invertible — not generally known, so only PSD is derivable unconditionally.)

Current behavior (probed)

trans(F)*F   -> is_symmetric=0  is_positive_semidefinite=0   (expected: sym, PSD)
F*trans(F)   -> is_symmetric=0  is_positive_semidefinite=0   (expected: sym, PSD)

No false positives — the annotation is simply dropped (sound but incomplete).

How to fix

Binary hook in the tensor·tensor mul operator, mirroring the existing orthogonal trans(Q)·Q → I fold. In include/numsim_cas/tensor/tensor_operators.h, tag_invoke(mul_fn, L&&, R&&) (the tensor×tensor overload), right after the orthogonal branch (~lines 164-169) and before returning the generic mul_base result:

// Build the product first (via mul_base visitor), then annotate.
// Gram form: trans(X)·X and X·trans(X) are symmetric PSD for any X.
if (lhs.get().rank() == 2 && rhs.get().rank() == 2 &&
    (is_trans_of(lhs, rhs) || is_trans_of(rhs, lhs))) {
  // ... obtain `result` from the mul_base visitor ...
  if (result.is_valid()) {
    result.data()->set_space({Symmetric{}, AnyTraceTag{}});
    result.data()->tensor_algebra_assumptions().insert(positive_semidefinite{});
  }
  return result;
}
  • is_trans_of(a, b) already exists in this file (matches permute_indices_wrapper with {2,1} over b).
  • Post-construction set_space is hash-safe today because the n_ary hash excludes the space annotation — see the identical note on the existing skew branch (lines 118-124).
  • Do not insert positive_definiteF invertibility is not tracked.

How to test

tests/TensorSpacePropagationTest.h (and/or the #111 matrix in tests/TensorAnnotationMatrixTest.h):

auto F = mk("F", 3);              // general, no assumption
EXPECT_TRUE(is_symmetric(trans(F) * F));
EXPECT_TRUE(is_positive_semidefinite(trans(F) * F));
EXPECT_TRUE(is_symmetric(F * trans(F)));
EXPECT_TRUE(is_positive_semidefinite(F * trans(F)));
EXPECT_FALSE(is_skew(trans(F) * F));           // safety: not skew
// Regression guard already present: A·B of two DISTINCT symmetric tensors
// must stay non-symmetric (do not over-generalize the pattern).

Also verify sym(trans(F)*F) now short-circuits to trans(F)*F.

Part of the tensor space-annotation completeness effort (umbrella below). Related: #228 (algebra-property annotations), #111 (annotation matrix).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions