Why does this function compile on every iteration?

This function is intended to be called repeatedly until it converges to some value of Xnew:

using DifferentialEquations
using ControlSystemsBase.MatrixEquations: lyapd

function newton_iterate(X::Union{ODESolution,Function}, 
mathieu::GeneralizedMathieu)
    lqrparams = LQRParamsMathieu()
    T = 2*pi/mathieu.ω
    M = B * inv(lqrparams.r) * B'
    
    ϕ = state_transition(mathieu, M, X)
    
    # Find Xnew_init = X_{n+1}(0) == X_{n+1}(T)
    integrand(τ) = ϕ(τ)' * (lqrparams.Q + X(τ) * M * X(τ)) * ϕ(τ)
    Xnew_init = lyapd(ϕ(T)', ∫(integrand, 0, T))
    
    # Find Xnew(t) = X_{n+1}(t) by integrating the periodic Lyapunov equation
    L_operator(Xnp1, _, t) = 
        -(A(t, mathieu, M, X)' * Xnp1 + Xnp1 * A(t, mathieu, M, X) + (X(t) * M * X(t) + lqrparams.Q))
    PLEProb = ODEProblem(L_operator, Xnew_init, (T, 0), nothing)
    Xnew = solve(PLEProb, Tsit5(), saveat=T/50.0)
end

Unfortunately,

  • it compiles on every call, and
  • around the sixth iteration it consumed 10+ GB of RAM and hung indefinitely (1+ hour).

I’m trying to understand why it compiles on every call. I suspect the RAM issue is related. Here is a summary of what it does:

  1. Accept as input a 1-arg function or ODE solution X.
  2. X is used as a parameter to find two intermediate quantites: ϕ and Xnew_init.
  3. ϕ(t) is the solution to an ODE that depends on X(t). Xnew_init is found by integrating X(t) and solving an algebraic equation.
  4. Finally, ϕ(t), Xnew_init and X(t) are used in the inputs to an ODEProblem whose solution is Xnew.
  5. The function can then be called with Xnew as input, starting the process over. (It’s a Newton-Raphson iteration step applied to a time-varying Differential Ricatti Equation.)

The other quantities (B, mathieu, lqrparams etc) are just constants defined in the function or in the calling environment. is a simple Gaussian quadrature-based integrator. I can supply more details if required.

I’m guessing it’s the functions you define inside the function that keep compiling anew. Can you move those outside and make the variables arguments?