Automatic Differentiation in Geometry Processing Made Simple

You must be signed in to change notification settings

## Folders and files

NameName
Last commit message
Last commit date

## History

TinyAD is a C++ header-only library for second-order automatic differentiation. Small dense problems are differentiated in forward mode, which allows unrestricted looping and branching. An interface for per-element functions allows convenient differentiation of large sparse problems, which are typical in geometry processing on meshes. For more details see our paper or watch our talk.

# Integration

TinyAD has been tested on Linux, Mac, and Windows (VS >= 2017). It only requires:

• A C++17 compiler
• Eigen (e.g. `sudo apt-get install libeigen3-dev`)

To use TinyAD in your existing project, include either `TinyAD/Scalar.hh`, `TinyAD/ScalarFunction.hh`, or `TinyAD/VectorFunction.hh`.

A minimal example project using TinyAD with libigl is available here.

# Basic Usage

We provide the scalar type `TinyAD::Double<k>` as a drop-in replacement for `double`. For small problems, simply choose the number of variables `k` and generate a vector of active variables. Then, perform computations as usual (e.g. using Eigen) and query the gradient and Hessian of any intermediate variable:

```#include <TinyAD/Scalar.hh>

// Choose autodiff scalar type for 3 variables

// Init a 3D vector of active variables and a 3D vector of passive variables
Eigen::Vector3<double> y(2.0, 3.0, 5.0);

// Compute angle using Eigen functions and retrieve gradient and Hessian w.r.t. x
ADouble angle = acos(x.dot(y) / (x.norm() * y.norm()));
Eigen::Matrix3d H = angle.Hess;```

All derivative computations are inlined and thus available for compiler optimization. As no taping is needed in forward mode, any kind of run time branching is possible.

# Sparse Interface

Sparse problems on meshes can be implemented using our `ScalarFunction` or `VectorFunction` interfaces. Just pass a set of variable handles, a set of element handles, and a lambda function to be evaluated for each element. For example, in a planar parametrization problem, the variables are 2D positions per vertex, and the summands of the objective function are defined per face, each accessing 3 vertices:

```#include <TinyAD/ScalarFunction.hh>

// Set up a function with 2D vertex positions as variables

// Add an objective term per triangle. Each connecting 3 vertices
{
// Element is evaluated with either double or TinyAD::Double<6>

// Get variable 2D vertex positions of triangle t
OpenMesh::SmartFaceHandle t = element.handle;
Eigen::Vector2<T> a = element.variables(t.halfedge().to());
Eigen::Vector2<T> b = element.variables(t.halfedge().next().to());
Eigen::Vector2<T> c = element.variables(t.halfedge().from());

return ...
});

// Evaluate the funcion using any of these methods:
double f = func.eval(x);
auto [f, g, H] = func.eval_with_derivatives(x);
auto [f, g, H_proj] = func.eval_with_hessian_proj(x);
...```

Handle types from multiple mesh data structures are supported, e.g., OpenMesh, polymesh, geometry-central, or libigl-style matrices. Support for new types can be added by overloading a single function (see `TinyAD/Support/Common.hh`).

# Examples

To get started, take a look at one of our TinyAD-Examples. We implement objective functions and basic solvers for typical geometry processing tasks using various mesh libraries.

## Surface Mesh Parametrization

We compute a piecewise linear map from a disk-topology triangle mesh to the plane and optimize the symmetric Dirichlet energy via a Projected-Newton solver. This can be the basis to experiment with more specialized algorithms or more complex objective functions. We provide examples using different mesh representations:

## Volume Mesh Deformation

In this example, we compute a 3D deformation of a tetrahedral mesh by optimizing different distortion energies subject to position constraints:

`deformation.cc`

## Frame Field Optimization

Here, we show how to re-implement the non-linear frame field optimization algorithm presented in Integrable PolyVector Fields [Diamanti et al. 2015], using very little code. Given an input frame field (two tangent vectors per triangle), the algorithm optimizes an objective based on complex polynomials via a Gauss-Newton method:

`polycurl_reduction.cc`

## Manifold Optimization

We optimize a map from a genus 0 surface to the sphere using a technique from manifold optimization. Vertex trajectories on the sphere are parametrized via tangent vectors and a retraction operator:

`manifold_optimization.cc`

In this example, we optimize the 3D vertex positions of a quad mesh for face planarity. We implement one of the objective terms from Geometric Modeling with Conical Meshes and Developable Surfaces [Liu 2006]:

`quad_planarization.cc`

# Advanced Usage and Common Pitfalls

• Internal floating point types other than `double` can be used via `TinyAD::Scalar<k, T>`.
• A gradient-only mode is availabe via `TinyAD::Scalar<k, T, false>`.
• Use `to_passive(...)` to explicitly cast an active variable back to its scalar type without derivatives. E.g. to implement assertions or branching which should not be differentiated.
• Avoid using the `auto` keyword when when working with Eigen expressions. This is a limitation of Eigen and can produce unexpected results due to the deleted temporary objects.
• Use e.g. `cos(...)` instead of `std::cos(...)`.
• A common source for errors in the implementation of objective functions (per-element lambdas passed to `func.add_elements(...)`) are multiple return statements of different types. This may lead to a compiler error, but can be prevented by explicitly stating the correct return type via:
`func.add_elements<...>(..., [&] (auto& element) -> TINYAD_SCALAR_TYPE(element) { return ... });`
• Note that calls to math functions involving TinyAD types are only legal if the derivatives exist and are finite for the given function argument. E.g. it is illegal to call `acos(x)` with `x==1.0` since the derivative of acos is unbounded at 1.0.

# Unit Tests

When contributing to TinyAD, please run and extend the unit tests located in `TinyAD/tests`.

You can build and run the tests via:

``````mkdir build
cd build
make -j4
``````

Alternatively, you can use the TinyAD-Examples project which builds the unit tests by default.

# Authors

We thank all test users and contributors. In particular: Alexandra Heuschling, Anton Florey, Dörte Pieper, Joe Jakobi, Philipp Domagalski, and David Jourdan.

``````@article{schmidt2022tinyad,
author={Schmidt, Patrick and Born, Janis and Bommes, David and Campen, Marcel and Kobbelt, Leif},
year={2022},
journal={Computer Graphics Forum},
volume={41},
number={5},
}
``````

Automatic Differentiation in Geometry Processing Made Simple

## Releases

No releases published

## Packages 0

No packages published

•
•
•
•