Skip to content

Problem interface

spec.build() turns a recipe into a LoweredProblem, which ds.pdesolve and the inference verbs take. The problem interface overview builds one problem three ways.


Building

to_equation_set() freezes a recipe into an EquationSet and lower compiles it.

diff_soup.problem.lower

lower(eqset: Any, *, layout: Optional[Layout] = None, precision: Optional[Precision] = None, **options: Any) -> Any

Compile an EquationSet into a problem, or a Study into a lowered study.

Equation sets equal in structure, values, layout and precision share one lowered problem.

Parameters:

Name Type Description Default
eqset Any

An EquationSet or a Study.

required
layout Optional[Layout]

Device placement, a Layout, or None for the default.

None
precision Optional[Precision]

Precision of an equation set, or None for the default, refused for a study.

None
**options Any

Keywords for a study, such as chunk, refused for an equation set.

{}

Returns:

Type Description
Any

A LoweredProblem, or a LoweredStudy for a study.

Raises:

Type Description
LoweringError

If eqset is neither an equation set nor a study.

TypeError

If precision is given for a study, or options for an equation set.

diff_soup.problem.EquationSet

Frozen description of a problem, its components, globals, constraints, observations, controls, couplings and families.

spec.to_equation_set() builds one, and lower compiles it. a | b concatenates two sets, and with_(...) returns a copy with nodes added, removed or replaced.

Attributes:

Name Type Description
components tuple[Component, ...]

Components, each a domain with its fields, equations and boundary conditions, at least one.

globals tuple[GlobalVariable, ...]

Global unknowns.

constraints tuple[Constraint, ...]

Constraint records that close the globals.

observations tuple[Observation, ...]

Declared observations.

controls tuple[Control, ...]

Declared controls.

couplings tuple

Couplings that move values between components.

families tuple

Families of identical members.

scales Scales

Scales of the problem.

residual_scale Mapping

Divisor of each field's residual rows, keyed by (component, field).

quad_fields 'frozenset[str]'

Names of the fields that q reads at quadrature points on a finite-element domain.

diff_soup.problem.LoweredProblem

Solve-ready problem compiled from a recipe or an equation set.

spec.build() and lower return one, and pdesolve and the inference verbs take it. It is a pytree whose floating-point leaves are the declared values.

Attributes:

Name Type Description
layout Layout

Layout the problem was built with.

precision Precision

Precision the problem was built with.

quad_fields 'frozenset[str]'

Names of the fields that q reads at quadrature points on a finite-element domain.

observables property

observables: tuple[Observable, ...]

One record per declared observation, with its name, kind, shape, dtype, owner and reads.

layout class-attribute instance-attribute

layout: Layout = eqx.field(static=True, default_factory=Layout)

precision class-attribute instance-attribute

precision: Precision = eqx.field(static=True, default_factory=Precision)

observe

observe(u: Array, t: Array, lam_internal: Optional[Array] = None, *, names: Optional[Sequence[str]] = None) -> dict[str, Any]

Evaluate the declared observations at an internal state and time, in dimensional units.

Parameters:

Name Type Description Default
u Array

State vector in internal units.

required
t Array

Internal time.

required
lam_internal Optional[Array]

Global unknowns in internal units, or None.

None
names Optional[Sequence[str]]

Observation names to evaluate, or None for all, where an undeclared name raises ConfigurationError.

None

Returns:

Type Description
dict[str, Any]

Mapping from observation name to its value, empty when nothing is declared.

controls

controls(t: Array) -> dict[str, Any]

Evaluate every declared control at an internal time.

Parameters:

Name Type Description Default
t Array

Internal time, multiplied by the time scale before each control function is called.

required

Returns:

Type Description
dict[str, Any]

Mapping from control name to its dimensional value, empty when no control is declared.

default_theta

default_theta()

Return the pytree a solve takes as theta, holding every floating-point array of the problem.

That is the declared parameters and functions, the mass diagonal, the initial multipliers and the family member parameters. theta[D] reads a declared parameter and theta.replace(D, value) sets it.

assemble_state

assemble_state(values: dict[str, Any], *, default: float = 0.0) -> jax.Array

Assemble the flat initial state from per-field values.

Fields missing from values take default, not their declared init, and a callable value raises TypeError. ProblemSpec.assemble_state evaluates declared initial conditions and coordinate callables.

Parameters:

Name Type Description Default
values dict[str, Any]

Mapping from field name to a scalar or an array of the field's shape, with family fields keyed "<family>.<field>".

required
default float

Value of every field left out.

0.0

Returns:

Type Description
Array

The state vector, component fields in layout order, then family members.

initial_u

initial_u(y0: Array) -> jax.Array

Convert a dimensional state to internal units, (y0 - offset) / scale per field.

Parameters:

Name Type Description Default
y0 Array

Full state vector in dimensional units.

required

Returns:

Type Description
Array

The state vector in internal units.

state_size

state_size() -> int

Return the length of the state vector, which excludes the global unknowns.

global_layout

global_layout() -> tuple[dict, ...]

Describe where each global unknown sits in the global block.

Returns:

Type Description
tuple[dict, ...]

One dict per global with its name, offset, size, and mass, "differential" or "algebraic".

solver_problem

solver_problem()

Return the MassProblem or ImplicitProblem the solvers integrate.


Problem types

diff_soup.problem.MassProblem

Mass-matrix problem M(t, y) @ ydot = f(t, y), one of the two problem types the solvers integrate.

The sizes of mass, rhs_sparsity and structure must agree. An algebraic variable's column of the mass must be empty.

Attributes:

Name Type Description
rhs Callable

Function (t, y, theta, control) returning f.

mass MassOperator
structure DAEStructure

DAEStructure marking the differential variables.

rhs_sparsity Union[SparseTopology, str]

SparseTopology holding every possible nonzero of df/dy, or "dense".

linearization Optional[ResidualLinearization]

Optional ResidualLinearization supplying Jacobian values.

precision Precision

Precision of the solve, the default when None.

diff_soup.problem.ImplicitProblem

Fully implicit problem F(t, y, ydot) = 0, one of the two problem types the solvers integrate.

Use it when the residual is nonlinear in ydot. An algebraic variable's column of the declared dF/dydot support must be empty.

Parameters:

Name Type Description Default
residual Callable

Function (t, y, ydot, theta, control) returning F.

required
structure DAEStructure

DAEStructure marking the differential variables.

required
linearization ResidualLinearization

ResidualLinearization declaring the Jacobian supports.

required
precision Optional[Precision]

Precision of the solve, or None for the default.

None

diff_soup.problem.DAEStructure

Which unknowns of a problem are differential and which are algebraic, plus named index ranges.

Properties differential_indices, algebraic_indices and n_differential read the mask, and partition(name) returns a range by name.

Parameters:

Name Type Description Default
differential_vars

Nonempty 1-D boolean mask, true for each differential unknown.

required
partitions tuple

Triples (name, lo, hi), each the index range [lo, hi) within the unknowns.

()

diff_soup.problem.ResidualSparsity

Supports of the two residual Jacobians, dF/dy and dF/dydot.

Parameters:

Name Type Description Default
dy

SparseTopology holding every possible nonzero of dF/dy, or "dense".

required
dydot

SparseTopology holding every possible nonzero of dF/dydot, or "dense".

required

diff_soup.problem.ResidualLinearization

Jacobian supports of a residual, with optional callbacks for their values.

A callback left None is replaced by automatic differentiation over the declared support.

Attributes:

Name Type Description
sparsity ResidualSparsity

ResidualSparsity of dF/dy and dF/dydot.

dy_values Optional[Callable]

Function (t, y, ydot, theta, control) returning dF/dy on its support, or None.

dydot_values Optional[Callable]

Function (t, y, ydot, theta, control) returning dF/dydot on its support, or None.

combined_values Optional[Callable]

Function (t, y, ydot, theta, control, cj) returning dF/dy + cj * dF/dydot on the union support, or None.

max_fy_reuse_steps Optional[int]

Most steps a solver may reuse one dF/dy, nonnegative, or None for the solver's default.

diff_soup.problem.Dependencies

Which solve-time inputs a set of coefficients varies with.

Parameters:

Name Type Description Default
time bool

Whether the coefficients depend on t.

required
state bool

Whether they depend on the state y.

required
parameters bool

Whether they depend on theta.

required
control bool

Whether they depend on the control values.

required

constant property

constant: bool

Whether the coefficients depend on none of the four inputs.


Structure diagnostics

diff_soup.problem.topology_report

topology_report(problem, *, top_k: int = 5) -> TopologyReport

Summarize the Jacobian sparsity pattern a problem presents to the solvers.

A problem without a sparse pattern reports a dense one.

Parameters:

Name Type Description Default
problem required
top_k int

Number of widest rows to report.

5

Returns:

Type Description
TopologyReport

diff_soup.problem.TopologyReport

Summary of a problem's Jacobian sparsity pattern, returned by topology_report.

Attributes:

Name Type Description
n int

Number of unknowns.

nnz int

Number of structural nonzeros.

max_row_degree int

Largest number of nonzeros in one row.

mean_row_degree float

Mean number of nonzeros per row.

color_count Optional[int]

Colors needed to compute the Jacobian by automatic differentiation, or None for a dense pattern.

widest_rows tuple

RowOwner records of the rows with the most nonzeros, widest first.

per_variable_nnz dict

Nonzeros per field or global name, empty when the rows carry no names.

diff_soup.problem.RowOwner

One row of a Jacobian pattern, with its nonzero count and owning variable.

Attributes:

Name Type Description
row int

Row index.

degree int

Number of nonzeros in the row.

variable Optional[str]

Name of the field or global the row belongs to, or None.


Families and couplings

These matter only when you build an EquationSet by hand or write a custom transfer.

diff_soup.problem.Family

Record of a population of count identical members on one member domain.

spec.family(...) builds one. Names within a family must be unique, and every graph must have the family's member count.

Parameters:

Name Type Description Default
name str

Family name, non-empty.

required
count int

Number of members.

required
domain

Member domain shared by every member.

required
member_variables tuple

Member field variables.

()
member_equations tuple

Member equations.

()
member_bcs tuple

Member flux boundary conditions.

()
member_couplings tuple

Couplings owned by the family, left empty by the Recipe API.

()
scale

State scale of each member field, keyed by field name.

None
residual_scale

Residual divisor of each member field, keyed by field name.

None
operator_factory object

Operator-bank class or top-level named function of the domain, or None for the built-in bank.

None
member_params Mapping[str, 'jax.Array'] | None

Per-member parameter arrays, keyed by name.

None
graphs tuple

FamilyGraph records over the members.

()
graph_exchanges tuple

FamilyGraphExchange terms along those graphs.

()
stencil_radius int | None

Jacobian reach of a member kernel in cells per axis, or None for one cell.

None

diff_soup.problem.FamilyGraph

Edge list connecting members of one family, with a weight per edge.

family.graph(...) builds one. Undirected edges are stored with the lower member index first, and self-loops and duplicate edges raise ValueError.

Parameters:

Name Type Description Default
name str

Graph name, unique within the family.

required
member_count int

Number of members, the family's count.

required
edges Any

Integer array of shape (2, E), source member indices in row 0 and targets in row 1.

required
directed bool

Whether each edge has a direction, given explicitly.

required
weights Any

A float scalar, a float array of length E, or a function (source, target, edge) returning each edge's weight.

required

diff_soup.problem.FamilyGraphExchange

Pairwise exchange term along the edges of a family graph.

family.graph_exchange(...) builds one.

Parameters:

Name Type Description Default
name str

Exchange name, unique within the family.

required
graph str

Name of the FamilyGraph the exchange runs along.

required
writes str

Member field whose rows receive the exchange.

required
reads tuple[str, ...]

Member fields fn reads at both endpoints.

required
weight_reads tuple[str, ...]

Member fields a weight function reads.

required
fn Any

Callable (source, target, edge) returning the flow into the target.

required

diff_soup.problem.TransferOp

Base class of the operators that move a field's values into another component.

Pass one as spec.couple(..., via=op). A subclass implements apply and sparsity, and overrides alias_size(n_source=..., n_target=...) when the coupled value is not the size of the target component.

apply

apply(source: Array, target_size: int) -> Array

Map a source vector to a target vector.

Parameters:

Name Type Description Default
source Array

Values of the coupled field.

required
target_size int

Length of the returned vector.

required

Returns:

Type Description
Array

The transferred values, of length target_size.

sparsity

sparsity(source_size: int, target_size: int) -> tuple[np.ndarray, np.ndarray]

Return the nonzero pattern of the Jacobian d(target)/d(source).

A dense transfer lists every pair of target row and source column.

Parameters:

Name Type Description Default
source_size int

Length of the source vector.

required
target_size int

Length of the target vector.

required

Returns:

Type Description
tuple[ndarray, ndarray]

Integer arrays (target_rows, source_cols) of the nonzeros.

diff_soup.problem.NodalExtractOp

Transfer that reads entry i of the coupled value from source row source_indices[i].

Parameters:

Name Type Description Default
source_indices

Source rows, one per entry of the coupled value.

required

diff_soup.problem.NodalInjectOp

Transfer that writes source entry k into target row target_indices[k] and zero elsewhere.

Parameters:

Name Type Description Default
target_indices

Target rows, one per source entry.

required

diff_soup.problem.VarRef dataclass

Address of a variable by its owner's name and its own.

Attributes:

Name Type Description
component_name str

Name of the component or family that owns the variable.

variable_name str

Name of the variable.

family bool

Whether the owner is a family.

diff_soup.problem.Scales dataclass

Scales of an equation set, relating internal values to dimensional ones.

A dimensional value is offset + scale * internal. spec.build() fills one from the inline and central scales of a recipe.

Attributes:

Name Type Description
state_ref Mapping[tuple[str, str], float]

State scale per (component, field) key, with component "" for a global, finite and nonzero.

state_offset Mapping[tuple[str, str], float]

State offset per (component, field) key, finite.

time_ref float

Time scale, finite and nonzero.

integrand_ref Mapping[str, float]

Target divisor per constraint name, finite and nonzero.