pymob.solvers package#

Submodules#

pymob.solvers.analytic module#

pymob.solvers.analytic.solve_analytic_1d(model, parameters, dimensions, coordinates, data_variables, seed=None)#

Solves an anlytic function for all coordinates in the first data dimension of the model

  • parameters: define the model

  • y0: set the initial values of the ODE states

  • coordinates: are needed to know over which values to integrate

  • seed: In case stochastic processes take place inside the model this is necessary

In order to make things explicit, all information which is needed by the model needs to be specified in the function signature. This also makes the solvers functionally oriented, a feature that helps the usability of models accross inference frameworks. Where functions should not have side effects.

Additionally, passing arguments via the signature makes it easier to write up models in a casual way and only later embed them into more regulated structures such as pymob

pymob.solvers.base module#

class pymob.solvers.base.SolverBase(model: Callable, dimensions: Tuple, dimension_sizes: frozendict[str, int], parameter_dims: frozendict[str, Tuple[str, ...]], n_ode_states: int, coordinates: frozendict[str, Tuple], coordinates_input_vars: frozendict[str, frozendict[str, frozendict[str, Tuple[float | int | str, ...]]]], dims_input_vars: frozendict[str, frozendict[str, Tuple[str, ...]]], coordinates_indices: frozendict[str, tuple], data_variables: Tuple, data_structure_and_dimensionality: frozendict[str, frozendict[str, int]], is_stochastic: bool, post_processing: Callable, solver_kwargs: frozendict = {}, indices: frozendict[str, Tuple] = {}, x_dim: str = 'time', batch_dimension: str = 'batch_id', exclude_kwargs_model: Tuple[str, ...] = ('t', 'x_in', 'y', 'X'), exclude_kwargs_postprocessing: Tuple[str, ...] = ('t', 'time', 'interpolation', 'results'))#

Bases: object

The idea of creating a solver as a class is that it is easier to pass on important arguments of the simulation relevant to the Solver. Therefore a solver can access all attributes of an Evaluator

batch_dimension: str = 'batch_id'#
coordinates: frozendict[str, Tuple]#
coordinates_indices: frozendict[str, tuple]#
coordinates_input_vars: frozendict[str, frozendict[str, frozendict[str, Tuple[float | int | str, ...]]]]#
data_structure_and_dimensionality: frozendict[str, frozendict[str, int]]#
data_variables: Tuple#
dimension_sizes: frozendict[str, int]#
dimensions: Tuple#
dims_input_vars: frozendict[str, frozendict[str, Tuple[str, ...]]]#
exclude_kwargs_model: Tuple[str, ...] = ('t', 'x_in', 'y', 'X')#
exclude_kwargs_postprocessing: Tuple[str, ...] = ('t', 'time', 'interpolation', 'results')#
indices: frozendict[str, Tuple] = {}#
is_stochastic: bool#
len_batch_coordinate: int#
model: Callable#
n_ode_states: int#
parameter_dims: frozendict[str, Tuple[str, ...]]#
post_processing: Callable#
preprocess_parameters(parameters, num_backend: module = <module 'numpy' from '/home/docs/checkouts/readthedocs.org/user_builds/pymob/envs/latest/lib/python3.11/site-packages/numpy/__init__.py'>)#
preprocess_x_in(x_in, num_backend: module = <module 'numpy' from '/home/docs/checkouts/readthedocs.org/user_builds/pymob/envs/latest/lib/python3.11/site-packages/numpy/__init__.py'>)#
preprocess_y_0(y0, num_backend: module = <module 'numpy' from '/home/docs/checkouts/readthedocs.org/user_builds/pymob/envs/latest/lib/python3.11/site-packages/numpy/__init__.py'>)#
shapes_coordinates: Dict[str, int]#
shapes_input_vars_coordinates: frozendict[str, frozendict[str, Tuple[int, ...]]]#
shapes_parameter_coordinates: Dict[str, Tuple[int, ...]]#
solve()#
solver_kwargs: frozendict = {}#
test_batch_dim_consistency(X_in, Y_0, ode_args, pp_args, num_backend: module = <module 'numpy' from '/home/docs/checkouts/readthedocs.org/user_builds/pymob/envs/latest/lib/python3.11/site-packages/numpy/__init__.py'>)#
test_matching_batch_dims()#
test_x_coordinates()#
x: Tuple[float]#
x_dim: str = 'time'#
x_shape_batched: Tuple[int, ...]#
pymob.solvers.base.curve_jumps(x, y, i, r, n)#
pymob.solvers.base.jump_interpolation(x_in: Dataset, x_dim: str = 'time', factor: float = 0.001, interpolation: Literal['fill-forward', 'linear'] = 'fill-forward') Dataset#

Make the interpolation safe by adding a coordinate just before each x-value (except the first vaue). The distance between the new and the next point are calculated as a fraction of the previous distance between neighboring points. The corresponding y-values are first set to NaN and then interpolated based on the interpolation method.

Parameters:
  • x_in (xr.Dataset) – The input dataset which contains a coordinate (x) and a data variable (y)

  • x_dim (str, optional) – The name of the x coordinate, by default “time”

  • factor (float, optional) – The distance between the newly added points and the following existing points on the x-scale, by default 1e-4

  • interpolation (Literal["fill-forward", "linear"], optional) – The interpolation method. In addition to ‘fill-forward’ and ‘linear’, any method give in xr.interpolate_na can be chosen, by default “fill-forward”

Returns:

The interpolated dataset

Return type:

xr.Dataset

pymob.solvers.base.mappar(func, parameters: Dict[str, float | int | List | Tuple], exclude=[], to: Literal['tuple', 'dict', 'names'] = 'tuple') Tuple | Dict#
pymob.solvers.base.radius_interpolation(x_in: Dataset, x_dim: str = 'time', radius: float = 0.1, num_points: int = 10, rectify=True) Dataset#

Smooth the interpolation by first creating a dense x vector and forward filling all ys. Following this the values are smoothed by a gaussian filter.

WARNING! It is very pretty but does not work with diffrax

Parameters:
  • x_in (xr.Dataset) – The input dataset which contains a coordinate (x) and a data variable (y)

  • x_dim (str, optional) – The name of the x coordinate, by default “time”

  • radius (float, optional) – The radius of the quarter-circle to curve the jump transition. By default 0.1

  • num_points (int, optional) – The number of points to interpolate each jump with. Default: 10

  • rectify (bool) – Whether the input should be converted to a stepwise pattern. Default is True. This is typically applied if an unprocessed signal is included. E.g. the signal was observed y_i

Returns:

The interpolated dataset

Return type:

xr.Dataset

pymob.solvers.base.rect_interpolation(x_in: Dataset, x_dim: str = 'time')#

Use diffrax’ rectilinear_interpolation. To add values and interpolate one more step after the end of the timeseries

pymob.solvers.base.smoothed_interpolation(x_in: Dataset, x_dim: str = 'time', factor: float = 0.001, sigma: int = 20) Dataset#

Smooth the interpolation by first creating a dense x vector and forward filling all ys. Following this the values are smoothed by a gaussian filter.

Parameters:
  • x_in (xr.Dataset) – The input dataset which contains a coordinate (x) and a data variable (y)

  • x_dim (str, optional) – The name of the x coordinate, by default “time”

  • factor (float, optional) – The distance between the newly added points and the following existing points on the x-scale, by default 1e-4

Returns:

The interpolated dataset

Return type:

xr.Dataset

pymob.solvers.diffrax module#

class pymob.solvers.diffrax.JaxSolver(model: ~typing.Callable, dimensions: ~typing.Tuple, dimension_sizes: ~frozendict.frozendict[str, int], parameter_dims: ~frozendict.frozendict[str, ~typing.Tuple[str, ...]], n_ode_states: int, coordinates: ~frozendict.frozendict[str, ~typing.Tuple], coordinates_input_vars: ~frozendict.frozendict[str, ~frozendict.frozendict[str, ~frozendict.frozendict[str, ~typing.Tuple[float | int | str, ...]]]], dims_input_vars: ~frozendict.frozendict[str, ~frozendict.frozendict[str, ~typing.Tuple[str, ...]]], coordinates_indices: ~frozendict.frozendict[str, tuple], data_variables: ~typing.Tuple, data_structure_and_dimensionality: ~frozendict.frozendict[str, ~frozendict.frozendict[str, int]], is_stochastic: bool, post_processing: ~typing.Callable, solver_kwargs: ~frozendict.frozendict = {}, indices: ~frozendict.frozendict[str, ~typing.Tuple] = {}, x_dim: str = 'time', batch_dimension: str = 'batch_id', exclude_kwargs_model: ~typing.Tuple[str, ...] = ('t', 'x_in', 'y', 'X'), exclude_kwargs_postprocessing: ~typing.Tuple[str, ...] = ('t', 'time', 'interpolation', 'results'), diffrax_solver: ~diffrax.solver.base._MetaAbstractSolver | str = <class 'diffrax.solver.dopri5.Dopri5'>, rtol: float = 1e-06, atol: float = 1e-07, pcoeff: float = 0.0, icoeff: float = 1.0, dcoeff: float = 0.0, max_steps: int = 100000, throw_exception: bool = True)#

Bases: SolverBase

see https://jax.readthedocs.io/en/latest/faq.html#strategy-3-making-customclass-a-pytree to make thinks robust

Parameters:

throw_exceptions (bool) – Default is True. The JaxSolver will throw an exception if it runs into a problem with the step size and return infinity. This is done, because likelihood based inference algorithms can deal with infinity values and consider the tested parameter combination impossible. If used without caution, this can lead to severely biased parameter estimates.

atol: float = 1e-07#
dcoeff: float = 0.0#
diffrax_solver#

alias of Dopri5

icoeff: float = 1.0#
max_steps: int = 100000#
odesolve(y0, args, x_in)#
odesolve_splitargs(*args, odestates, n_odeargs, n_ppargs, n_xin)#
pcoeff: float = 0.0#
preprocess_parameters(parameters, num_backend: module = <module 'jax.numpy' from '/home/docs/checkouts/readthedocs.org/user_builds/pymob/envs/latest/lib/python3.11/site-packages/jax/numpy/__init__.py'>)#
preprocess_x_in(x_in, num_backend: module = <module 'jax.numpy' from '/home/docs/checkouts/readthedocs.org/user_builds/pymob/envs/latest/lib/python3.11/site-packages/jax/numpy/__init__.py'>)#
preprocess_y_0(y0, num_backend: module = <module 'jax.numpy' from '/home/docs/checkouts/readthedocs.org/user_builds/pymob/envs/latest/lib/python3.11/site-packages/jax/numpy/__init__.py'>)#
rtol: float = 1e-06#
solve(parameters: Dict, y0: Dict = {}, x_in: Dict = {})#
throw_exception: bool = True#
x_in_jumps: Tuple[float, ...]#

pymob.solvers.scipy module#

pymob.solvers.scipy.solve_ivp_1d(model, parameters, coordinates, data_variables)#

Initial value problems always need the same number of recurrent arguments

  • parameters: define the model

  • y0: set the initial values of the ODE states

  • coordinates: are needed to know over which values to integrate

  • seed: In case stochastic processes take place inside the model this is necessary

In order to make things explicit, all information which is needed by the model needs to be specified in the function signature. This also makes the solvers functionally oriented, a feature that helps the usability of models accross inference frameworks. Where functions should not have side effects.

Additionally, passing arguments via the signature makes it easier to write up models in a casual way and only later embed them into more regulated structures such as pymob

Module contents#