DifferentialEquations.jl icon indicating copy to clipboard operation
DifferentialEquations.jl copied to clipboard

`PeriodicCallback` not triggering with `Rational` numbers

Open jonniedie opened this issue 2 years ago • 1 comments

It can be nice to use Rational numbers in PeriodicCallbacks if you have a bunch of different discrete things that fire at different rates. That way you can take the gcd of the sample Δts, have one single callback function that checks if the current t is a multiple of the specific Δt for each affect. The problem is it seems that Rational numbers don't cause the PeriodicCallback to trigger.

using DifferentialEquations

function continuous_dynamics!(du, u, p, t)
	(; f, A, B) = p
	du .= A*u + B*f[]
end

function control_update!(integrator)
	(; p, u) = integrator
	integrator.p.f[] = -p.k'u
end

p = (
	k = [8, 4],
	f = Ref(0.0),
	A = [0 1; 2 0],
	B = [0, 1],
)
x0 = [1.0, 0.0]
tspan = (0.0, 2.0)
control_Δt = 1//10

prob = ODEProblem(continuous_dynamics!, x0, tspan, p)
cb = PeriodicCallback(control_update!, control_Δt; initial_affect=true)

sol = solve(prob, OrdinaryDiffEq.Tsit5(), callback=cb)

Plotting this we see that the control never triggers past t=0: bad

If we change control_Δt to a Float64, though, it works as expected: good

I currently work around this by just converting it to Float64 for the callback and then converting back to the Rational{Int} that's the closest multiple of the overall Δt. Which works, it's just a little hacky.

jonniedie avatar Jun 24 '22 13:06 jonniedie

That's because for example there is no Float64 which equals 3//10 so Float64(3//10) == 3//10 == false. We could change https://github.com/SciML/DiffEqCallbacks.jl/blob/master/src/iterative_and_periodic.jl#L101 the condition to be, instead of exact, make it within some epsilon tolerance to account for rounding.

ChrisRackauckas avatar Jun 24 '22 22:06 ChrisRackauckas