patrick-kidger / patrick-kidger/diffrax

How to properly scale with multiple event conditions?

Open
#637 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

question
Dominant language
Python
Stars
2.1k
Forks
189
Avg merge
3d 18h
Merged PRs (30d)
1

Description

Hi diffrax team,

I am working on a problem, where the amount of cond_fn scales with my state dimension. Unfortunately, this currently scales pretty badly timewise even when jitting the code. Do you have any idea what could be done to improve here?

Here as an example:

\dot{y} + y = 0
y(0) \sim [0.9, 1.0]

event condition (any state):

y - 0.1 = 0

Solving with Euler and a fixed stepsize of 0.005ms between 0 and 30ms (or until first event), it scales really badly with the state dimension:

   Dim |   Time (s)
--------------------
     1 |   0.001022
     2 |   0.000625
     4 |   0.000657
     8 |   0.001162
    16 |   0.001870
    32 |   0.004110
    64 |   0.010566
   128 |   0.044464
   200 |   0.218422

see for comparison, the timing where i just used the cond_fn on the first element of the state y[0]:

   Dim |   Time (s)
--------------------
     1 |   0.000723
     2 |   0.000612
     4 |   0.000595
     8 |   0.000615
    16 |   0.000596
    32 |   0.000716
    64 |   0.000903
   128 |   0.000724
   200 |   0.001835

Do you see any way to speed this up a reasonable amount? Do you think it would be smh possible to additionally support vectorized condition functions for events? Like:

def cond_fn(t, y, args, **kwargs):
    return y - 0.1

Thanks for the help!

Here is the MWE i used (Single cond_fn commented out)

import jax
import jax.numpy as jnp
import diffrax as dfx
import optimistix as optx
from timeit import default_timer as timer

def solve_once(y0, t0, t1, dt0, solver, stepsize_controller, event):
    term = dfx.ODETerm(lambda t, y, args: -y)
    return dfx.diffeqsolve(
        terms=term,
        solver=solver,
        t0=t0,
        t1=t1,
        dt0=dt0,
        y0=y0,
        args=None,
        stepsize_controller=stepsize_controller,
        event=event,
        saveat=dfx.SaveAt(t0=False, t1=True, steps=False),
        max_steps=3000000,
        throw=True,
    )

solve_once = jax.jit(solve_once, static_argnames=["solver", "stepsize_controller", "event"])

def benchmark_diffeqsolve_with_event():
    key = jax.random.PRNGKey(0)
    t0, t1, dt0 = 0.0, 30.0, 0.005
    solver = dfx.Euler()
    controller = dfx.ConstantStepSize()
    dims = [1, 2, 4, 8, 16, 32, 64, 128, 200]

    print(f"{'Dim':>6} | {'Time (s)':>10}")
    print("-" * 20)

    for n in dims:
        key, subkey = jax.random.split(key)
        y0 = jax.random.uniform(subkey, shape=(n,), minval=0.9, maxval=1.1)

        cond_fns = [lambda t, y, *args, i=i, **kwargs: y[i] - 0.01 for i in range(n)]
        # cond_fns = lambda t, y, *args, **kwargs: y[0] - 0.01

        event = dfx.Event(cond_fns, root_finder=optx.Newton(rtol=1e-4, atol=1e-4))

        _ = solve_once(y0, t0, t1, dt0, solver, controller, event)  # warm-up

        start = timer()
        _ = solve_once(y0, t0, t1, dt0, solver, controller, event)
        end = timer()

        print(f"{n:6d} | {end - start:10.6f}")

benchmark_diffeqsolve_with_event()

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the supplied benchmark and the dfx.Event construction in solve_once, comparing the list of scalar cond_fns with the proposed vectorized function. Measure event-solving time across the listed state dimensions; done means the event API supports the intended condition shape or clearly documents the supported approach with improved scaling.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
performance
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.