Skip to content

SPIN Problem Setup

problem

Setup of the PDE variational problem for stochastic process inference.

This module implements the functionality of SPIN that is specific to stochastic processes. It sets up the Kolmogorov equations for PDE-based inference with Hippylib, as defined in the weakforms module. The builder pattern is employed to return a Hippylib-conformant object with additional data and methods for convenience. Different inference modes are available to infer drift, diffusion, or both.

Info

We do not consider the actual diffusion matrix, but the logarithm of its square. Inferring the square avoids disambiguity issues, whereas inferring the log enforces positivity of the diffusion matrix. SPIN can only infer diagonal diffusion matrices, meaning that enforcing positivity of the diagonal entries ensures that the diffusion matrix is s.p.d.

Classes:

Name Description
SPINProblemSettings

Configuration and data for SPIN problem setup.

PDEType

Registration of PDEs, including weak form and metadata.

SPINProblem

Wrapper for Hippylib PDE problem with additional data and functionality.

SPINProblemBuilder

Builder for the SPINProblem object.

spin.core.problem.SPINProblemSettings dataclass

Configuration and data for SPIN problem setup.

Attributes:

Name Type Description
mesh dl.Mesh

Dolfin mesh as discretization of the PDE domain.

pde_type str

Identifier of the PDE to use, needs to be registered in the _registered_pde_types dictionary of the SPINProblemBuilder class. Available options are "mean_exit_time", "mean_exit_time_moments", and "fokker_planck".

inference_type str

Type of inference, meaning which parameter(s) to infer. Available options are "drift_only", "diffusion_only", and "drift_and_diffusion".

element_family_variables str

FE Family for the forward and adjoint variable, according to options in UFL.

element_family_parameters str

FE Family for the parameter variable(s), according to options in UFL.

element_degree_variables int

FE degree for the forward and adjoint variable, according to options in UFL.

element_degree_parameters int

FE degree for the parameter variable(s), according to options in UFL.

drift str | Iterable[str] | None

String in dolfin syntax defining drift vector. Needs to be provided as list of length corresponding to problem dimension. Only required for "diffusion_only" inference mode.

log_squared_diffusion str | Iterable[str] | None

String in dolfin syntax defining diagonal of the log squared diffusion function. Needs to be provided as list of length corresponding to problem dimension. Only required for "drift_only" inference mode.

start_time Real | None

Start time for PDE solver, only required for time-dependent PDE.

end_time Real | None

End time for PDE solver, only required for time-dependent PDE.

num_steps int | None

Number of time steps for PDE solver, only required for time-dependent PDE.

initial_condition str | Iterable[str] | None

Initial condition for PDE solver, only required for time-dependent PDE..

spin.core.problem.PDEType dataclass

Registration of PDEs, including weak form and metadata.

Info

This class is used by the builder internally, and does not require interaction by the user. Only for development purposes, when a new PDE is implemented.

Attributes:

Name Type Description
weak_form Callable

Weak form in UFL syntax, defined in the weakforms module.

num_components int

Number of components of the solution/adjoint variable.

linear bool

If the PDE is linear.

stationary bool

If the PDE is stationary.

spin.core.problem.SPINProblem dataclass

Wrapper for Hippylib PDE problem with additional data and functionaliry.

A SPINProblem object is returned as the output of the builder. It wraps a Hippylib PDE problem to conduct inference with. It further provides data like function spaces, coordinates, etc. for more transparency. Moreoever, it implements methods for forward, adjoint and gradient solves with a Numpy interface.

Attributes:

Name Type Description
hippylib_variational_problem hl.PDEProblem

Hippylib PDE problem object.

num_variable_components int

Number of components in forard/adjoint variable.

domain_dim int

Dimension of the computational domain.

function_space_variables dl.FunctionSpace

Function space for forward/adjoint variable.

function_space_parameters dl.FunctionSpace

Function space for parameter.

function_space_drift dl.FunctionSpace

Function space for the drift vector.

function_space_diffusion dl.FunctionSpace

Function space for the diffusion matrix.

coordinates_variables npt.NDArray[np.floating]

Coordinates for the forward/adjoint variable degrees of freedom.

coordinates_parameters npt.NDArray[np.floating]

Coordinates for the parameter degrees of freedom.

drift_array npt.NDArray[np.floating] | None

Drift function converted to numpy array, if provided by the user.

log_squared_diffusion_array npt.NDArray[np.floating] | None

Log squared diffusion function converted to numpy array, if provided by the user.

initial_condition_array npt.NDArray[np.floating] | None

Initial condition converted to numpy array, if provided by the user.

Methods:

Name Description
solve_forward

Solve the PDE, given a parameter.

solve_adjoint

Solve the adjoint equation, given parameter and forward solution.

evaluate_gradient

Evaluate parametric gradient, given parameter, forward, and adjoint solution.

solve_forward

solve_forward(parameter_array: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]

Solve the defined PDE, given a parameter function.

The parameter can be drift, difusion, or both, depending on the inference mode. It has to be provided according to the convention defined in the fenics converter module. This means that the array has shape \(K\times N\) for \(k\) components and \(N\) degrees of freedom on the computational domain.

Parameters:

Name Type Description Default
parameter_array npt.NDArray[np.floating]

Parameter function to solve PDE for.

required

Raises:

Type Description
ValueError

Checks the parameter array has correct size.

Returns:

Type Description
npt.NDArray[np.floating]

npt.NDArray[np.floating]: Forward solution of the PDE.

solve_adjoint

solve_adjoint(forward_array: npt.NDArray[np.floating], parameter_array: npt.NDArray[np.floating], right_hand_side_array: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]

Solve the adjoint equation for the defined PDE, given parameter and forward solution.

The parameter can be drift, difusion, or both, depending on the inference mode. It has to be provided according to the convention defined in the fenics converter module. This means that the array has shape \(K\times N\) for \(k\) components and \(N\) degrees of freedom on the computational domain. The forward solution can be obtained by calling the solve_forward method of this class. In addition, the adjoint equation is solved with a given right hand side, which is typically provided as the gradient of some loss functional governed by the PDE model.

Parameters:

Name Type Description Default
forward_array npt.NDArray[np.floating]

Forward solution of the PDE.

required
parameter_array npt.NDArray[np.floating]

Parameter function.

required
right_hand_side_array npt.NDArray[np.floating]

Right-hand-side for the adjoint.

required

Raises:

Type Description
ValueError

Checks that the array sizes of forward, parameter, and RHS are correct.

Returns:

Type Description
npt.NDArray[np.floating]

npt.NDArray[np.floating]: Adjoint solution of the PDE problem.

evaluate_gradient

evaluate_gradient(forward_array: npt.NDArray[np.floating], parameter_array: npt.NDArray[np.floating], adjoint_array: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]

Evaluate the parametric gradient forgiven parameter, forward, and adjoint solution.

The parameter can be drift, difusion, or both, depending on the inference mode. It has to be provided according to the convention defined in the fenics converter module. This means that the array has shape \(K\times N\) for \(k\) components and \(N\) degrees of freedom on the computational domain. The forward solution can be obtained by calling the solve_forward method of this class, the adjoint solution by calling the solve_adjoint method.

Parameters:

Name Type Description Default
forward_array npt.NDArray[np.floating]

Forward solution.

required
parameter_array npt.NDArray[np.floating]

Parameter function.

required
adjoint_array npt.NDArray[np.floating]

Adjoint solution.

required

Raises:

Type Description
ValueError

Checks that the array sizes of forward, parameter, and adjoint are correct.

Returns:

Type Description
npt.NDArray[np.floating]

npt.NDArray[np.floating]: Parametric gradient.

spin.core.problem.SPINProblemBuilder

Spin problem builder.

The builder assembles a SPINProblem object from the configuration provided in a SPINProblemSettings object. It casts problem-specific data structures into a Hippylib-conformant interface.

Methods:

Name Description
build

Main interface of the builder, returning a SPINProblem object.

__init__

__init__(settings: SPINProblemSettings) -> None

Constructor, set all data structures internally for usage in build method.

Parameters:

Name Type Description Default
settings SPINProblemSettings

Configuration and data for the PDE variational problem.

required

Raises:

Type Description
ValueError

Checks that given PDE type is registered with the builder in _registered_pde_types.

build

build() -> SPINProblem

Main interface of the builder, returning a SPINProblem object.

The builder internally cals a sequence of methods that result in a Hippylib PDEProblem object to be used for inference. The methods are implemented in a semi-explicit manner: Function arguments are implicit, as they are set as object attributes in the constructor. The output of the methods is explicit, however. This is a compromise between clarity and verbosity of the OOP design in this class.

Returns:

Name Type Description
SPINProblem SPINProblem

Object wrapping the Hippylib PDEProblem with additional methods and metadata.

_create_function_spaces

_create_function_spaces() -> tuple[dl.FunctionSpace, dl.FunctionSpace, dl.FunctionSpace, dl.FunctionSpace]

Create function spaces for variables, drift, diffusion, and composite parameters.

The precise form of the function spaces depends on the PDE type and inference mode. For scalar PDEs, the solution and adjoint variable space is scalar, otherwise it is vector-valued. The drift and diffusion spaces are always vector-valued, while the composite space is a vector space comprising both the drift and diffusion components.

Returns:

Type Description
tuple[dl.FunctionSpace, dl.FunctionSpace, dl.FunctionSpace, dl.FunctionSpace]

tuple[dl.FunctionSpace, dl.FunctionSpace, dl.FunctionSpace, dl.FunctionSpace]: Tuple of function spaces for variables, drift, diffusion, and composite parameters.

_compile_expressions

_compile_expressions() -> tuple[dl.Function | None, dl.Function | None, dl.Function | None]

Generate dolfin expressions from strings, if they are provided.

Depending on the inference mode, different expressions need to be provided. Their existence is checked upon assembly of the PDE problem.

Returns:

Type Description
tuple[dl.Function | None, dl.Function | None, dl.Function | None]

tuple[dl.Function | None, dl.Function | None, dl.Function | None]: Created dolfin functions for drift, diffusion, and initial condition, if their string representation has been provided.

_assign_parameter_function_space

_assign_parameter_function_space() -> dl.FunctionSpace

Decide which function space is the parameter space, depending on inference type.

Returns:

Type Description
dl.FunctionSpace

dl.FunctionSpace: Space to use for the parameter variable

_get_parameter_arrays

_get_parameter_arrays() -> tuple[npt.NDArray[np.floating] | None, npt.NDArray[np.floating] | None, npt.NDArray[np.floating] | None]

Convert dolfin functions to numpy arrays, if they are provided by the user.

Returns:

Type Description
tuple[npt.NDArray[np.floating] | None, npt.NDArray[np.floating] | None, npt.NDArray[np.floating] | None]

tuple[npt.NDArray | None, npt.NDArray | None, npt.NDArray | None]: Drift, diffusion, and initial condition as numpy arrays, if provided by the user.

_create_boundary_condition

_create_boundary_condition() -> dl.DirichletBC

Create homogeneous Dirichlet Boundary conditions on the given mesh.

Returns:

Type Description
dl.DirichletBC

dl.DirichletBC: Dolfin boundary conditions.

_create_weak_form_wrapper

_create_weak_form_wrapper() -> Callable[[Any, Any, Any], ufl.Form]

Generate generic weak form taking forward, parameter, and adjoint variable.

The implemented PDE forms in the weakforms explicitly take drift and diffusion as coefficient functions. This method provides a wrapper that dispatches to either drift, diffusion, or both as the parameter, depending on the inference mode. The resulting form wrapper has the generic argument signature (forward, parameter, adjoint) that is required for computations in Hippylib.

Raises:

Type Description
ValueError

Checks that drift has been provided for "diffusion_only" inference.

ValueError

Checks that diffusion has been provided for "drift_only" inference.

Returns:

Type Description
Callable[[Any, Any, Any], ufl.Form]

Callable[[Any, Any, Any], ufl.Form]: UFL weak form wrapper with generic signature.

_compute_matrix_exponential

_compute_matrix_exponential(matrix_diagonal: dl.Function | ufl.tensors.ListTensor) -> ufl.tensors.ListTensor

Create matrix exponential for diagonal matrix in UFL syntax.

Parameters:

Name Type Description Default
matrix_diagonal dl.Function | ufl.tensors.ListTensor

Diagonal entries

required

Returns:

Type Description
ufl.tensors.ListTensor

ufl.tensors.ListTensor: Diagonal matrix exponential.

_create_variational_problem

_create_variational_problem() -> hl.PDEProblem

Create the Hippylib-conformant PDEProblem object.

This method utilizes the previously assembled dolfin objects to create an object conforming to the interface of the hippylib PDEProblem class through nominal subtyping. For stationary problems. this is the PDEVariationalProblem class, for time-dependent problems, we utilize the TDPDELinearVariationalProblem class in SPIN.

Warning

Time-dependent PDE inference is not implemented yet.

Raises:

Type Description
ValueError

Checks that time-stepping parameters are provided for time-dependent PDEs.

ValueError

Checks that initial condition is provided for time-dependent PDEs.

NotImplementedError

Indicates that time-dependent problems are not yet implemented.

Returns:

Type Description
hl.PDEProblem

hl.PDEProblem: Hippylib object for inference.