Invariant

Direct computation of invariant(s)

ndmap.invariant.invariant(order: tuple[int, ...], state: torch.Tensor, knobs: list[torch.Tensor], observable: Callable, data: list, *, threshold: float = 1e-09, solve: Callable | None = None, jacobian: Callable | None = None) tuple[list, list][source]

Compute Taylor invariant for a given derivative table

Parameters:
  • order (tuple[int, ...]) – computation order

  • state (State) – state fixed point

  • knobs (Knobs) – knobs value

  • observable (Observable) – invariant guess

  • data (Table) – table mapping representation

  • threshold (float, default=1.0E-9) – threshold value

  • solve (Optional[Callable]) – linear solver(matrix, vecor)

  • jacobian (Optional[Callable]) – torch.func.jacfwd (default) or torch.func.jacrev

Return type:

tuple[Table, list]

Examples

>>> import torch
>>> from ndmap.util import nest
>>> from ndmap.derivative import derivative
>>> from ndmap.series import series
>>> from ndmap.series import clean
>>> from ndmap.yoshida import yoshida
>>> def fn(x, t): q, p = x ; return torch.stack([q, p - t*q - t*q**2])
>>> def gn(x, t): q, p = x ; return torch.stack([q + t*p, p])
>>> l = torch.tensor(1.0, dtype=torch.float64)
>>> x = torch.tensor([0.0, 0.0], dtype=torch.float64)
>>> t = derivative(2, nest(100, lambda x: yoshida(0, 1, True, [fn, gn])(x, l/100)), x)
>>> i, _ = invariant((3, ), x, [], lambda x: 1/2*(x**2).sum(), t, threshold=1.0E-6)
>>> clean(series((2, ), (3, ), i))
{(2, 0): tensor([0.5000], dtype=torch.float64),
(0, 2): tensor([0.5000], dtype=torch.float64),
(3, 0): tensor([0.3333], dtype=torch.float64)}
>>> from ndmap.util import nest
>>> from ndmap.derivative import derivative
>>> from ndmap.series import series
>>> from ndmap.series import clean
>>> from ndmap.yoshida import yoshida
>>> def fn(x, t, k): q, p = x ; k, = k ; return torch.stack([q, p - t*q - t*(1 + k)*q**2])
>>> def gn(x, t, k): q, p = x ; k, = k ; return torch.stack([q + t*p, p])
>>> l = torch.tensor(1.0, dtype=torch.float64)
>>> x = torch.tensor([0.0, 0.0], dtype=torch.float64)
>>> k = torch.tensor([0.0], dtype=torch.float64)
>>> y = nest(100, lambda x, k: yoshida(0, 2, True, [fn, gn])(x, l/100, k))
>>> t = derivative((2, 1), y, x, k)
>>> i, _ = invariant((3, 1), x, [k], lambda x, k: 1/2*(x**2).sum(), t, threshold=1.0E-6)
>>> clean(series((2, 1), (3, 1), i))
{(2, 0, 0): tensor([0.5000], dtype=torch.float64),
(0, 2, 0): tensor([0.5000], dtype=torch.float64),
(3, 0, 0): tensor([0.3333], dtype=torch.float64),
(3, 0, 1): tensor([0.3333], dtype=torch.float64)}

Note

Input table is assumed to be origin preserving Initial guess is required to avoid trivial solution