fips.Problem.simulate#

Problem.simulate(rng=None, truth=None)[source]#

Return a truth and the observations it would give, \(H x + c + e\).

The problem as a world that makes data: the truth is drawn from the prior, \(x \sim N(x_0, S_0)\), and the observations are its noise-free prediction (predict()) plus an error drawn from the model-data mismatch, \(e \sim N(0, S_z)\). Solving the problem on these observations, post.with_data(obs), and comparing the estimate with the truth tests an inversion on data whose answer is known; a loop of them over one generator is a Monte Carlo of how often the posterior’s intervals hold the truth.

Parameters:
  • rng (int | Generator | None) – A seed, or the generator to draw from (used as given, so one generator passed to every call gives independent trials). None draws from fresh entropy. It goes through numpy.random.default_rng(), as SciPy’s rng argument does. The truth is drawn first and the error second, so a given truth draws the error alone.

  • truth (Vector | None) – A state to use as the truth instead of drawing one, on the prior’s labels in any order: another inventory, 1.5 times the prior. Its observations still carry the error.

Return type:

tuple[Vector, Vector]

Returns:

  • truth (Vector) – The truth, on the prior’s space (the given one, put in the prior’s order).

  • obs (Vector) – The synthetic observations, on the observations’ space and named as the problem’s. They are ready for Posterior.with_data.

Raises:
  • TypeError – If truth is not a Vector.

  • ValueError – If truth does not have exactly the prior’s labels; the message names those on one side only.

  • numpy.linalg.LinAlgError – If a covariance has a clearly negative eigenvalue.

See also

predict

The observations without the error.

Covariance.sample

The draw of the truth and of the error.

Posterior.with_data

The posterior of other observations, one product.

Posterior.recovery

The exact mean and spread of the error over such trials.

Notes

The problem’s own observations are not used, only their labels and name. A different world (another background, another forward operator, an observation error four times the assumed one) is another Problem, made the way the first was with one input changed: world.simulate(rng) gives its data, and post.with_data(obs) solves them with the assumptions of post’s problem.

Examples

>>> import numpy as np
>>> import pandas as pd
>>> import fips
>>> cells = pd.Index(["a", "b"], name="cell")
>>> obs_id = pd.Index(["o1", "o2", "o3"], name="obs_id")
>>> prior = fips.Vector.from_series(pd.Series(0.0, index=cells), block="flux")
>>> obs = fips.Vector.from_series(pd.Series(0.0, index=obs_id), block="co2")
>>> problem = fips.Problem(
...     prior=prior,
...     prior_cov=fips.Independent().with_std(1.0),
...     obs=obs,
...     obs_cov=fips.Independent().with_std(0.1),
...     forward=fips.Operator(
...         np.array([[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]),
...         obs.space,
...         prior.space,
...     ),
... )
>>> truth, synthetic = problem.simulate(rng=0)
>>> truth.values.round(2)
array([ 0.13, -0.13])
>>> synthetic.values.round(2)
array([ 0.19, -0.12, -0.06])
>>> fips.solve(problem).with_data(synthetic).estimate.values.round(2)
array([ 0.15, -0.16])