def test_step_variables(solvers, y, start_point, stop_point):
    testsolver, scipysolver = solvers
    solver_y_new, solver_error_estimation = testsolver.step(
        start_point, stop_point, randvars.Constant(y))
    y_new, f_new = rk.rk_step(
        scipysolver.fun,
        start_point,
        y,
        scipysolver.f,
        stop_point - start_point,
        scipysolver.A,
        scipysolver.B,
        scipysolver.C,
        scipysolver.K,
    )

    # error estimation is correct
    scipy_error_estimation = scipysolver._estimate_error(
        scipysolver.K, stop_point - start_point)
    np.testing.assert_allclose(solver_error_estimation,
                               scipy_error_estimation,
                               atol=1e-14,
                               rtol=1e-14)

    # locations are correct
    np.testing.assert_allclose(testsolver.solver.t_old,
                               start_point,
                               atol=1e-14,
                               rtol=1e-14)
    np.testing.assert_allclose(testsolver.solver.t,
                               stop_point,
                               atol=1e-14,
                               rtol=1e-14)
    np.testing.assert_allclose(
        testsolver.solver.h_previous,
        stop_point - start_point,
        atol=1e-14,
        rtol=1e-14,
    )

    # evaluations are correct
    np.testing.assert_allclose(testsolver.solver.y_old.mean,
                               y,
                               atol=1e-14,
                               rtol=1e-14)
    np.testing.assert_allclose(testsolver.solver.y,
                               y_new,
                               atol=1e-14,
                               rtol=1e-14)
    np.testing.assert_allclose(testsolver.solver.h_abs,
                               stop_point - start_point,
                               atol=1e-14,
                               rtol=1e-14)
    np.testing.assert_allclose(testsolver.solver.f,
                               f_new,
                               atol=1e-14,
                               rtol=1e-14)
    def attempt_step(self, state: _odesolver_state.ODESolverState,
                     dt: FloatLike):
        """Perform one ODE-step from start to stop and set variables to the
        corresponding values.

        To specify start and stop directly, rk_step() and not _step_impl() is used.

        Parameters
        ----------
        state
            Current state of the ODE solver.
        dt
            Step-size.

        Returns
        -------
        _odesolver_state.ODESolverState
            New state.
        """

        y_new, f_new = rk.rk_step(
            self.solver.fun,
            state.t,
            state.rv.mean,
            self.solver.f,
            dt,
            self.solver.A,
            self.solver.B,
            self.solver.C,
            self.solver.K,
        )

        # Unnormalized error estimation is used as the error estimation is normalized in
        # solve().
        error_estimation = self.solver._estimate_error(self.solver.K, dt)
        y_new_as_rv = randvars.Constant(y_new)
        new_state = _odesolver_state.ODESolverState(
            ivp=state.ivp,
            rv=y_new_as_rv,
            t=state.t + dt,
            error_estimate=error_estimation,
            reference_state=state.rv.mean,
        )

        # Update the solver settings. This part is copied from scipy's _step_impl().
        self.solver.h_previous = dt
        self.solver.y_old = state.rv.mean
        self.solver.t_old = state.t
        self.solver.t = state.t + dt
        self.solver.y = y_new
        self.solver.h_abs = dt
        self.solver.f = f_new

        return new_state
Ejemplo n.º 3
0
    def step(self, start: FloatArgType, stop: FloatArgType, current: randvars,
             **kwargs):
        """Perform one ODE-step from start to stop and set variables to the
        corresponding values.

        To specify start and stop directly, rk_step() and not _step_impl() is used.

        Parameters
        ----------
        start : float
            starting location of the step
        stop : float
            stopping location of the step
        current : :obj:`list` of :obj:`RandomVariable`
            current state of the ODE.

        Returns
        -------
        random_var : randvars.RandomVariable
            Estimated states of the discrete-time solution.
        error_estimation : float
            estimated error after having performed the step.
        """

        y = current.mean
        dt = stop - start
        y_new, f_new = rk.rk_step(
            self.solver.fun,
            start,
            y,
            self.solver.f,
            dt,
            self.solver.A,
            self.solver.B,
            self.solver.C,
            self.solver.K,
        )

        # Unnormalized error estimation is used as the error estimation is normalized in
        # solve().
        error_estimation = self.solver._estimate_error(self.solver.K, dt)
        y_new_as_rv = randvars.Constant(y_new)

        # Update the solver settings. This part is copied from scipy's _step_impl().
        self.solver.h_previous = dt
        self.solver.y_old = current
        self.solver.t_old = start
        self.solver.t = stop
        self.solver.y = y_new
        self.solver.h_abs = dt
        self.solver.f = f_new
        return y_new_as_rv, error_estimation
def test_step_variables(solvers, y, start_point, stop_point):
    testsolver, scipysolver, ode = solvers

    teststate = diffeq.ODESolverState(
        ivp=ode,
        rv=randvars.Constant(y),
        t=start_point,
        error_estimate=None,
        reference_state=None,
    )
    testsolver.initialize(ode)
    solver_y_new = testsolver.attempt_step(teststate, dt=stop_point - start_point)
    y_new, f_new = rk.rk_step(
        scipysolver.fun,
        start_point,
        y,
        scipysolver.f,
        stop_point - start_point,
        scipysolver.A,
        scipysolver.B,
        scipysolver.C,
        scipysolver.K,
    )

    # error estimation is correct
    scipy_error_estimation = scipysolver._estimate_error(
        scipysolver.K, stop_point - start_point
    )
    np.testing.assert_allclose(
        solver_y_new.error_estimate, scipy_error_estimation, atol=1e-13, rtol=1e-13
    )

    # locations are correct
    np.testing.assert_allclose(
        testsolver.solver.t_old, start_point, atol=1e-13, rtol=1e-13
    )
    np.testing.assert_allclose(testsolver.solver.t, stop_point, atol=1e-13, rtol=1e-13)
    np.testing.assert_allclose(
        testsolver.solver.h_previous,
        stop_point - start_point,
        atol=1e-13,
        rtol=1e-13,
    )

    # evaluations are correct
    np.testing.assert_allclose(testsolver.solver.y_old, y, atol=1e-13, rtol=1e-13)
    np.testing.assert_allclose(testsolver.solver.y, y_new, atol=1e-13, rtol=1e-13)
    np.testing.assert_allclose(
        testsolver.solver.h_abs, stop_point - start_point, atol=1e-13, rtol=1e-13
    )
    np.testing.assert_allclose(testsolver.solver.f, f_new, atol=1e-13, rtol=1e-13)
Ejemplo n.º 5
0
 def _step_impl(self):
     # print("called")
     t = self.t
     y = self.y
     # we will stick to a max_step untill we reach the end
     h = self.max_step
     t_new = t + h
     y_new, f_new, error = rk_step(self.fun, t, y, self.f, h, self.A,
                                   self.B, self.C, self.E, self.K)
     # this error is produced by comparing two RK23 methods
     # but we will never use it
     self.y_old = y
     self.t = t_new
     self.y = y_new
     self.h_abs = h
     self.f = f_new
     return True, None
Ejemplo n.º 6
0
    def _step_impl(self):
        t = self.t
        y = self.y

        max_step = self.max_step
        rtol = self.rtol
        atol = self.atol

        min_step = 10 * np.abs(np.nextafter(t, self.direction * np.inf) - t)

        if self.h_abs > max_step:
            h_abs = max_step
        elif self.h_abs < min_step:
            h_abs = min_step
        else:
            h_abs = self.h_abs

        order = self.order
        step_accepted = False

        while not step_accepted:
            if h_abs < min_step:
                return False, self.TOO_SMALL_STEP

            h = h_abs * self.direction
            t_new = t + h

            if self.direction * (t_new - self.t_bound) > 0:
                t_new = self.t_bound

            h = t_new - t
            h_abs = np.abs(h)

            y_new, f_new, error = rk_step(self.fun, t, y, self.f, h, self.A,
                                          self.B, self.C, self.E, self.K)
            scale = atol + np.maximum(np.abs(y), np.abs(y_new)) * rtol
            error_norm = norm(error / scale)
            if error_norm == 0.0:
                h_abs *= MAX_FACTOR
                step_accepted = True
            elif error_norm < 1:
                h_abs *= min(MAX_FACTOR,
                             max(1, SAFETY * error_norm ** (-1 / (order + 1))))
                step_accepted = True
            else:
                if (h_abs<self.min_step_user):
                    step_accepted = True
                    logger.debug("nmin step size reached")
                else:
                    h_abs *= max(MIN_FACTOR,
                             SAFETY * error_norm ** (-1 / (order + 1)))

        self.y_old = y

        self.t = t_new
        self.y = y_new

        self.h_abs = h_abs
        self.f = f_new

        return True, None