def test_convergence_error(ivp, algo_order): """Assert that by halfing the step-size, the error of the small step is roughly the error of the large step multiplied with (small / large)**(nu)""" # Set up two different step-sizes step_large = 0.2 step_small = 0.5 * step_large expected_decay = (step_small / step_large) ** algo_order # Solve IVP with both step-sizes f = ivp.rhs t0, tmax = ivp.timespan y0 = ivp.initrv.mean sol_small_step = probsolve_ivp( f, t0, tmax, y0, step=step_small, algo_order=algo_order, adaptive=False ) sol_large_step = probsolve_ivp( f, t0, tmax, y0, step=step_large, algo_order=algo_order, adaptive=False ) # Check that the final point is identical (sanity check) np.testing.assert_allclose(sol_small_step.t[-1], sol_large_step.t[-1]) # Compute both errors ref_sol = ivp.solution(sol_small_step.t[-1]) err_small_step = np.linalg.norm(ref_sol - sol_small_step.y[-1].mean) err_large_step = np.linalg.norm(ref_sol - sol_large_step.y[-1].mean) # Non-strict rtol, bc this test is flaky by construction # As long as rtol < 1., this test seems meaningful. np.testing.assert_allclose( err_small_step, expected_decay * err_large_step, rtol=0.9 )
def test_filter_ivp_mat32_kf(self): probsolve_ivp( self.ivp, atol=self.tol, rtol=self.tol, which_prior="matern32", method="eks0", )
def test_filter_ivp_mat52_ekf(self): probsolve_ivp( self.ivp, atol=self.tol, rtol=self.tol, which_prior="matern52", method="eks1", )
def test_no_step_or_tol_info_raises_error(ivp): """Providing neither a step-size nor a tolerance raises an error.""" f = ivp.rhs t0, tmax = ivp.timespan y0 = ivp.initrv.mean with pytest.raises(ValueError): probsolve_ivp(f, t0, tmax, y0, step=None, adaptive=True, atol=None, rtol=None)
def test_small_step_feasible(self): """With the 'old' preconditioner, this is impossible because step**(2*order + 1) is too small. With the 'new' preconditioner, the smallest value that appears in the solver code is step**order """ probsolve_ivp(self.ivp, step=self.step, which_prior=self.prior, method="eks0")
def test_wrong_method_raises_error(ivp): """Methods that are not in the list raise errors.""" f = ivp.rhs t0, tmax = ivp.timespan y0 = ivp.initrv.mean # UK1 does not exist anymore with pytest.raises(ValueError): probsolve_ivp(f, t0, tmax, y0, method="UK")
def test_filter_ivp_ioup3_ukf(self): """ UKF requires some evaluation-variance to have a positive definite innovation matrix, apparently. """ probsolve_ivp(self.ivp, tol=self.tol, evlvar=0.01, which_prior="ioup3", method="uks")
def test_filter_ivp_h_mat52_ukf(self): """ UKF requires some evaluation-variance to have a positive definite innovation matrix, apparently. """ probsolve_ivp(self.ivp, step=self.step, evlvar=0.01, which_prior="matern52", method="uks")
def test_error_ioup2(self): """Expect error rate q+1.""" stp1, stp2 = self.stps sol = probsolve_ivp(self.ivp, step=stp1, which_prior="ioup2") means1 = sol.y.mean sols1 = np.array([self.ivp.solution(t) for t in sol.t]) err1 = np.amax(np.abs(sols1 - means1)) sol = probsolve_ivp(self.ivp, step=stp2, which_prior="ioup2") means2 = sol.y.mean sols2 = np.array([self.ivp.solution(t) for t in sol.t]) err2 = np.amax(np.abs(sols2 - means2)) exp_decay = (stp2 / stp1) ** 3 diff = np.abs(exp_decay * err1 - err2) / np.abs(err2) self.assertLess(diff, 1.0)
def test_adaptive_solver_successful(ivp, method, algo_order, dense_output, step): """The solver terminates successfully for all sorts of parametrizations.""" f = ivp.rhs df = ivp.jacobian t0, tmax = ivp.timespan y0 = ivp.initrv.mean sol = probsolve_ivp( f, t0, tmax, y0, df=df, adaptive=True, atol=1e-1, rtol=1e-1, algo_order=algo_order, method=method, dense_output=dense_output, step=step, ) # Successful return value as documented assert isinstance(sol, KalmanODESolution) # Adaptive steps are not evenly distributed step_diff = np.diff(sol.t) step_ratio = np.amin(step_diff) / np.amax(step_diff) assert step_ratio < 0.5
def test_kf_ibm1(self): """Tests whether resulting steps are not evenly distributed.""" sol = probsolve_ivp( self.ivp, atol=self.tol, rtol=self.tol, which_prior="ibm1", method="eks0" ) steps = np.diff(sol.t) self.assertLess(np.amin(steps) / np.amax(steps), 0.8)
def test_kf_ibm1_stdev(self): """Standard deviation at end point roughly equal to tolerance.""" sol = probsolve_ivp( self.ivp, atol=self.tol, rtol=self.tol, which_prior="ibm1", method="eks0" ) self.assertLess(np.sqrt(sol.y.cov[-1, 0, 0]), 10 * self.tol) self.assertLess(0.1 * self.tol, np.sqrt(sol.y.cov[-1, 0, 0]))
def setUp(self): initrv = Constant(0.1 * np.ones(1)) self.ivp = ode.logistic([0.0, 1.5], initrv) self.step = 0.5 sol = probsolve_ivp(self.ivp, step=self.step, diffconst=1.0, which_prior="ibm1") state_rvs = sol.kalman_posterior.filtering_posterior.state_rvs self.ms, self.cs = state_rvs.mean, state_rvs.cov
def setUp(self): initrv = Dirac(0.1 * np.ones(1)) self.ivp = ode.logistic([0.0, 1.5], initrv) self.step = 0.5 sol = probsolve_ivp(self.ivp, step=self.step, initrv=initrv, diffconst=1.0, which_prior="ibm1") state_rvs = sol._state_rvs self.ms, self.cs = state_rvs.mean, state_rvs.cov
def sol(ivp, step): f = ivp.rhs t0, tmax = ivp.timespan y0 = ivp.initrv.mean return probsolve_ivp( f, t0, tmax, y0, method="ek0", algo_order=1, adaptive=False, step=step, )
def test_filter_ivp_ioup1_kf(self): probsolve_ivp(self.ivp, tol=self.tol, which_prior="ioup1", method="eks0")
def test_filter_ivp_h_mat72_kf(self): probsolve_ivp(self.ivp, step=self.step, which_prior="matern72", method="eks0")
def test_filter_ivp_h_mat32_ekf(self): probsolve_ivp(self.ivp, step=self.step, which_prior="matern32", method="eks1")
def test_filter_ivp_h_ioup3_kf(self): probsolve_ivp(self.ivp, step=self.step, which_prior="ioup3", method="eks0")
def test_filter_ivp_ioup2_ekf(self): probsolve_ivp( self.ivp, atol=self.tol, rtol=self.tol, which_prior="ioup2", method="eks1" )