Skip to content

Commit 8779648

Browse files
authored
Merge pull request #166 from pnkraemer/new_precond
Preconditioner
2 parents 9c0c0cf + 4c83e5f commit 8779648

File tree

3 files changed

+27
-3
lines changed

3 files changed

+27
-3
lines changed

src/probnum/diffeq/odefiltsmooth/prior.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class ODEPrior(LTISDEModel):
4141
By default, we choose :math:`P` to be the
4242
matrix that maps to filtering iteration to the Nordsieck vector,
4343
44-
.. math:: P = \\text{diag }(1, h, h^2, ..., h^q).
44+
.. math:: P = \\text{diag }(h^{-q}, h^{-q+1}, ..., 1).
4545
4646
Here, :math:`h` is some expected average step size. Note that we
4747
ignored the factorials in this matrix. Our setting makes it easy to
@@ -70,6 +70,10 @@ class ODEPrior(LTISDEModel):
7070
steps and higher order methods which especially makes smoothing
7171
algorithms unstable.
7272
73+
Another advantage of this preconditioning is that the smallest value
74+
that appears inside the algorithm is :math:`h^{q}` (with
75+
preconditioning) instead of :math:`h^{2q+1}` (without preconditioning).
76+
7377
The matrices :math:`F, u, L` are the usual matrices for
7478
IBM(:math:`q`), IOUP(:math:`q`) or Matern(:math:`q+1/2`) processes.
7579
As always, :math:`B(t)` is
@@ -149,7 +153,7 @@ def precond2nordsieck(self, step):
149153
)
150154
warnings.warn(message=warnmsg, category=RuntimeWarning)
151155
step = 1e-15 ** (1 / self.ordint)
152-
powers = np.arange(self.ordint + 1)
156+
powers = np.arange(start=-self.ordint, stop=1)
153157
diags = step ** powers
154158
precond = np.kron(np.eye(self.spatialdim), np.diag(diags))
155159
invprecond = np.kron(np.eye(self.spatialdim), np.diag(1.0 / diags))

tests/test_diffeq/test_odefiltsmooth/test_odefiltsmooth.py

+20
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,23 @@ def test_filter_ivp_h_mat52_ukf(self):
469469

470470
def test_filter_ivp_h_mat72_kf(self):
471471
probsolve_ivp(self.ivp, step=self.step, which_prior="matern72", method="eks0")
472+
473+
474+
class TestPreconditioning(unittest.TestCase):
475+
"""
476+
Solver with high order and small stepsize should work up to a point where
477+
step**order is below machine precision.
478+
"""
479+
480+
def setUp(self):
481+
initdist = RandomVariable(distribution=Dirac(20 * np.ones(2)))
482+
self.ivp = ode.lotkavolterra([0.0, 1e-4], initdist)
483+
self.step = 1e-5
484+
self.prior = "ibm3"
485+
486+
def test_small_step_feasible(self):
487+
"""
488+
With the 'old' preconditioner, this is impossible because step**(2*order + 1) is too small.
489+
With the 'new' preconditioner, the smallest value that appears in the solver code is step**order
490+
"""
491+
probsolve_ivp(self.ivp, step=self.step, which_prior=self.prior, method="eks0")

tests/test_diffeq/test_odefiltsmooth/test_prior.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646

4747
QH_21_PRE = (
4848
DIFFCONST ** 2
49-
* STEP ** 5
49+
* STEP
5050
* np.array([[1 / 20, 1 / 8, 1 / 6], [1 / 8, 1 / 3, 1 / 2], [1 / 6, 1 / 2, 1]])
5151
)
5252

0 commit comments

Comments
 (0)