Discrete callback is triggered twice each time

amynang
Mon, 17 Mar 2025 14:28:30 +0000
General Usage

I am working on a Lotka-Volterra type competition system. I am trying to specify a rule such that at pre-specified time-points the (one) species with the lowest abundance at that time-step is eliminated. I am using a discrete callback to achieve that. I am getting stuck at this: the callback works but it eliminates two species at a time. I am still struggling with Julia Types and I suspect that the problem relates to that but I have not been able to isolate it.

I would appreciate any pointers. Here is a simple example that reproduces the problem.

using DifferentialEquations
using Plots

function lotka_volterra!(du, u, p, t)
    r = [0.5, 0.6, 0.7, 0.8]
    alpha = [
        1.0 0.1 0.2 0.3;
        0.1 1.0 0.4 0.5;
        0.2 0.4 1.0 0.6;
        0.3 0.5 0.6 1.0
    ]
    for i in 1:4
        du[i] = u[i] * r[i] * (1 - sum(alpha[i, j] * u[j] for j in 1:4))
    end
end

times = [100, 200]

condition(u, t, integrator) = t ∈ times

function affect!(integrator)
    # indentify nonzero elements of u
    nonzero = findall(!iszero, integrator.u)
    # find the one with the smallest value
    minu = argmin(integrator.u[nonzero])
    idx = nonzero[minu]
    println("Eliminating species with index $idx at timestep $(integrator.t)")
    integrator.u[idx] = 0.0
end

cb = DiscreteCallback(condition, affect!)

u0 = [0.5, 0.4, 0.3, 0.2]  # Initial populations of species 1, 2, 3, 4

tspan = (0.0, 250.0)

prob = ODEProblem(lotka_volterra!, u0, tspan, callback = cb)

sol = solve(prob, tstops = times)

plot(sol)

Outside of the solver the relevant code works as expected:

u = [2.0, 0.0, 1.0, 3.0]
nonzero = findall(!iszero, u)
minu = argmin(u[nonzero])
idx = nonzero[minu]
u[idx]

16 posts - 3 participants