Follow-up to #30 (Phase 1.1) and the tighten-rank-2-only decision in #42.
Summary
`tensor_inv` of rank ≠ 2 currently throws at emit time. The algebraic inverse is well-defined (and tmech can compute it), but emitting the correct `tmech::inv` template invocation requires knowing which contraction-index pair to invert against.
Example: a rank-4 elasticity-tangent inverse needs
```cpp
tmech::inv<tmech::sequence<1,2>, tmech::sequence<3,4>>(C)
```
— and the `<1,2>`/`<3,4>` choice is a property of the tensor's algebraic structure (minor-symmetric, major-symmetric, full, etc.), not a codegen-side default.
Where the work lives
Upstream (numsim-cas): add an accessor (e.g. `inv_contraction_pair()` returning two `sequence`-shaped index lists) on the `cas::tensor_inv` node. The factory `cas::inv(...)` already inspects the input tensor's space (`Symmetric`, `MinorMajor`, etc., via `tensor_inv.h:18-25`); deriving the natural index pair from that space is a small extension.
Downstream (numsim-codegen): once the accessor exists, the emit case in `tensor_code_emit.h` reads the pair and produces `tmech::inv<tmech::sequence<...>, tmech::sequence<...>>()`. The current rank-2 path stays (no template args).
Acceptance
- numsim-cas exposes the contraction-index pair on `tensor_inv`
- numsim-codegen emit case for rank-4 produces the right tmech template form
- New test in `tests/TensorCodeEmitTest.cpp` exercises a rank-4 `tensor_inv` (e.g. the natural inverse of a rank-4 elasticity tensor) and asserts the emitted sequence args
- The current rank-4 throw in `tensor_code_emit.h` is removed
Why it matters
This is on the critical path of #35 (Phase 3b algorithmic-tangent pass — D7 of the plan emits `tmech::inv(...)` at the rank-4 Jacobian inversion site). Without rank-4 inv, the tangent pass can't ship.
Refs
Follow-up to #30 (Phase 1.1) and the tighten-rank-2-only decision in #42.
Summary
`tensor_inv` of rank ≠ 2 currently throws at emit time. The algebraic inverse is well-defined (and tmech can compute it), but emitting the correct `tmech::inv` template invocation requires knowing which contraction-index pair to invert against.
Example: a rank-4 elasticity-tangent inverse needs
```cpp
tmech::inv<tmech::sequence<1,2>, tmech::sequence<3,4>>(C)
```
— and the `<1,2>`/`<3,4>` choice is a property of the tensor's algebraic structure (minor-symmetric, major-symmetric, full, etc.), not a codegen-side default.
Where the work lives
Upstream (numsim-cas): add an accessor (e.g. `inv_contraction_pair()` returning two `sequence`-shaped index lists) on the `cas::tensor_inv` node. The factory `cas::inv(...)` already inspects the input tensor's space (`Symmetric`, `MinorMajor`, etc., via `tensor_inv.h:18-25`); deriving the natural index pair from that space is a small extension.
Downstream (numsim-codegen): once the accessor exists, the emit case in `tensor_code_emit.h` reads the pair and produces `tmech::inv<tmech::sequence<...>, tmech::sequence<...>>()`. The current rank-2 path stays (no template args).
Acceptance
Why it matters
This is on the critical path of #35 (Phase 3b algorithmic-tangent pass — D7 of the plan emits `tmech::inv(...)` at the rank-4 Jacobian inversion site). Without rank-4 inv, the tangent pass can't ship.
Refs