patrick-kidger / patrick-kidger/diffrax
Event and PIDController: event doesn't always occure
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 2.1k
- Forks
- 189
- Avg merge
- 3d 18h
- Merged PRs (30d)
- 1
Description
First, I want to thank you for your amazing library. You have done a massive work which are very useful for my research.
diffrax 0.6.0
optimistix 0.0.7
jax 0.4.30
When using PID controler and Event functionnality are used simultaneously, I found that the event will not always raised due to the difference of complexity between the event function and the ode function. For example, If the ode function si very simple (straight line), the pid controler will allow to have large steps. Large step can miss the event condition if there is two changes of signs between this two steps.
To avoid this issue, I found that integrate the event condition in the ode function can correct the issue in my particular usecase. Is there a more mathematical grounded method to resolve this issue?
A small python example:
import jax
import jax.numpy as jnp
import jax.random as random
jax.config.update("jax_enable_x64", True)
import diffrax
import optimistix as optx
def event(x, high_pres=False,coeff=1.0):
x = jnp.concatenate([jnp.ones((1, 1)) * x, jnp.zeros((1, 1))], axis=1)
# event_condition = (2.5 - y) . (5.0 - y) . (0.0 - y)
event_condition = lambda t, y, args, **kwargs: (-y[0, 0] + 2.5) * (-y[0, 0] + 5.0) * (-y[0, 0] + 0.0)
# dx/dt = [1, 0]
# adding the event condition: dx/dt = [1, 0, | event_condition(t, x) | * coeff ]
# coeff used to defined if the event condition is taken into account or not
ode_fun = lambda t, x_, args: jnp.concatenate([jnp.ones((1,1)), jnp.zeros((1,1)), coeff * jnp.expand_dims(jnp.expand_dims(jnp.abs(event_condition(t, x_, args)),axis=0),axis=0)],axis=1)
fun = diffrax.ODETerm(ode_fun)
if high_pres:
stepsize_controller = diffrax.PIDController(rtol=1E-14, atol=1E-14)
else:
stepsize_controller = diffrax.PIDController(rtol=1E-8, atol=1E-8)
solver = diffrax.Dopri8()
root_finder = optx.Bisection(1E-10, 1E-10)
t1 = 10
sol = diffrax.diffeqsolve(
fun,
solver,
0.0,
t1,
None,
jnp.concatenate([x, jnp.zeros((1,1))],axis=1),
stepsize_controller=stepsize_controller,
max_steps=None,
event=diffrax.Event(event_condition, root_finder),
throw=False
)
event_occurred = diffrax.RESULTS.event_occurred == sol.result
t_result = sol.ts
x_last = sol.ys[0][:,:2]
print(event_occurred, x_last, t_result)
print(sol.result)
event(0.5, high_pres=True, coeff=0.0) # -> with high precision (1E-14), the event is detected
event(0.5, high_pres=False, coeff=0.0) # -> with "low" precision (1E-8), the event is not detected
event(0.5, high_pres=False, coeff=1.0) # -> with "low" precision (1E-8) and take into account the event condition on the ode terms, the event is detected
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the provided Python example with the stated diffrax, optimistix, and JAX versions, comparing PIDController settings and event outcomes. Investigate the interaction between Event root detection and large adaptive steps; done means establishing a mathematically grounded resolution and demonstrating reliable detection for the reported case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100