tidal.symbolic package#

Symbolic computation layer for Lagrangian-to-PDE pipeline.

This package provides the Python-side interface for loading and processing field equations derived symbolically from Lagrangians via Mathematica/xAct.

class tidal.symbolic.BoundaryCondition(type, value=None, derivative=None, gamma=None)[source]#

Bases: object

Boundary condition for one spatial axis.

Parameters:
type#

One of “periodic”, “dirichlet”, “neumann”, or “robin”.

Type:

str

value#

Fixed value for Dirichlet BCs, or Robin beta.

Type:

float | None

derivative#

Fixed normal derivative for Neumann BCs.

Type:

float | None

gamma#

Robin coefficient gamma in d_n f + gamma*f = beta.

Type:

float | None

derivative: float | None = None#
classmethod from_dict(data)[source]#

Create a BoundaryCondition from a dictionary.

Raises:

ValueError – If the BC type is not recognized.

Parameters:

data (Mapping[str, Any])

Return type:

BoundaryCondition

gamma: float | None = None#
to_side_bc()[source]#

Convert to a SideBCSpec for the operator layer.

Raises:

ValueError – If the BC type is “periodic” (not representable as a side BC).

Return type:

SideBCSpec

value: float | None = None#
type: str#
class tidal.symbolic.ComponentEquation(field_name, field_index, time_derivative_order, rhs_terms, constraint_solver=<factory>)[source]#

Bases: object

Equation of motion for a single field component.

For a wave-type equation:

d^2/dt^2 field = sum of OperatorTerms

Parameters:
field_name#

Name of the field component (e.g., “A_0”, “phi”).

Type:

str

field_index#

Index of this component in the field array.

Type:

int

time_derivative_order#

Order of the time derivative on the LHS (2 for wave equations).

Type:

int

rhs_terms#

Terms on the RHS of the equation.

Type:

tuple[OperatorTerm, …]

constraint_solver#

Configuration for elliptic constraint solving. Only meaningful when time_derivative_order == 0.

Type:

ConstraintSolverConfig

classmethod from_dict(data, fields_lookup)[source]#

Create a ComponentEquation from a dictionary.

Raises:

ValueError – If the RHS type is not “linear_combination”, or if constraint_solver is enabled for a non-constraint equation.

Parameters:
Return type:

ComponentEquation

field_name: str#
field_index: int#
time_derivative_order: int#
rhs_terms: tuple[OperatorTerm, ...]#
constraint_solver: ConstraintSolverConfig#
class tidal.symbolic.ConstraintSolverConfig(enabled=False, method='auto', boundary_conditions=<factory>, max_iterations=20, tolerance=1e-08)[source]#

Bases: object

Configuration for elliptic constraint solving.

When enabled is True, the constraint equation is solved at each timestep rather than remaining frozen at its initial value.

Solver Methods#

  • “auto” (default): Automatically selects the best solver. Uses FFT for fully periodic grids (O(N log N)), sparse matrix for non-periodic grids (O(N) via LU). Handles all constraint types: Poisson, Helmholtz, algebraic, anisotropic, etc.

  • “fft”: Force FFT solver. Requires fully periodic grid.

  • “matrix”: Force sparse matrix solver. Works with any BCs.

  • “poisson”: Original py-pde Poisson solver. Requires exactly one laplacian(self_field) term with no other self-referencing operators. Will warn if non-laplacian self-terms are present. Backward compatible.

Coupled Constraint Parameters#

When multiple constraints reference each other’s fields, the solver iterates using Gauss-Seidel until convergence or max_iterations. For fully periodic grids, coupled constraints are solved exactly via Fourier-space block solve (no iteration needed).

enabled#

Whether to solve the constraint elliptically. Default False preserves existing frozen-constraint behavior.

Type:

bool

method#

Solver method: “auto”, “fft”, “matrix”, or “poisson”.

Type:

str

boundary_conditions#

Per-axis boundary conditions (e.g., {"x": ..., "y": ...}).

Type:

dict[str, BoundaryCondition]

max_iterations#

Maximum Gauss-Seidel iterations for coupled constraints. Must be >= 1.

Type:

int

tolerance#

Convergence threshold for coupled constraint iteration. Iteration stops when max|field_new - field_old| < tolerance (scaled by field magnitude for robustness). Must be > 0.

Type:

float

enabled: bool = False#
classmethod from_dict(data)[source]#

Create from a dictionary or return default (disabled).

Parameters:

data (Mapping[str, Any] | None) – Parsed constraint_solver block from JSON, or None.

Returns:

Configuration instance.

Return type:

ConstraintSolverConfig

Raises:

ValueError – If method is not one of the recognized solver methods.

max_iterations: int = 20#
method: str = 'auto'#
tolerance: float = 1e-08#
boundary_conditions: dict[str, BoundaryCondition]#
Parameters:
class tidal.symbolic.EquationSystem(n_components, dimension, spatial_dimension, component_names, equations, mass_matrix, coupling_matrix, metadata, coordinates=(), mass_matrix_symbolic=(), coupling_matrix_symbolic=(), canonical=None)[source]#

Bases: object

Complete system of field equations derived from a Lagrangian.

Parameters:
n_components#

Number of field components.

Type:

int

dimension#

Spacetime dimension (e.g., 2 for 1+1D).

Type:

int

spatial_dimension#

Number of spatial dimensions (dimension - 1).

Type:

int

component_names#

Names of field components in order.

Type:

tuple[str, …]

equations#

Equations for each component.

Type:

tuple[ComponentEquation, …]

mass_matrix#

Mass matrix M^2_ij for coupled systems.

Type:

tuple[tuple[float, …], …]

coupling_matrix#

Coupling matrix for field interactions.

Type:

tuple[tuple[float, …], …]

metadata#

Additional metadata (source, gauge, etc.)

Type:

dict[str, Any]

coordinates#

Coordinate names from JSON spacetime.coordinates (e.g., (“t”, “x”, “y”)). Defaults to empty tuple; use effective_coordinates for a guaranteed non-empty result that infers names from dimension when not set.

Type:

tuple[str, …]

canonical#

Canonical momentum and Hamiltonian structure from Legendre transform. Present when the JSON spec includes a "canonical" section (generated by tidal derive for non-linearization theories). None for legacy specs or linearization theories.

Type:

CanonicalStructure | None

canonical: CanonicalStructure | None = None#
coordinates: tuple[str, ...] = ()#
coupling_matrix_symbolic: tuple[tuple[str | None, ...], ...] = ()#
property effective_coordinates: tuple[str, ...]#

Coordinate names, inferred from dimension if not set explicitly.

property equation_map: dict[str, int]#

Map from field name to equation index. Cached on frozen dataclass.

classmethod from_dict(data)[source]#

Create an EquationSystem from a dictionary (parsed JSON).

Raises:
  • ValueError – If the JSON data is invalid or component references are inconsistent.

  • TypeError – If a metadata parameter has an unsupported type.

Parameters:

data (Mapping[str, Any])

Return type:

EquationSystem

property has_constraint_velocity_terms: bool#

True if the Hamiltonian has kinetic coupling between constraint fields.

Detects time_derivative(C_i) x time_derivative(C_j) terms where both C_i and C_j are constraint fields (time_derivative_order == 0). These indicate the naive Hamiltonian H = sum(pi * v) - L is not the correct conserved quantity for this theory (Dirac-Bergmann theory).

See GitHub issue #178 for details.

mass_matrix_symbolic: tuple[tuple[str | None, ...], ...] = ()#
property spatial_coordinates: tuple[str, ...]#

Spatial coordinate names (all except first, which is time).

property state_layout: tuple[tuple[str, str], ...]#

State vector layout as (field_name, slot_type) tuples.

slot_type is “field” or “momentum”. Second-order components produce two entries (field, momentum); first-order/constraint produce one (field).

property state_size: int#

Total number of state fields.

Second-order components contribute 2 slots (field + momentum). First-order and constraint components contribute 1 slot (field only).

property time_orders: tuple[int, ...]#

Per-component time derivative orders.

n_components: int#
dimension: int#
spatial_dimension: int#
component_names: tuple[str, ...]#
equations: tuple[ComponentEquation, ...]#
mass_matrix: tuple[tuple[float, ...], ...]#
coupling_matrix: tuple[tuple[float, ...], ...]#
metadata: dict[str, Any]#
class tidal.symbolic.OperatorTerm(coefficient, operator, field, coefficient_symbolic=None, time_dependent=False, coordinate_dependent=())[source]#

Bases: object

A single term in the RHS of a field equation.

Represents: coefficient * operator(field)

Parameters:
  • coefficient (float)

  • operator (str)

  • field (str)

  • coefficient_symbolic (str | None)

  • time_dependent (bool)

  • coordinate_dependent (tuple[str, ...])

coefficient#

Numeric coefficient for this term.

Type:

float

operator#

Name of the differential operator (“laplacian”, “identity”, “gradient_x”, etc.)

Type:

str

field#

Name of the field this operator acts on.

Type:

str

coefficient_symbolic#

Optional symbolic name for the coefficient (e.g., “m2”, “-kappa”). When present, the coefficient can be overridden at runtime by passing a parameters dict to the PDE constructor.

Type:

str | None

time_dependent#

Whether the coefficient depends on time. For curved spacetime, terms like -2H∂_t φ (Hubble friction) have time-dependent coefficients when the conformal factor Ω(t) varies with time. Default False for flat spacetime.

Type:

bool

coordinate_dependent#

Coordinate names the coefficient depends on (e.g., (“x”, “y”) for position-dependent coefficients on curved spatial surfaces, or (“t”,) for time-dependent). Empty tuple for constant coefficients.

Type:

tuple[str, …]

coefficient_symbolic: str | None = None#
coordinate_dependent: tuple[str, ...] = ()#
classmethod from_dict(data)[source]#

Create an OperatorTerm from a dictionary.

Raises:

ValueError – If required keys are missing or operator is unknown.

Parameters:

data (Mapping[str, Any])

Return type:

OperatorTerm

property position_dependent: bool#

Whether the coefficient depends on spatial coordinates.

Returns True when coordinate_dependent is non-empty (explicit declaration), or when coefficient_symbolic contains a spatial coordinate call pattern such as x[] or y[] (auto-detection for JSON exports that predate the coordinate_dependent field). Time-only dependence (t[]) returns False.

time_dependent: bool = False#
coefficient: float#
operator: str#
field: str#
tidal.symbolic.load_equation_system(json_path)[source]#

Load an equation system from a JSON file.

Parameters:

json_path (Path | str) – Path to the JSON file exported from Mathematica.

Returns:

The parsed equation system.

Return type:

EquationSystem

Raises:

FileNotFoundError – If the JSON file does not exist.

Submodules#

tidal.symbolic.json_loader module#

Load and validate JSON equation specifications from Mathematica/xAct export.

This module provides the data structures and parsing logic for loading field equations that were derived symbolically from Lagrangians.

tidal.symbolic.json_loader.AXIS_LETTERS: tuple[str, ...] = ('x', 'y', 'z', 'w', 'v', 'u')#

Canonical spatial axis letters, ordered by dimension index. Supports up to 6 spatial dimensions (sufficient for all foreseeable physics).

tidal.symbolic.json_loader.is_known_operator(name)[source]#

Check whether an operator name is recognized.

Accepts static operators (identity, laplacian, gradient_x, …), user-registered custom operators (via register_operator), dynamic patterns for generic Nth-order derivatives (derivative_3_x, derivative_5_y, derivative_2x_1y, …), mixed time-space derivative operators (mixed_T2_S2x, mixed_T_S1x, …), and pure higher-order time operators on RHS (d2_t, d3_t, d4_t).

Parameters:

name (str)

Return type:

bool

class tidal.symbolic.json_loader.LHSStructure(expression, time_order, space_order=0)[source]#

Bases: object

Structure describing the left-hand side of a PDE.

Supports different PDE types: - Elliptic (time_order=0): ∇²φ = f (Poisson, Laplace) - Parabolic (time_order=1): ∂_t φ = … (heat, diffusion) - Hyperbolic (time_order=2): ∂²_t φ = … (wave) - Higher order: ∂^n_t φ = …

Parameters:
  • expression (str)

  • time_order (int)

  • space_order (int)

expression#

String representation (e.g., “d2_t(phi_0)”, “d_t(phi)”, “phi”).

Type:

str

time_order#

Order of time derivative on LHS (0, 1, 2, or higher).

Type:

int

space_order#

Order of space derivative on LHS (usually 0).

Type:

int

expression: str#
time_order: int#
space_order: int = 0#
classmethod from_dict(data)[source]#

Create LHSStructure from structured JSON data.

Expected format: {“expression”: “…”, “order”: {“time”: N, “space”: 0}}

Parameters:

data (Mapping[str, Any]) – The structured LHS data from JSON.

Returns:

Parsed LHS structure.

Return type:

LHSStructure

Raises:

ValueError – If order dict does not contain a 'time' key.

class tidal.symbolic.json_loader.OperatorTerm(coefficient, operator, field, coefficient_symbolic=None, time_dependent=False, coordinate_dependent=())[source]#

Bases: object

A single term in the RHS of a field equation.

Represents: coefficient * operator(field)

Parameters:
  • coefficient (float)

  • operator (str)

  • field (str)

  • coefficient_symbolic (str | None)

  • time_dependent (bool)

  • coordinate_dependent (tuple[str, ...])

coefficient#

Numeric coefficient for this term.

Type:

float

operator#

Name of the differential operator (“laplacian”, “identity”, “gradient_x”, etc.)

Type:

str

field#

Name of the field this operator acts on.

Type:

str

coefficient_symbolic#

Optional symbolic name for the coefficient (e.g., “m2”, “-kappa”). When present, the coefficient can be overridden at runtime by passing a parameters dict to the PDE constructor.

Type:

str | None

time_dependent#

Whether the coefficient depends on time. For curved spacetime, terms like -2H∂_t φ (Hubble friction) have time-dependent coefficients when the conformal factor Ω(t) varies with time. Default False for flat spacetime.

Type:

bool

coordinate_dependent#

Coordinate names the coefficient depends on (e.g., (“x”, “y”) for position-dependent coefficients on curved spatial surfaces, or (“t”,) for time-dependent). Empty tuple for constant coefficients.

Type:

tuple[str, …]

coefficient: float#
operator: str#
field: str#
coefficient_symbolic: str | None = None#
time_dependent: bool = False#
coordinate_dependent: tuple[str, ...] = ()#
property position_dependent: bool#

Whether the coefficient depends on spatial coordinates.

Returns True when coordinate_dependent is non-empty (explicit declaration), or when coefficient_symbolic contains a spatial coordinate call pattern such as x[] or y[] (auto-detection for JSON exports that predate the coordinate_dependent field). Time-only dependence (t[]) returns False.

classmethod from_dict(data)[source]#

Create an OperatorTerm from a dictionary.

Raises:

ValueError – If required keys are missing or operator is unknown.

Parameters:

data (Mapping[str, Any])

Return type:

OperatorTerm

class tidal.symbolic.json_loader.BoundaryCondition(type, value=None, derivative=None, gamma=None)[source]#

Bases: object

Boundary condition for one spatial axis.

Parameters:
type#

One of “periodic”, “dirichlet”, “neumann”, or “robin”.

Type:

str

value#

Fixed value for Dirichlet BCs, or Robin beta.

Type:

float | None

derivative#

Fixed normal derivative for Neumann BCs.

Type:

float | None

gamma#

Robin coefficient gamma in d_n f + gamma*f = beta.

Type:

float | None

type: str#
value: float | None = None#
derivative: float | None = None#
gamma: float | None = None#
classmethod from_dict(data)[source]#

Create a BoundaryCondition from a dictionary.

Raises:

ValueError – If the BC type is not recognized.

Parameters:

data (Mapping[str, Any])

Return type:

BoundaryCondition

to_side_bc()[source]#

Convert to a SideBCSpec for the operator layer.

Raises:

ValueError – If the BC type is “periodic” (not representable as a side BC).

Return type:

SideBCSpec

class tidal.symbolic.json_loader.ConstraintSolverConfig(enabled=False, method='auto', boundary_conditions=<factory>, max_iterations=20, tolerance=1e-08)[source]#

Bases: object

Configuration for elliptic constraint solving.

When enabled is True, the constraint equation is solved at each timestep rather than remaining frozen at its initial value.

Solver Methods#

  • “auto” (default): Automatically selects the best solver. Uses FFT for fully periodic grids (O(N log N)), sparse matrix for non-periodic grids (O(N) via LU). Handles all constraint types: Poisson, Helmholtz, algebraic, anisotropic, etc.

  • “fft”: Force FFT solver. Requires fully periodic grid.

  • “matrix”: Force sparse matrix solver. Works with any BCs.

  • “poisson”: Original py-pde Poisson solver. Requires exactly one laplacian(self_field) term with no other self-referencing operators. Will warn if non-laplacian self-terms are present. Backward compatible.

Coupled Constraint Parameters#

When multiple constraints reference each other’s fields, the solver iterates using Gauss-Seidel until convergence or max_iterations. For fully periodic grids, coupled constraints are solved exactly via Fourier-space block solve (no iteration needed).

enabled#

Whether to solve the constraint elliptically. Default False preserves existing frozen-constraint behavior.

Type:

bool

method#

Solver method: “auto”, “fft”, “matrix”, or “poisson”.

Type:

str

boundary_conditions#

Per-axis boundary conditions (e.g., {"x": ..., "y": ...}).

Type:

dict[str, BoundaryCondition]

max_iterations#

Maximum Gauss-Seidel iterations for coupled constraints. Must be >= 1.

Type:

int

tolerance#

Convergence threshold for coupled constraint iteration. Iteration stops when max|field_new - field_old| < tolerance (scaled by field magnitude for robustness). Must be > 0.

Type:

float

enabled: bool = False#
method: str = 'auto'#
boundary_conditions: dict[str, BoundaryCondition]#
max_iterations: int = 20#
tolerance: float = 1e-08#
classmethod from_dict(data)[source]#

Create from a dictionary or return default (disabled).

Parameters:

data (Mapping[str, Any] | None) – Parsed constraint_solver block from JSON, or None.

Returns:

Configuration instance.

Return type:

ConstraintSolverConfig

Raises:

ValueError – If method is not one of the recognized solver methods.

Parameters:
class tidal.symbolic.json_loader.HamiltonianFactor(field, operator)[source]#

Bases: object

One factor in a quadratic Hamiltonian term.

Represents a field (or its time/spatial derivative) that appears as a multiplicative factor in a term of the component-form Hamiltonian density.

Parameters:
field#

Component field name (e.g., “A_1”, “phi_0”).

Type:

str

operator#

Differential operator: “identity” (bare field), “time_derivative” (∂_t field, maps to canonical momentum at evaluation), or a spatial operator (“gradient_x”, “laplacian”, etc.).

Type:

str

field: str#
operator: str#
classmethod from_dict(data)[source]#

Parse from JSON dict.

Parameters:

data (Mapping[str, Any])

Return type:

HamiltonianFactor

class tidal.symbolic.json_loader.HamiltonianTerm(coefficient, factor_a, factor_b, coefficient_symbolic=None, coordinate_dependent=(), term_class='unknown')[source]#

Bases: object

A single quadratic term in the Hamiltonian density.

H = Σ coefficient * factor_a * factor_b

Parameters:
coefficient#

Numeric coefficient.

Type:

float

factor_a#

First field factor.

Type:

HamiltonianFactor

factor_b#

Second field factor (may equal factor_a for squared terms).

Type:

HamiltonianFactor

coefficient_symbolic#

Symbolic coefficient expression (for parameter override).

Type:

str | None

coordinate_dependent#

Spatial axes the coefficient depends on (e.g. ("x", "y")). When non-empty, the coefficient must be evaluated on the grid. Older JSON exports omit this field; auto-detection via position_dependent covers those cases.

Type:

tuple[str, …]

term_class#

Classification: "self" (both factors reference the same base field) or "interaction" (cross-field coupling). Defaults to "unknown" for older JSONs; use is_self_energy property which auto-classifies by comparing factor field names.

Type:

str

coefficient: float#
factor_a: HamiltonianFactor#
factor_b: HamiltonianFactor#
coefficient_symbolic: str | None = None#
coordinate_dependent: tuple[str, ...] = ()#
term_class: str = 'unknown'#
property position_dependent: bool#

True if the coefficient is a function of spatial coordinates.

Returns True when coordinate_dependent is non-empty (explicit declaration), or when coefficient_symbolic contains a coordinate call pattern such as x[] or y[] (auto-detection for JSON exports that predate the coordinate_dependent field).

property is_self_energy: bool#

True if both factors reference the same base field (self-energy).

Uses term_class when available (from Wolfram export), otherwise classifies by comparing base field names (stripping v_ prefix).

property base_field_a: str#

Base field name for factor_a (strips v_ velocity prefix).

property base_field_b: str#

Base field name for factor_b (strips v_ velocity prefix).

classmethod from_dict(data)[source]#

Parse from JSON dict.

Parameters:

data (Mapping[str, Any])

Return type:

HamiltonianTerm

class tidal.symbolic.json_loader.CanonicalStructure(hamiltonian_terms, volume_element=None)[source]#

Bases: object

Canonical structure: Hamiltonian terms for energy measurement.

The Hamiltonian’s bilinear terms are used by energy measurement to compute H(q, v). The E-L equations are stored in the equations array directly.

Parameters:
hamiltonian_terms#

Quadratic terms in the component-form Hamiltonian density. Used by energy measurement to compute H(q, v).

Type:

tuple[HamiltonianTerm, …]

volume_element#

Symbolic expression for sqrt|det(g_spatial)|, the spatial volume element. None for flat (Minkowski) spacetimes where the volume element is 1. Used by energy measurement to weight the Hamiltonian density before spatial integration.

Type:

str or None

hamiltonian_terms: tuple[HamiltonianTerm, ...]#
volume_element: str | None = None#
classmethod from_dict(data)[source]#

Parse from JSON canonical section.

Parameters:

data (Mapping[str, Any])

Return type:

CanonicalStructure

class tidal.symbolic.json_loader.ComponentEquation(field_name, field_index, time_derivative_order, rhs_terms, constraint_solver=<factory>)[source]#

Bases: object

Equation of motion for a single field component.

For a wave-type equation:

d^2/dt^2 field = sum of OperatorTerms

Parameters:
field_name#

Name of the field component (e.g., “A_0”, “phi”).

Type:

str

field_index#

Index of this component in the field array.

Type:

int

time_derivative_order#

Order of the time derivative on the LHS (2 for wave equations).

Type:

int

rhs_terms#

Terms on the RHS of the equation.

Type:

tuple[OperatorTerm, …]

constraint_solver#

Configuration for elliptic constraint solving. Only meaningful when time_derivative_order == 0.

Type:

ConstraintSolverConfig

field_name: str#
field_index: int#
time_derivative_order: int#
rhs_terms: tuple[OperatorTerm, ...]#
constraint_solver: ConstraintSolverConfig#
classmethod from_dict(data, fields_lookup)[source]#

Create a ComponentEquation from a dictionary.

Raises:

ValueError – If the RHS type is not “linear_combination”, or if constraint_solver is enabled for a non-constraint equation.

Parameters:
Return type:

ComponentEquation

class tidal.symbolic.json_loader.EquationSystem(n_components, dimension, spatial_dimension, component_names, equations, mass_matrix, coupling_matrix, metadata, coordinates=(), mass_matrix_symbolic=(), coupling_matrix_symbolic=(), canonical=None)[source]#

Bases: object

Complete system of field equations derived from a Lagrangian.

Parameters:
n_components#

Number of field components.

Type:

int

dimension#

Spacetime dimension (e.g., 2 for 1+1D).

Type:

int

spatial_dimension#

Number of spatial dimensions (dimension - 1).

Type:

int

component_names#

Names of field components in order.

Type:

tuple[str, …]

equations#

Equations for each component.

Type:

tuple[ComponentEquation, …]

mass_matrix#

Mass matrix M^2_ij for coupled systems.

Type:

tuple[tuple[float, …], …]

coupling_matrix#

Coupling matrix for field interactions.

Type:

tuple[tuple[float, …], …]

metadata#

Additional metadata (source, gauge, etc.)

Type:

dict[str, Any]

coordinates#

Coordinate names from JSON spacetime.coordinates (e.g., (“t”, “x”, “y”)). Defaults to empty tuple; use effective_coordinates for a guaranteed non-empty result that infers names from dimension when not set.

Type:

tuple[str, …]

canonical#

Canonical momentum and Hamiltonian structure from Legendre transform. Present when the JSON spec includes a "canonical" section (generated by tidal derive for non-linearization theories). None for legacy specs or linearization theories.

Type:

CanonicalStructure | None

n_components: int#
dimension: int#
spatial_dimension: int#
component_names: tuple[str, ...]#
equations: tuple[ComponentEquation, ...]#
mass_matrix: tuple[tuple[float, ...], ...]#
coupling_matrix: tuple[tuple[float, ...], ...]#
metadata: dict[str, Any]#
coordinates: tuple[str, ...] = ()#
mass_matrix_symbolic: tuple[tuple[str | None, ...], ...] = ()#
coupling_matrix_symbolic: tuple[tuple[str | None, ...], ...] = ()#
canonical: CanonicalStructure | None = None#
property time_orders: tuple[int, ...]#

Per-component time derivative orders.

property state_size: int#

Total number of state fields.

Second-order components contribute 2 slots (field + momentum). First-order and constraint components contribute 1 slot (field only).

property state_layout: tuple[tuple[str, str], ...]#

State vector layout as (field_name, slot_type) tuples.

slot_type is “field” or “momentum”. Second-order components produce two entries (field, momentum); first-order/constraint produce one (field).

property effective_coordinates: tuple[str, ...]#

Coordinate names, inferred from dimension if not set explicitly.

property spatial_coordinates: tuple[str, ...]#

Spatial coordinate names (all except first, which is time).

property has_constraint_velocity_terms: bool#

True if the Hamiltonian has kinetic coupling between constraint fields.

Detects time_derivative(C_i) x time_derivative(C_j) terms where both C_i and C_j are constraint fields (time_derivative_order == 0). These indicate the naive Hamiltonian H = sum(pi * v) - L is not the correct conserved quantity for this theory (Dirac-Bergmann theory).

See GitHub issue #178 for details.

property equation_map: dict[str, int]#

Map from field name to equation index. Cached on frozen dataclass.

classmethod from_dict(data)[source]#

Create an EquationSystem from a dictionary (parsed JSON).

Raises:
  • ValueError – If the JSON data is invalid or component references are inconsistent.

  • TypeError – If a metadata parameter has an unsupported type.

Parameters:

data (Mapping[str, Any])

Return type:

EquationSystem

tidal.symbolic.json_loader.validate_json_schema(data)[source]#

Validate that the JSON data matches the expected schema.

Raises:

ValueError – If required fields are missing or have invalid types.

Parameters:

data (Mapping[str, Any])

Return type:

None

tidal.symbolic.json_loader.load_equation_system(json_path)[source]#

Load an equation system from a JSON file.

Parameters:

json_path (Path | str) – Path to the JSON file exported from Mathematica.

Returns:

The parsed equation system.

Return type:

EquationSystem

Raises:

FileNotFoundError – If the JSON file does not exist.

tidal.symbolic.latex module#

Convert JSON equation specifications to LaTeX math notation.

Provides functions to render TIDAL equation systems as publication-ready LaTeX, including:

  • Component PDEs with proper operator notation

  • Lagrangian expressions with tensor index notation (\\tensor{} package)

  • Hamiltonian density terms

  • Symbolic coefficients (Mathematica InputForm → LaTeX)

Primary public entry point:

  • system_to_latex(spec, ...) — full equation system

tidal.symbolic.latex.coefficient_to_latex(expr)[source]#

Convert a Mathematica-style symbolic coefficient to LaTeX.

Examples

>>> coefficient_to_latex("-(B0^2*kappa^2)")
'-B_0^{2} \\\\kappa^{2}'
>>> coefficient_to_latex("1/2")
'\\\\frac{1}{2}'
Parameters:

expr (str)

Return type:

str

tidal.symbolic.latex.field_to_latex(name, *, tensor_meta=None, coordinates=())[source]#

Convert a field component name to LaTeX.

Parameters:
  • name (str) – Field name (e.g., “h_5”, “phi_0”, “v_phi_0”).

  • tensor_meta (dict, optional) – Tensor metadata from enriched JSON: {"tensor_head": "h", "tensor_rank": 2, "tensor_indices": [2, 2]}.

  • coordinates (tuple[str, ...], optional) – Coordinate names for resolving index labels (e.g., (“t”, “x”, “y”, “z”)).

Returns:

LaTeX string for the field.

Return type:

str

tidal.symbolic.latex.operator_to_latex(operator, field_latex)[source]#

Render an operator applied to a field in LaTeX.

Parameters:
  • operator (str) – Operator name (e.g., “laplacian_x”, “gradient_y”, “identity”).

  • field_latex (str) – Already-rendered LaTeX for the field.

Returns:

LaTeX expression for the term.

Return type:

str

tidal.symbolic.latex.equation_to_latex(eq, spec)[source]#

Convert a single component equation to LaTeX.

Parameters:
Returns:

LaTeX string (without environment wrapping).

Return type:

str

tidal.symbolic.latex.hamiltonian_to_latex(terms, spec)[source]#

Render the Hamiltonian density as a LaTeX equation.

Returns:

LaTeX for \\mathcal{H} = ....

Return type:

str

Parameters:
tidal.symbolic.latex.lagrangian_to_latex(expr)[source]#

Convert a Lagrangian expression from xAct notation to LaTeX.

This is a best-effort conversion. The xAct abstract index notation is rich and idiosyncratic; this handles the patterns found in the 33 example JSONs in this project.

Parameters:

expr (str) – The lagrangian_expr string from JSON metadata.

Returns:

LaTeX representation using \\tensor{} for index placement.

Return type:

str

tidal.symbolic.latex.system_to_latex(spec, *, output_format='align', include_hamiltonian=True, include_lagrangian=True)[source]#

Convert a full equation system to LaTeX.

Parameters:
  • spec (EquationSystem) – The equation system to render.

  • output_format ({"align", "document", "raw"}) – Output format.

  • include_hamiltonian (bool) – Whether to include the Hamiltonian density.

  • include_lagrangian (bool) – Whether to include the Lagrangian expression.

Returns:

LaTeX output.

Return type:

str

tidal.symbolic.ostrogradsky module#

Ostrogradsky reduction for higher-derivative field equations.

Converts 4th-order-in-time equations to 2nd-order by introducing auxiliary fields. For d⁴h/dt⁴ = RHS, defines w = d²h/dt² and rewrites as two coupled 2nd-order equations:

d²h/dt² = w (kinematic) d²w/dt² = RHS’ (dynamics, with substituted operators)

This is the standard Ostrogradsky reduction (Ostrogradsky 1850, “Mémoires sur les équations différentielles”). Modern review: Woodard (2015, arXiv:1506.02210).

Physics warning: Non-degenerate 4th-order Lagrangians generically have Ostrogradsky ghosts (unbounded Hamiltonian). Ghost-free parameter windows exist for PGT (Barker 2024, arXiv:2406.12826). Full ghost detection is tracked in issue #164.

The reduction is applied in-memory during JSON loading, transparent to all solvers. The JSON on disk is unchanged.

tidal.symbolic.ostrogradsky.MAX_NATIVE_TIME_ORDER = 2#

Maximum time-derivative order that solvers support natively. Equations with higher order are reduced via Ostrogradsky.

class tidal.symbolic.ostrogradsky.AuxiliaryField(original_field, auxiliary_name, original_time_order)[source]#

Bases: object

Metadata for an Ostrogradsky auxiliary field.

Parameters:
  • original_field (str)

  • auxiliary_name (str)

  • original_time_order (int)

original_field#

Name of the higher-order field (e.g., “h_3”).

Type:

str

auxiliary_name#

Name of the auxiliary field (e.g., “w_h_3”).

Type:

str

original_time_order#

Time derivative order of the original equation (4, 6, …).

Type:

int

original_field: str#
auxiliary_name: str#
original_time_order: int#
tidal.symbolic.ostrogradsky.apply_ostrogradsky_reduction(spec)[source]#

Apply Ostrogradsky reduction to higher-derivative equations.

For each equation with time_derivative_order > 2, introduces auxiliary fields to reduce to 2nd-order. The reduction is applied in-memory — the JSON on disk is unchanged.

Parameters:

spec (EquationSystem) – Original equation system, possibly with higher-order equations.

Returns:

Modified system where all equations have time_order <= 2. Auxiliary fields are added to the component list.

Return type:

EquationSystem

Raises:

ValueError – If equations have odd time-derivative order (not physically meaningful for Euler-Lagrange equations from a Lagrangian).

References

  • Ostrogradsky M.V. (1850), Mem. Acad. St. Petersbourg VI 4, 385.

  • Woodard R.P. (2015), arXiv:1506.02210.

  • Barker W.E.V. et al. (2024), arXiv:2406.12826.

tidal.symbolic.reduction module#

Plane-wave dimensional reduction for JSON equation specs.

After the Wolfram pipeline exports a JSON spec with the original higher-dimensional metadata (dimension, coordinates, operator names), this module transforms it into a clean reduced-dimension spec suitable for efficient simulation.

For example, a 3+1D theory reduced along z produces a 1+1D spec with: - dimension: 2, coordinates: ["t", "x"], signature: [-1, 1] - Operators remapped: laplacian_z laplacian_x, gradient_z gradient_x - Killed-axis operators (laplacian_x, gradient_y, etc.) removed - coordinate_dependent arrays and coefficient_symbolic strings updated - Provenance metadata recording the reduction

Curved-coordinate support: - Coefficients depending only on the surviving coordinate are preserved and remapped - Volume element is kept if it depends only on the surviving coordinate - If any surviving coefficient or volume element references a killed coordinate,

ValueError is raised (incompatible reduction)

tidal.symbolic.reduction.reduce_spec(spec_data, reduction_config)[source]#

Apply plane-wave dimensional reduction to a JSON equation spec.

Transforms a higher-dimensional spec into a clean 1+1D spec by:

  1. Remapping operator names (laplacian_z laplacian_x)

  2. Removing terms with killed-axis operators

  3. Updating spacetime metadata (dimension, signature, coordinates)

  4. Remapping coordinate references in coefficient expressions

  5. Handling volume element (keep if surviving-coord-only, error if not)

  6. Adding provenance metadata

Parameters:
  • spec_data (dict) – Parsed JSON spec (as loaded by json.loads).

  • reduction_config (dict) – The [reduction] section from the TOML config.

Returns:

Transformed spec with reduced dimension.

Return type:

dict