Skip to content

Eigen Integration

tax::TaylorExpansion ships with a NumTraits specialisation so it behaves as a first-class Eigen scalar. Anywhere you can write Eigen::Matrix<double, R, C>, you can write Eigen::Matrix<tax::TE<N, M>, R, C> and Eigen's linear-algebra machinery just works — at the cost of one Cauchy product per scalar multiplication.

#include <tax/tax.hpp>

using TE2 = tax::TE<3, 2>;
auto x = TE2::variable<0>({1.0, 2.0});
auto y = TE2::variable<1>({1.0, 2.0});

Eigen::Vector2<TE2> F = { tax::sin(x) * tax::cos(y),
                          tax::exp(x + y) };

auto val = tax::value(F);             // Eigen::Vector2d of constant terms
auto J   = tax::jacobian(F);          // 2×2 Jacobian
auto vp  = tax::eval(F, Eigen::Vector2d{0.05, -0.1});  // displace + evaluate

The complete signature list lives in the Eigen API reference; this page is the how-to.


What's provided

Capability Helper
Build coordinate variables as an Eigen vector tax::variables<TE>(x0)
Strip constant terms tax::value(F)
Evaluate at a displacement tax::eval(F, dx)
Element-wise compile-time partial tax::derivative<Alpha...>(F)
Gradient of a scalar TE tax::gradient(f)
Hessian of a scalar TE tax::hessian(f)
Jacobian of a vector TE tax::jacobian(F)
Local map inverse tax::invert(F)

All helpers are free functions in namespace tax.


NumTraits

The specialisation in tax/eigen.hpp reports IsComplex = 0, IsInteger = 0, IsSigned = 1, RequireInitialization = 1, and cost estimates proportional to the monomial count \(\binom{N+M}{M}\). This makes Eigen prefer cache-friendly algorithms when working with TE-valued matrices.

You can use any Eigen routine that does not require additional traits (determinant via FullPivLU, dense matrix products, .norm() on a vector, fixed-size linear solves). Eigen's sparse-matrix routines are not (yet) wired up.


Vector-valued functions

A natural use is propagating a Taylor expansion of a vector function — for example, the right-hand side of an ODE:

// f : R^2 → R^2,  f(x, y) = (x² − y, x · y)
auto f = [](const auto& v) {
    using S = std::decay_t<decltype(v)>;
    S out;
    out(0) = v(0) * v(0) - v(1);
    out(1) = v(0) * v(1);
    return out;
};

auto v = tax::variables<tax::TE<3, 2>>(Eigen::Vector2d{1.0, 0.5});
auto F = f(v);                        // Eigen::Vector2<TE<3, 2>>

auto J = tax::jacobian(F);            // 2×2 Jacobian at (1, 0.5)
auto H0 = tax::hessian(F(0));         // 2×2 Hessian of F₀ at (1, 0.5)

The same idea drives generic ODE integrators built on tax: a user-written generic RHS lambda can be instantiated on plain Eigen::Matrix<T, D, 1> for the RK steppers and on Eigen::Matrix<tax::TE<N, M>, D, 1> for the Taylor stepper without source changes.


Map inversion

tax::invert(F) formally inverts a square polynomial map \(\mathbf{P} : \mathbb{R}^M \to \mathbb{R}^M\) by Picard iteration on its non-constant part:

auto v = tax::variables<tax::TE<5, 2>>(Eigen::Vector2d::Zero());
Eigen::Vector2<tax::TE<5, 2>> F = { v(0) + v(0)*v(1),
                                    v(1) + 0.5 * v(0)*v(0) };

auto Finv = tax::invert(F);
// Composition Finv ∘ F is the identity up to order N

The linear part of the map must be invertible; otherwise invert throws std::invalid_argument. The inverse is returned in the same Eigen shape as the input.

invert operates on the perturbation part only (constant terms are dropped). For a flow map \(\mathbf F(\mathbf x_0 + \boldsymbol{\delta x}) = \mathbf y_0 + \mathbf{P}(\boldsymbol{\delta x})\), recover the displacement-to-displacement inverse by evaluating the returned map at \(\mathbf y - \mathbf y_0\) and adding \(\mathbf x_0\) yourself.

For the underlying math — the linear/nonlinear split, the fixed-point form, and why the Picard iteration terminates at order \(N\) — see Internals / Map Inversion.