Skip to main content

Using Past Values

๐Ÿงช Experimental Features

These features are experimental and may change in future versions. Use with caution, as breaking changes can occur.

warning

Use these features with a small time step. The solver cannot guarantee high precision for results computed with delayed or differentiated variables.

Delayed Variablesโ€‹

You can create a delayed copy of a Temporal Variable using the .delayed(n_steps) method.

The n_steps argument specifies the number of time steps (not seconds) by which the variable is delayed.

In the example below, we delay a variable by 2 time steps:

step = vip.temporal(lambda t: 0 if t < 5 else 1)
delayed_step = step.delayed(2)

step.to_plot()
delayed_step.to_plot()

vip.solve(10, time_step=1)

Since the simulation uses a time step of 1 second, the delayed variable is effectively shifted by 2 seconds:

Delayed step

warning

Delays may not behave as expected when combined with events.

When an event is triggered, the solver automatically changes the time step. For example, if your time step is 1s and an event happens at 4.3s, the solver will use intermediate steps at 4s, 4.3s, and 5s. This results in irregular step sizes (e.g., 0.3s before the event and 0.7s after).

To maintain a truly constant time step even in the presence of events, pass include_events_times=False to vip.solve().

Approximate Derivativesโ€‹

The .derivative() method computes an approximate derivative of a Temporal Variable using its past values.

Ordinarily, it's not possible to compute derivatives directly with an IVP solver, as it uses numerical integration. However, by accessing past values, we can approximate the derivative using a difference quotient:

ฮ”yn={k0ifย n=0ynโˆ’ynโˆ’1tnโˆ’tnโˆ’1ifย nโ‰ฅ1\Delta y_n = \begin{cases} k_0 & \text{if } n = 0 \\ \displaystyle\frac{y_n - y_{n-1}}{t_n - t_{n-1}} & \text{if } n \geq 1 \end{cases}

Here, nn is the current time step and k0k_0 an user-defined initial value.

Because this method relies on past values, the result is slightly delayed compared to the true solution:

import numpy as np

sine = vip.temporal(lambda t: np.sin(t))
cosine_true = vip.temporal(lambda t: np.cos(t))
cosine = sine.derivative(initial_value=1)

sine.to_plot()
cosine_true.to_plot()
cosine.to_plot()

vip.solve(10)

Derivative of sine

danger

Integration should always be preferred to differentiationโ€‹

Integration provides accurate and stable results, while differentiation can introduce significant errors.

The following example demonstrates how differentiation may lead to incorrect results:

step = vip.temporal(lambda t: 0 if t < 1 else 1)
# Integrate then differentiate โ†’ Just a slight delay
i_step = vip.integrate(step, 0)
step_ok = i_step.derivative()

# Differentiate then integrate โ†’ Huge error !!!
d_step_bad = step.derivative()
step_bad = vip.integrate(d_step_bad, 0)

step.to_plot()
step_ok.to_plot()
step_bad.to_plot()

vip.solve(2, time_step=0.01)

The output shows a 96% relative error when differentiating first and then integrating. Differentiation introduces a local error that gets amplified during integration.

In contrast, integrating first and then differentiating only causes a minor delay โ€” because integration preserves numerical stability.

Differentiation error