Skip to content

Commit

Permalink
Introduce MarkovSequence (#646)
Browse files Browse the repository at this point in the history
* Renamed _markov_process module to _markov

* Extract reusable information from Markov processes

* Distinguish MarkovSequence and MarkovProcess through transition types.

* Import Markov sequence into toplevel namespace

* Markov* inputs are keyword-only

* Raise TypeError if MarkovProcess and MarkovSequence are mixed up.

* moved sample_at_input to interface

* delete redundant tests for auxiliary functions

* Improved error messages.

* Unified the functionality of MarkovProcess and MarkovSequence for now

* Updated output of problem zoo

* Fixed tests

* Update EKF/UKF test to MarkovSequence interface

* Correct else-return

* Corrected string concatenation

* Rudimentary docstring for MarkovSequence

* Updated benchmarks to MarkovSequence where necessary

* Use MarkovSequence in docs

* Added an accidentally-deleted line back in.

* Make codecov ignore an exception branch

Co-authored-by: Marvin Pförtner <marvin.pfoertner@icloud.com>

Co-authored-by: Marvin Pförtner <marvin.pfoertner@icloud.com>
  • Loading branch information
pnkraemer and marvinpfoertner committed Apr 23, 2022
1 parent 7e0e94c commit 83d4669
Show file tree
Hide file tree
Showing 15 changed files with 100 additions and 107 deletions.
6 changes: 3 additions & 3 deletions benchmarks/filtsmooth.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def setup(self, linearization_implementation):
regression_problem.locations
)

prior_process = randprocs.markov.MarkovProcess(
prior_process = randprocs.markov.MarkovSequence(
transition=linearized_dynmod,
initrv=prior_process.initrv,
initarg=regression_problem.locations[0],
Expand Down Expand Up @@ -93,7 +93,7 @@ def setup(self, linearization_implementation):
regression_problem.locations
)

prior_process = randprocs.markov.MarkovProcess(
prior_process = randprocs.markov.MarkovSequence(
transition=linearized_dynmod,
initrv=prior_process.initrv,
initarg=regression_problem.locations[0],
Expand Down Expand Up @@ -160,7 +160,7 @@ def setup(self, linearization_implementation, num_samples):
regression_problem.locations
)

prior_process = randprocs.markov.MarkovProcess(
prior_process = randprocs.markov.MarkovSequence(
transition=linearized_dynmod,
initrv=prior_process.initrv,
initarg=regression_problem.locations[0],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@
"metadata": {},
"outputs": [],
"source": [
"prior_process = randprocs.markov.MarkovProcess(\n",
"prior_process = randprocs.markov.MarkovSequence(\n",
" transition=dynamics_model, initrv=initial_state_rv, initarg=0.0\n",
")"
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@
"metadata": {},
"outputs": [],
"source": [
"prior_process = randprocs.markov.MarkovProcess(\n",
"prior_process = randprocs.markov.MarkovSequence(\n",
" transition=linearised_dynamics_model, initrv=initial_state_rv, initarg=0.0\n",
")"
]
Expand Down
2 changes: 1 addition & 1 deletion docs/source/tutorials/filtsmooth/particle_filtering.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@
"outputs": [],
"source": [
"num_particles = 20\n",
"prior_process = randprocs.markov.MarkovProcess(\n",
"prior_process = randprocs.markov.MarkovSequence(\n",
" transition=dynamics_model, initrv=initial_state_rv, initarg=0.0\n",
")\n",
"importance_distribution = filtsmooth.particle.LinearizationImportanceDistribution.from_ekf(\n",
Expand Down
19 changes: 10 additions & 9 deletions src/probnum/filtsmooth/_kalman_filter_smoother.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,23 +203,24 @@ def smooth_rts(

def _setup_prior_process(F, L, m0, C0, t0, prior_model):
zero_shift_prior = np.zeros(F.shape[0])
initrv = randvars.Normal(m0, C0)
initarg = t0
if prior_model == "discrete":
prior = randprocs.markov.discrete.LTIGaussian(
transition_matrix=F,
noise=randvars.Normal(mean=zero_shift_prior, cov=L),
)
elif prior_model == "continuous":
return randprocs.markov.MarkovSequence(
transition=prior, initrv=initrv, initarg=initarg
)
if prior_model == "continuous":
prior = randprocs.markov.continuous.LTISDE(
drift_matrix=F, force_vector=zero_shift_prior, dispersion_matrix=L
)
else:
raise ValueError
initrv = randvars.Normal(m0, C0)
initarg = t0
prior_process = randprocs.markov.MarkovProcess(
transition=prior, initrv=initrv, initarg=initarg
)
return prior_process
return randprocs.markov.MarkovProcess(
transition=prior, initrv=initrv, initarg=initarg
)
raise ValueError


def _setup_regression_problem(H, R, observations, locations):
Expand Down
4 changes: 2 additions & 2 deletions src/probnum/problems/zoo/filtsmooth/_filtsmooth_problems.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def car_tracking(
# Set up regression problem
time_grid = np.arange(*timespan, step=step)

prior_process = randprocs.markov.MarkovProcess(
prior_process = randprocs.markov.MarkovSequence(
transition=discrete_dynamics_model, initrv=initrv, initarg=time_grid[0]
)

Expand Down Expand Up @@ -377,7 +377,7 @@ def dh(t, x):

if initarg is None:
initarg = time_grid[0]
prior_process = randprocs.markov.MarkovProcess(
prior_process = randprocs.markov.MarkovSequence(
transition=dynamics_model, initrv=initrv, initarg=initarg
)

Expand Down
4 changes: 3 additions & 1 deletion src/probnum/randprocs/markov/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
"""

from . import continuous, discrete, integrator, utils
from ._markov_process import MarkovProcess
from ._markov import MarkovProcess, MarkovSequence
from ._transition import Transition

# Public classes and functions. Order is reflected in documentation.
__all__ = [
"MarkovProcess",
"MarkovSequence",
"Transition",
]

# Set correct module paths. Corrects links and module paths in documentation.
MarkovProcess.__module__ = "probnum.randprocs.markov"
MarkovSequence.__module__ = "probnum.randprocs.markov"
Transition.__module__ = "probnum.randprocs.markov"
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,24 @@

from probnum import _function, randvars, utils
from probnum.randprocs import _random_process, kernels
from probnum.randprocs.markov import _transition
from probnum.randprocs.markov import _transition, continuous, discrete
from probnum.typing import ShapeLike

InputType = Union[np.floating, np.ndarray]
OutputType = Union[np.floating, np.ndarray]


class MarkovProcess(_random_process.RandomProcess):
r"""Random processes with the Markov property.
A Markov process is a random process with the additional property that
conditioned on the present state of the system its future and past states are
independent. This is known as the Markov property or as the process being
memoryless. A Markov process can be fully defined via an initial state and a
state transition.
Parameters
----------
initarg
Initial starting input of the process.
initrv
Random variable describing the initial state.
transition
State transition of the system.
See Also
--------
RandomProcess : Random processes.
GaussianProcess : Gaussian processes.
"""

class _MarkovBase(_random_process.RandomProcess):
def __init__(
self,
initarg: np.ndarray,
*,
initrv: randvars.RandomVariable,
transition: _transition.Transition,
input_shape: ShapeLike = (),
):
self.initarg = initarg
self.initrv = initrv
self.transition = transition

input_shape = np.asarray(initarg).shape
output_shape = initrv.shape

super().__init__(
Expand All @@ -60,7 +36,7 @@ def __init__(
input_shape=input_shape,
output_shape=output_shape,
),
cov=MarkovProcess.Kernel(
cov=_MarkovBase.Kernel(
self.__call__,
input_shape=input_shape,
output_shape=2 * output_shape,
Expand Down Expand Up @@ -124,3 +100,68 @@ def _evaluate(self, x0: np.ndarray, x1: Optional[np.ndarray]) -> np.ndarray:
return self._markov_proc_call(args=x0).cov

raise NotImplementedError


class MarkovProcess(_MarkovBase):
r"""Random processes with the Markov property.
A Markov process is a random process with the additional property that
conditioned on the present state of the system its future and past states are
independent. This is known as the Markov property or as the process being
memoryless. A Markov process can be fully defined via an initial state and a
state transition.
Parameters
----------
initarg
Initial starting input of the process.
initrv
Random variable describing the initial state.
transition
State transition of the system.
See Also
--------
RandomProcess : Random processes.
GaussianProcess : Gaussian processes.
"""

def __init__(
self,
*,
initarg: np.ndarray,
initrv: randvars.RandomVariable,
transition: continuous.SDE,
):
if not isinstance(transition, continuous.SDE): # pragma: no cover
msg = "The transition is not continuous. Did you mean 'MarkovSequence'?"
raise TypeError(msg)

super().__init__(
initrv=initrv,
transition=transition,
input_shape=np.asarray(initarg).shape,
)
self.initarg = initarg


class MarkovSequence(_MarkovBase):
"""Discrete-time Markov processes."""

def __init__(
self,
*,
initarg: np.ndarray,
initrv: randvars.RandomVariable,
transition: continuous.SDE,
):
if not isinstance(transition, discrete.NonlinearGaussian): # pragma: no cover
msg = "The transition is not discrete. Did you mean 'MarkovProcess'?"
raise TypeError(msg)

super().__init__(
initrv=initrv,
transition=transition,
input_shape=np.asarray(initarg).shape,
)
self.initarg = initarg
4 changes: 2 additions & 2 deletions src/probnum/randprocs/markov/integrator/_ioup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
import numpy as np

from probnum import randvars
from probnum.randprocs.markov import _markov_process, continuous
from probnum.randprocs.markov import _markov, continuous
from probnum.randprocs.markov.integrator import _integrator, _preconditioner


class IntegratedOrnsteinUhlenbeckProcess(_markov_process.MarkovProcess):
class IntegratedOrnsteinUhlenbeckProcess(_markov.MarkovProcess):
r"""Integrated Ornstein-Uhlenbeck process.
Convenience access to :math:`\nu` times integrated (:math:`d` dimensional)
Expand Down
4 changes: 2 additions & 2 deletions src/probnum/randprocs/markov/integrator/_iwp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
import scipy.special

from probnum import config, linops, randvars
from probnum.randprocs.markov import _markov_process, continuous, discrete
from probnum.randprocs.markov import _markov, continuous, discrete
from probnum.randprocs.markov.integrator import _integrator, _preconditioner


class IntegratedWienerProcess(_markov_process.MarkovProcess):
class IntegratedWienerProcess(_markov.MarkovProcess):
r"""Integrated Wiener process.
Convenience access to :math:`\nu` times integrated (:math:`d` dimensional)
Expand Down
4 changes: 2 additions & 2 deletions src/probnum/randprocs/markov/integrator/_matern.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import scipy.special

from probnum import randvars
from probnum.randprocs.markov import _markov_process, continuous
from probnum.randprocs.markov import _markov, continuous
from probnum.randprocs.markov.integrator import _integrator, _preconditioner


class MaternProcess(_markov_process.MarkovProcess):
class MaternProcess(_markov.MarkovProcess):
r"""Matern process.
Convenience access to (:math:`d` dimensional) Matern(:math:`\nu`) processes.
Expand Down
4 changes: 2 additions & 2 deletions src/probnum/randprocs/markov/utils/_generate_measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import numpy as np

from probnum.randprocs.markov import _markov_process, _transition
from probnum.randprocs.markov import _markov, _transition


def generate_artificial_measurements(
rng: np.random.Generator,
prior_process: _markov_process.MarkovProcess,
prior_process: _markov.MarkovProcess,
measmod: _transition.Transition,
times: np.ndarray,
):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def test_filtsmooth_pendulum(self, rng):
)

initrv = prior_process.initrv
prior_process = randprocs.markov.MarkovProcess(
prior_process = randprocs.markov.MarkovSequence(
transition=ekf_dyna, initrv=initrv, initarg=regression_problem.locations[0]
)
method = filtsmooth.gaussian.Kalman(prior_process)
Expand Down
Empty file.

This file was deleted.

0 comments on commit 83d4669

Please sign in to comment.