From the docs:
VectorContinuousCallback] https://diffeq.sciml.ai/stable/features/callback_functions/#SciMLBase.VectorContinuousCallback) works like a vector of
ContinuousCallbacks and lets the user specify which callback is called when.
Multiple callbacks can be chained together to form a
CallbackSet . A
CallbackSet is constructed by passing the constructor
VectorContinuousCallback or other
What exactly is the purpose of a VectorContinousCallback if you could should just use a CallbackSet of multiple ContinuousCallback? This seems to be like two options trying to achieve the same thing.
Additionally the VectorContinousCallback also requires some if else synthax sugaring in the affect function which does not look clean.
function condition(out,u,t,integrator) # Event when event_f(u,t) == 0
out = u
out = (u - 10.0)u
function affect!(integrator, idx)
if idx == 1
integrator.u = -0.9integrator.u
elseif idx == 2
integrator.u = -0.9integrator.u
cb = VectorContinuousCallback(condition,affect!,2)
They have very different performance characteristics. VectofContinuousCallbacks will scale to thousands of callbacks, but CallbackSet will hit compile time issues at around 10 or so.
Alright that makes sense so far. But with that the question arises how the situation looks like for DiscreteCallbacks? A VectorDiscreteCallback doesn’t seem to exist, is there a reasoning behind that?
If possible I suggest to include your explanation somewhere in the docs because I think this is important to know when making design decisions as a user.
DiscreteCallbacks run off of a true false. You can easily do a “VectorDiscreteCallback” by just doing
bool1 && bool2. Implicit state in the condition is then all that’s needed to know which one fired. So no extra machinery on the backend is required to do it.
VectorContinuousCallbacks on the other hand cannot be made from ContinuousCallbacks because it requires the ability to run multiple rootfinding problems simultaneously.
root1 * root2 can work in a pinch, but there are many ways which this can fail (for example,
0 * NaN). Also the problem of attribution, i.e. finding which callback fired first, can become an issue. Using a CallbackSet of ContinuousCallbacks stacks them and if two fire in the same interval, it takes the one that fires first. But that requires doubling the interpolation, i.e. doing two completely independent rootfinds. If you have
n of them, first of all you have an
n type tuple (so compile times die), and then you also have
n rootfinding problems so your runtimes die. It’s just not a feasible approach to scale. But VectorContinuousCallbacks have quite a bit of overhead in the scalar case, and a more cumbersome API, so leaving ContinuousCallbacks alone was the solution in the end.
I thought it was, but it wasn’t. See if this quenches your thirst:
Thanks a lot!
The updated docs look good to me.
@ChrisRackauckas I just wanted to briefly follow up on the question posed in this thread, specifically about the idea of a
VectorDiscreteCallback. Could you elaborate on how this would work?
I’m asking this question in the context of using
DiscreteCallbacks when I have high dimensional state vectors, and I want to check a condition on each component. As a thought-example, suppose I have a 10-dimensional vector
u where the first five entries
u[1:5] represent velocities and the last five
u[6:10] are the corresponding positions. Suppose, after each integration step, I want to kick the velocity if its corresponding position is below zero. E.g. if the condition
u <= 0, then I do the action
u += 10; if
u <= 0, then I do
u += 10, and so on for each component.
In this case, it seems like I would have to write five separate
affect functions for each of the components I am separately checking. Or is there a better solution that I’m not aware of for this to have just a single callback that affects all of the components in a single step?
If I do have to write separate discrete callbacks for each component of the state vector, is the best practice then to wrap all of those in a
Also — you mentioned some performance limits of
Is that statement about compile time issues true for a
DiscreteCallbacks as well?
No, just make one DiscreteCallback as described above.