Skip to content

Update Local 3rd-Order Tensor Jacobian Layout#219

Merged
zfergus merged 4 commits intomainfrom
update-jacobian-layout
Mar 1, 2026
Merged

Update Local 3rd-Order Tensor Jacobian Layout#219
zfergus merged 4 commits intomainfrom
update-jacobian-layout

Conversation

@zfergus
Copy link
Member

@zfergus zfergus commented Mar 1, 2026

Description

This pull request changes the layout of 3rd-Order tensors stored as matrices. The new layout matches the one described in "Dynamic Deformables" [Kim and Eberle 2022]. The following is an excerpt from their:

Next up, we define the following convention for 3rd-order tensors:

$$ \mathrm{vec}(\mathbb{A})=\mathrm{vec}\left[\begin{array}{c} {[\mathbf{A}]} \\ {[\mathbf{B}]} \\ {[\mathbf{C}]} \end{array}\right]=[\mathrm{vec}(\mathbf{A}) \quad \mathrm{vec}(\mathbf{B}) \quad \mathrm{vec}(\mathbf{C})] . $$

First we unfurl the matrices in the 3rd-order tensor into a row, and then vectorize them all in turn. Here's a concrete example:

$$ \mathrm{vec}\left[\begin{array}{c} {\left[\begin{array}{ll} a_0 & a_2 \\ a_1 & a_3 \end{array}\right]} \\ {\left[\begin{array}{ll} a_4 & a_6 \\ a_5 & a_7 \end{array}\right]} \\ {\left[\begin{array}{ll} a_8 & a_{10} \\ a_9 & a_{11} \end{array}\right]} \end{array}\right]=\left[\mathrm{vec}\left[\begin{array}{ll} a_0 & a_2 \\ a_1 & a_3 \end{array}\right] \quad \mathrm{vec}\left[\begin{array}{ll} a_4 & a_6 \\ a_5 & a_7 \end{array}\right] \quad \mathrm{vec}\left[\begin{array}{ll} a_8 & a_{10} \\ a_9 & a_{11} \end{array}\right]\right]=\left[\begin{array}{lll} a_0 & a_4 & a_8 \\ a_1 & a_5 & a_9 \\ a_2 & a_6 & a_{10} \\ a_3 & a_7 & a_{11} \end{array}\right]. $$

This allows us to more easily perform tensor contractions when applying tensor-calculus chain rules:

$$ \begin{aligned} (\mathrm{vec} A)^T \mathrm{vec} \mathbf{B} & =\left(\mathrm{vec}\left[\begin{array}{l} {\left[\begin{array}{ll} a_0 & a_2 \\ a_1 & a_3 \end{array}\right]} \\ {\left[\begin{array}{ll} a_4 & a_6 \\ a_5 & a_7 \end{array}\right]} \\ {\left[\begin{array}{ll} a_8 & a_{10} \\ a_9 & a_{11} \end{array}\right]} \end{array}\right]\right)^T \mathrm{vec}\left[\begin{array}{ll} b_0 & b_2 \\ b_1 & b_3 \end{array}\right] =\left[\begin{array}{lll} a_0 & a_4 & a_8 \\ a_1 & a_5 & a_9 \\ a_2 & a_6 & a_{10} \\ a_3 & a_7 & a_{11} \end{array}\right]^T\left[\begin{array}{l} b_0 \\ b_1 \\ b_2 \\ b_3 \end{array}\right] =\left[\begin{array}{c} a_0 b_0+a_1 b_1+a_2 b_2+a_3 b_3 \\ a_4 b_0+a_5 b_1+a_6 b_2+a_7 b_3 \\ a_8 b_0+a_9 b_1+a_{10} b_2+a_{11} b_3 \end{array}\right] . \end{aligned} $$


Other changes include renaming the relative velocity API across C++, Python bindings, and documentation to use more descriptive names (e.g., replacing *_matrix and *_matrix_jacobian with *_jacobian and *_dx_dbeta). It also updates the finite-diff dependency and improves code generation and documentation. The most important changes are grouped below:

API Renaming and Refactoring

  • Renamed C++ and Python API functions for relative velocity calculations from *_matrix/*_matrix_jacobian to *_jacobian/*_dx_dbeta in both implementation (src/ipc/collisions/tangential/edge_edge.cpp, src/ipc/collisions/tangential/edge_vertex.cpp, src/ipc/collisions/tangential/edge_edge.hpp, python/src/collisions/tangential/tangential_collision.cpp, python/src/tangent/relative_velocity.cpp) and documentation (docs/source/cpp-api/tangent.rst, docs/source/python-api/tangent.rst). This makes the API more descriptive and consistent. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13]

  • Changed the shapes and signatures of tangent basis Jacobian functions to reflect correct output dimensions, improving mathematical correctness and clarity. [1] [2] [3]

Code Generation and Utilities

  • Improved the generate_code function in notebooks/generate_cpp_code.py to better infer C++ types, handle boolean expressions, and apply regex-based C++ code cleanups for generated code. [1] [2]

  • Refactored file writing logic in code generation utilities to use consistent formatting and updated subprocess calls for clang-format.

  • Fixed the Jacobian computation in notebooks/utils.py to use correct matrix shapes and flattening order.

Documentation and Notebook Updates

  • Updated example code and formatting in notebooks/tangent_basis_grad.ipynb for clarity and compatibility with newer Python versions. [1] [2] [3]

Dependency Update

  • Updated the finite-diff library version in cmake/recipes/finite_diff.cmake from 1.0.3 to 1.0.4.

Type of change

  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Rename relative_velocity_matrix to relative_velocity_jacobian across
C++ API, Python bindings, and docs. Fix tangent-basis and normal
Jacobian/Hessian matrix dimensions and related function signatures
(MatrixMax sizes and Eigen::ConstRef usage). Update codegen and
notebooks to emit correct C++ types/formatting, bump finite-diff
dependency, and update tests/includes accordingly
Rename and correct tangential collision method signatures and MatrixMax
sizes for plane-vertex collisions. Rename helpers:
relative_velocity_matrix -> relative_velocity_jacobian and
relative_velocity_matrix_jacobian -> relative_velocity_dx_dbeta.
Update tests to use the new APIs, replace usages of << std::endl with
"\n", and add reorder_hessian (with include) to handle DOF layout
reordering in tests.
Switch to a 3rd-order vectorized representation for dΓ/dβ: each column
is vec(∂Γ/∂β) of size dim*ndof. Update headers and implementations to
the
new return types and indexing (tangent/relative_velocity, tangential/*
and tangential_potential). Adjust tests to use
fd::finite_jacobian_tensor<3>.
@codecov
Copy link

codecov bot commented Mar 1, 2026

Codecov Report

❌ Patch coverage is 99.62640% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.18%. Comparing base (6329dce) to head (627efaf).

Files with missing lines Patch % Lines
src/ipc/collisions/tangential/vertex_vertex.cpp 50.00% 2 Missing ⚠️
src/ipc/potentials/tangential_potential.cpp 98.11% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #219   +/-   ##
=======================================
  Coverage   97.17%   97.18%           
=======================================
  Files         161      161           
  Lines       24748    24705   -43     
  Branches      890      888    -2     
=======================================
- Hits        24050    24009   -41     
+ Misses        698      696    -2     
Flag Coverage Δ
unittests 97.18% <99.62%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@zfergus zfergus marked this pull request as ready for review March 1, 2026 13:03
Copilot AI review requested due to automatic review settings March 1, 2026 13:03
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates derivative/Jacobian tensor layouts (moving to a consistent “vectorized 3rd-order tensor” convention) and refactors the tangential relative-velocity API across C++, Python bindings, tests, and docs to use more descriptive names (*_jacobian, *_dx_dbeta).

Changes:

  • Renames relative velocity “matrix” APIs to “jacobian” APIs and updates downstream uses (tangential collisions, potentials, Python bindings, docs).
  • Changes tangent-basis Jacobian and normal-Hessian shapes/layouts to be consistent with a 3rd-order tensor vectorization convention; updates signed-distance Hessian contractions accordingly.
  • Updates/extends finite-difference tests (and bumps finite-diff to 1.0.4), plus improves notebook-based codegen utilities.

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/src/tests/test_collision_mesh.cpp Adjusts expected gradient/Hessian ordering via new DOF-layout helpers.
tests/src/tests/dof_layout.hpp Adds dense Hessian reorder helper to match VERTEX_DERIVATIVE_LAYOUT.
tests/src/tests/tangent/test_tangent_basis.cpp Updates FD Jacobian checking for tensor-shaped outputs.
tests/src/tests/tangent/test_relative_velocity.cpp Adds FD checks for new *_dx_dbeta APIs.
tests/src/tests/collisions/test_plane_vertex_collisions.cpp Updates test calls to renamed tangential collision API.
tests/src/tests/candidates/test_normals.cpp Switches Hessian FD checks to tensor Jacobian routine; updates output printing.
src/ipc/tangent/tangent_basis.hpp Updates tangent-basis Jacobian signatures/shapes and autogen comments.
src/ipc/tangent/tangent_basis.cpp Implements new Jacobian shapes and updates generated/autogen code.
src/ipc/tangent/relative_velocity.hpp Renames APIs and changes derivative-return types to *_dx_dbeta.
src/ipc/tangent/relative_velocity.cpp Implements renamed Jacobians and new vectorized dΓ/dβ conventions.
src/ipc/collisions/tangential/tangential_collision.hpp Renames virtual API and updates derivative tensor return type.
src/ipc/collisions/tangential/vertex_vertex.hpp Updates overrides to new tangent-basis Jacobian and relative-velocity API names/types.
src/ipc/collisions/tangential/vertex_vertex.cpp Updates implementations to call renamed relative-velocity routines.
src/ipc/collisions/tangential/plane_vertex.hpp Updates overrides to new Jacobian/tensor APIs.
src/ipc/collisions/tangential/plane_vertex.cpp Adjusts zero Jacobian/tensor sizing for plane-vertex collision.
src/ipc/collisions/tangential/edge_vertex.hpp Updates overrides to new Jacobian/tensor APIs.
src/ipc/collisions/tangential/edge_vertex.cpp Updates implementation to call renamed relative-velocity routines.
src/ipc/collisions/tangential/edge_edge.hpp Updates overrides to new Jacobian/tensor APIs.
src/ipc/collisions/tangential/edge_edge.cpp Updates implementation to call renamed relative-velocity routines.
src/ipc/collisions/tangential/face_vertex.hpp Updates overrides to new Jacobian/tensor APIs.
src/ipc/collisions/tangential/face_vertex.cpp Updates implementation to call renamed relative-velocity routines.
src/ipc/potentials/tangential_potential.cpp Refactors force Jacobian assembly to use the new tensor-layout conventions.
src/ipc/geometry/normal.hpp Updates cross-product Jacobian and normal Hessian return shapes.
src/ipc/geometry/normal.cpp Implements new Hessian/Jacobian shapes and updates normalization Hessian contraction loops.
src/ipc/distance/signed/point_plane.cpp Updates normal Hessian contraction to match new (27×9) storage.
src/ipc/distance/signed/point_line.cpp Updates normal Hessian contraction to match new (12×6) storage.
src/ipc/distance/signed/line_line.cpp Updates normal Hessian contraction to match new (36×12) storage.
python/src/tangent/relative_velocity.cpp Renames exported Python functions to *_jacobian / *_dx_dbeta and updates signatures.
python/src/collisions/tangential/tangential_collision.cpp Renames bound methods to relative_velocity_jacobian / relative_velocity_dx_dbeta.
docs/source/python-api/tangent.rst Updates referenced Python API names for relative velocity Jacobians/tensors.
docs/source/cpp-api/tangent.rst Updates referenced C++ API names for relative velocity Jacobians/tensors.
cmake/recipes/finite_diff.cmake Bumps finite-diff dependency from 1.0.3 to 1.0.4.
notebooks/utils.py Fixes matrix-valued Jacobian helper to produce correct shape/order.
notebooks/generate_cpp_code.py Improves codegen type inference and post-processing regex cleanups.
notebooks/tangent_basis_grad.ipynb Notebook formatting/code adjustments supporting updated codegen pipeline.
Comments suppressed due to low confidence (9)

src/ipc/tangent/relative_velocity.cpp:105

  • edge_edge_relative_velocity_dx_dbeta() does not use coords (the derivative is constant). Consider omitting the parameter name or marking it unused to avoid compiler warnings.
Eigen::Matrix<double, 36, 2>
edge_edge_relative_velocity_dx_dbeta(Eigen::ConstRef<Eigen::Vector2d> coords)
{

src/ipc/tangent/relative_velocity.cpp:154

  • point_triangle_relative_velocity_dx_dbeta() does not use coords (the derivative is constant). Consider omitting the parameter name or marking it unused to avoid compiler warnings.
Eigen::Matrix<double, 36, 2> point_triangle_relative_velocity_dx_dbeta(
    Eigen::ConstRef<Eigen::Vector2d> coords)
{

src/ipc/collisions/tangential/vertex_vertex.cpp:94

  • For vertex-vertex collisions closest_point is empty (asserted), so returning a (dim·ndof)×0 matrix here would better match the relative_velocity_dx_dbeta(closest_point) contract ("wrt closest points"). Returning a 1-column zero matrix can be surprising for API users/bindings.
MatrixMax<double, 36, 2>
VertexVertexTangentialCollision::relative_velocity_dx_dbeta(
    Eigen::ConstRef<VectorMax2d> _closest_point) const
{
    assert(_closest_point.size() == 0);
    return point_point_relative_velocity_dx_dbeta(dim());
}

tests/src/tests/tangent/test_tangent_basis.cpp:48

  • fd::compare_jacobian(J, J_fd) is computed twice (once in CHECK and again in the if). Consider storing the result in a local bool so the comparison (and any internal work) is done only once.
    tests/src/tests/tangent/test_relative_velocity.cpp:115
  • fd::compare_jacobian(J_analytical, J_numerical) is called twice (CHECK and then again in the if). Consider caching the comparison result in a bool to avoid repeating the computation.
    src/ipc/tangent/tangent_basis.hpp:21
  • The return-value doc for the *_tangent_basis_jacobian() functions is misleading: the Jacobian’s columns are derivatives w.r.t. the input DOFs, not “basis vectors”. Please update the comment to describe the Jacobian layout (e.g., vec(basis) in column-major) and what each column corresponds to.
/// @brief Compute the Jacobian of the tangent basis for the point-point pair.
/// @param p0 First point
/// @param p1 Second point
/// @return A (3*2)x6 matrix whose columns are the basis vectors.
MatrixMax<double, 6, 6> point_point_tangent_basis_jacobian(

src/ipc/tangent/relative_velocity.hpp:78

  • The docstring for edge_edge_relative_velocity_jacobian() still mentions a dim parameter, but the function no longer takes dim. Please remove/update the @param dim line so the documentation matches the signature.
/// @brief Compute du/dx where u is the relative velocity of two edges
/// @param dim Dimension (2 or 3)
/// @param coords Two parametric coordinates of the closest points on the edges
/// @return The relative velocity Jacobian du/dx
Eigen::Matrix<double, 3, 12>
edge_edge_relative_velocity_jacobian(Eigen::ConstRef<Eigen::Vector2d> coords);

src/ipc/tangent/relative_velocity.cpp:58

  • point_edge_relative_velocity_dx_dbeta() doesn’t use alpha (the derivative is constant). To avoid -Wunused-parameter warnings (often treated as errors), consider omitting the parameter name or marking it unused (e.g., const double /*alpha*/).
VectorMax<double, 27>
point_edge_relative_velocity_dx_dbeta(const int dim, const double alpha)
{

cmake/recipes/finite_diff.cmake:10

  • The CPMAddPackage call for zfergus/finite-diff is pinned only to a version tag (@1.0.4), which is a mutable reference that can be retagged to point to malicious code. If the upstream repository or tag is compromised, your build system will automatically fetch and compile attacker-controlled code with access to build artifacts and any CI secrets. To reduce supply-chain risk, pin this dependency to an immutable identifier (e.g., a specific commit hash) as done for your other CMake third-party recipes.
CPMAddPackage("gh:zfergus/finite-diff@1.0.4")

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@zfergus zfergus merged commit eba9e31 into main Mar 1, 2026
25 checks passed
@zfergus zfergus deleted the update-jacobian-layout branch March 1, 2026 13:26
@zfergus zfergus added this to the v1.6.0 milestone Mar 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants