bijx.cg

Crouch-Grossmann integration methods for Lie group ordinary differential equations.

This module implements geometric integration schemes for ODEs on matrix Lie groups, using the Crouch-Grossmann family of methods. These ensure solutions remain on the manifold throughout integration (up to numerical accuracy).

Key concepts:
  • Lie group ODEs: Differential equations \(\dot{g} = A(t,g) g\) where \(g(t) \in G\)

  • Crouch-Grossmann schemes: Runge-Kutta-type methods using matrix exponentials

  • Butcher tableaux: Coefficient arrays defining integration schemes

Mathematical background: For ODEs on matrix Lie groups of the form \(\dot{g} = f(t,g)\) where \(f\) takes values in the Lie algebra, Crouch-Grossmann methods approximate: \(g_{n+1} = \exp(\sum_i b_i k_i) g_n\)

where \(k_i\) are stage vectors computed via the tableau coefficients. This ensures \(g_{n+1} \in G\) whenever \(g_n \in G\).

Module Attributes

EULER

Forward Euler method (1st-order, 1 stage).

CG2

Second-order Crouch-Grossmann method (2nd-order, 2 stages).

CG3

Third-order Crouch-Grossmann method (3rd-order, 3 stages).

Functions

cg_stage(y0, vect, is_lie, ai, step_size)

Compute intermediate state for Crouch-Grossmann stage.

crouch_grossmann(vector_field, y0, args, t0, ...)

Integrate ODE using Crouch-Grossmann method with custom differentiation.

crouch_grossmann_step(is_lie, tableau, ...)

Execute single step of Crouch-Grossmann integration method.

stage_reduce(y0, is_lie, *deltas)

Accumulate stage increments using appropriate group operation.

transport(vect, z[, inverse])

Transport Lie algebra element to tangent space at group element.

Classes

ButcherTableau

Butcher tableau defining coefficients for Runge-Kutta integration schemes.

class bijx.cg.ButcherTableau[source]

Bases: object

Butcher tableau defining coefficients for Runge-Kutta integration schemes.

Encodes the coefficient structure for explicit Runge-Kutta methods in the standard Butcher tableau format. Used to define Crouch-Grossmann schemes for integration on Lie groups.

The tableau has the structure:

c_1 | a_11  a_12  ...  a_1s
c_2 | a_21  a_22  ...  a_2s
 :  |  :     :    ⋱    :
c_s | a_s1  a_s2  ...  a_ss
----+----------------------
    | b_1   b_2   ...  b_s

For explicit methods: \(a_{ij} = 0\) for \(j \geq i\). Consistency requires: \(c_i = \sum_j a_{ij}\) and \(\sum_i b_i = 1\).

stages: int

Number of stages \(s\) in the method.

a: tuple[tuple[int, ...]]

Coefficient matrix \((a_{ij})\) as nested tuples.

b: tuple[int, ...]

Weight vector \((b_i)\) as tuple.

c: tuple[int, ...]

Node vector \((c_i)\) as tuple (computed from \(a\)).

classmethod from_ab(a, b)[source]

Construct Butcher tableau from coefficient matrix and weights.

Creates a ButcherTableau instance from the \(a\) matrix and \(b\) vector, automatically computing the node vector \(c_i = \sum_j a_{ij}\) and validating consistency conditions.

Parameters:
  • a – Coefficient matrix as list of lists, shape \((s, s)\).

  • b – Weight vector as list, length \(s\).

Returns:

ButcherTableau instance with computed node vector.

Raises:

AssertionError – If consistency conditions are violated: - Weights don’t sum to 1: \(\sum_i b_i \neq 1\) - Method is not explicit: \(a_{ij} \neq 0\) for \(j \geq i\) - Dimensions don’t match

Example

>>> # Second-order Crouch-Grossmann method
>>> cg2 = ButcherTableau.from_ab(
...     a=[[0, 0], [1/2, 0]], b=[0, 1]
... )
replace(**updates)

Returns a new object replacing the specified fields with new values.

bijx.cg.EULER = ButcherTableau(stages=1, a=((0,),), b=(1,), c=(0,))

Forward Euler method (1st-order, 1 stage).

The simplest integration scheme: \(y_{n+1} = y_n + h f(t_n, y_n)\).

For Lie groups: \(g_{n+1} = \exp(h A(t_n, g_n)) g_n\).

bijx.cg.CG2 = ButcherTableau(stages=2, a=((0, 0), (0.5, 0)), b=(0, 1), c=(0, 0.5))

Second-order Crouch-Grossmann method (2nd-order, 2 stages).

A two-stage method achieving second-order accuracy for Lie group ODEs. This is the Lie group analogue of the classical midpoint rule.

Stages: 1. \(k_1 = A(t_n, g_n)\) 2. \(k_2 = A(t_n + h/2, \exp(h k_1/2) g_n)\)

Update: \(g_{n+1} = \exp(h k_2) g_n\)

bijx.cg.CG3 = ButcherTableau(stages=3, a=((0, 0, 0), (0.75, 0, 0), (0.5509259259259259, 0.1574074074074074, 0)), b=(0.2549019607843137, -0.6666666666666666, 1.411764705882353), c=(0, 0.75, 0.7083333333333334))

Third-order Crouch-Grossmann method (3rd-order, 3 stages).

bijx.cg.transport(vect, z, inverse=False)[source]

Transport Lie algebra element to tangent space at group element.

Performs parallel transport of a Lie algebra element (tangent vector at identity) to the tangent space at an arbitrary group element.

For right-invariant vector fields: \(X_g = X_e \cdot g\) For left-invariant vector fields: \(X_g = g \cdot X_e\)

Here, we choose the convention of right-invariant vector fields.

The transport maps vectors from \(T_e G\) (Lie algebra) to \(T_g G\).

Parameters:
  • vect – Lie algebra element (tangent vector at identity).

  • z – Group element providing the transport destination.

  • inverse – If True, transport from \(T_g G\) back to \(T_e G\).

Returns:

Transported tangent vector at the specified group element.

Example

>>> # Transport SU(2) generator to arbitrary group element
>>> X = bijx.lie.SU2_GEN[0]  # Generator at identity
>>> g = bijx.lie.sample_haar(rng, n=2)  # Arbitrary SU(2) element
>>> X_g = transport(X, g)  # Tangent vector at g
>>> X_g.shape
(2, 2)
bijx.cg.stage_reduce(y0, is_lie, *deltas)[source]

Accumulate stage increments using appropriate group operation.

Combines multiple increments using either vector addition (for Euclidean spaces) or matrix exponential composition (for Lie groups).

For Euclidean space: \(y = y_0 + \sum_i \delta_i\) For Lie groups: \(g = \left(\prod_i \exp(\delta_i)\right) g_0\)

The Lie group version ensures the result remains on the manifold by using the exponential map and group multiplication.

Parameters:
  • y0 – Initial state (group element or vector).

  • is_lie – Boolean indicating whether to use Lie group operations.

  • *deltas – Sequence of increments to accumulate.

Returns:

Updated state after accumulating all increments.

bijx.cg.cg_stage(y0, vect, is_lie, ai, step_size)[source]

Compute intermediate state for Crouch-Grossmann stage.

Combines previous stage vectors according to the Butcher tableau coefficients.

The computation follows: \(g_i = \left(\prod_j \exp(h a_{ij} k_j)\right) g_0\)

where \(k_j\) are stage vectors and \(a_{ij}\) are tableau coefficients.

Parameters:
  • y0 – Initial state for the current step.

  • vect – List of stage vectors from previous stages.

  • is_lie – Boolean tree indicating which components use Lie group operations.

  • ai – Row of Butcher tableau coefficients for current stage.

  • step_size – Integration step size \(h\).

Returns:

Intermediate state for evaluating the next stage vector.

bijx.cg.crouch_grossmann_step(is_lie, tableau, vector_field, step_size, t, y0)[source]

Execute single step of Crouch-Grossmann integration method.

Performs one integration step using the specified Butcher tableau, computing all intermediate stages and the final update.

Parameters:
  • is_lie – Pytree of booleans indicating Lie group vs Euclidean components.

  • tableau – ButcherTableau defining the integration method.

  • vector_field – Function \((t, g) \mapsto A\) where \(A\) is in the Lie algebra.

  • step_size – Integration step size \(h\).

  • t – Current time.

  • y0 – Current state (group element or vector).

Returns:

Updated state after one integration step.

Important

The vector field must return values in the Lie algebra for Lie group components, enabling the exponential map to produce valid group elements.

bijx.cg.crouch_grossmann(vector_field, y0, args, t0, t1, step_size, is_lie, tableau=ButcherTableau(stages=1, a=((0,),), b=(1,), c=(0,)))[source]

Integrate ODE using Crouch-Grossmann method with custom differentiation.

Solves: \(\dot{g} = f(t, g, \text{args})\) from \(t_0\) to \(t_1\)

For Lie groups: \(\dot{g} = A(t, g) g\) where \(A(t, g) \in \mathfrak{g}\) For Euclidean: \(\dot{y} = f(t, y)\) (standard ODE)

Parameters:
  • vector_field – Function \((t, g, \text{args}) \mapsto A\) where \(A\) is in the Lie algebra for Lie group components.

  • y0 – Initial condition (group element or vector).

  • args – Additional parameters passed to vector_field.

  • t0 – Initial time.

  • t1 – Final time.

  • step_size – Integration step size (positive for forward integration).

  • is_lie – Boolean tree indicating which components use Lie group operations.

  • tableau – ButcherTableau defining the integration method (default: Euler).

Returns:

Solution at time \(t_1\).

Example

>>> def rigid_body_eqs(t, R, omega):
...     # R(t) ∈ SO(3), omega is angular velocity
...     Omega = skew_symmetric(omega)
...     return Omega  # Lie algebra element
>>> R0 = jnp.eye(3)  # Initial orientation
>>> omega = jnp.array([1.0, 0.0, 0.0])  # Rotation about x-axis
>>> R_final = crouch_grossmann(
...     rigid_body_eqs, R0, omega, 0.0, 1.0, 0.01, True, CG2
... )

Important

The vector field must return values in the Lie algebra for Lie group components, enabling the exponential map to produce valid group elements.