Thursday, July 11, 2013

Differential equation modeling with Julia

In the last few examples of epidemiological modeling, I focused on stochastic models. However, most published models are in the form of a set of differential equations. In R, I often use simecol. The ODE package in Julia offers some functionality for solving ordinary differential equations, which should be very familiar to users of Matlab/Octave.

Here's the old favourite, the susceptible-infected-recovered model again.

# Load libraries
using ODE
using DataFrames
using Gadfly

# Define the model
# Has to return a column vector
function SIR(t,x)
    S=x[1]
    I=x[2]
    R=x[3]
    beta=x[4]
    gamma=x[5]
    N=S+I
    dS=-beta*S*I/N
    dI=beta*S*I/N-gamma*I
    dR=gamma*I
    return([dS;dI;dR;0;0])
end

# Initialise model
t = linspace(0,500,101);
inits=[9999,1,0,0.1,0.05];

# Run model
result=ode23(SIR,t,inits);

# Collate results in DataFrame
df=DataFrame();
df["t"]=result[1];
df["S"]=result[2][:,1];
df["I"]=result[2][:,2];
df["R"]=result[2][:,3];

# Plot using Gadfly
p=Gadfly.plot(df,x="t",y="I",Geom.line)

4000 -4000 2000 6000 1000 3000 -1000 5000 0 7000 -2000 -3000 I -600 600 900 -400 800 0 1000 -300 -500 200 700 1100 300 500 -200 -100 400 100 t

There are a few things to note; firstly, the syntax of the ode23 command expects only the time and a state vector. For parameter values, one can hard-code them in the model description (not good) or pass them as additional variables (with gradient 0, so they don't change). I don't really like either option. Perhaps one can use macros to do much the same job, but I'm not that well acquainted with Julia yet.


5 comments:

  1. Lexical scoping and anonymous functions are your friends here. If you are solving x' = f(t,x, p), with p being some additional parameter(s), and you have the function f, you can just do:

    ode23((t,x) -> f(t,x,p), trange, x0)

    in order to pass some additional parameteter p to f.

    ReplyDelete
    Replies
    1. Thanks Steven! Updated code as follows.

      function SIR(t,x,p)
      S=x[1]
      I=x[2]
      R=x[3]
      beta=p[1]
      gamma=p[2]
      N=S+I
      dS=-beta*S*I/N
      dI=beta*S*I/N-gamma*I
      dR=gamma*I
      return([dS;dI;dR])
      end

      # Initialise model
      t = linspace(0,500,101);
      inits=[9999,1,0];
      p=[0.1,0.05];

      # Run model
      result=ode23((t,x)->SIR(t,x,p),t,inits);

      Delete
  2. Hello,

    I begin with Julia, trying to do the same with a 3d (non-linear) system to simulate. I used your code and compiled to check if it worked for me and it did. I then modified your setting for mine, but compiling gives error.

    HERE IS THE CODE :

    # Load libraries
    using ODE # ODE package for simulations of the system
    using DataFrames # DataFrames package for managing matrices
    using Gadfly # Gadfly package for graphics
    # please load these packages if they are not in your library

    # If using IJulia and Gadfly, executing generates these natural errors
    # Warning: could not import Base.foldl into NumericExtensions
    # Warning: could not import Base.foldr into NumericExtensions
    # Warning: could not import Base.sum! into NumericExtensions
    # Warning: could not import Base.maximum! into NumericExtensions
    # Warning: could not import Base.minimum! into NumericExtensions
    # Warning: could not import StatsBase.bandwidth into Stat
    # Warning: could not import StatsBase.kde into Stat

    # Define the direct parameters of the dynamical system
    nu = 3 # capital-to-output ratio
    alpha = 0.025 # productivity growth rate
    beta = 0.02 # population growth rate
    gamma = 0.07 # markup adjustement speed and worst deflation rate
    delta = 0.01 # depreciation rate of capital
    r = 0.04 # interest rate on deposit/debt
    c_1 = 0.9 # propensity to consume out of wage
    c_2 = 0.03 # propensity to consume out of wealth
    eta_p = 0.7 # rigidity factor of inflation in wage progression
    m = 1.15 # target markup factor, m-1 = target profit rate

    par_phi_0 = (0.04^3)/(1-0.04^2)
    par_phi_1 = 0.04/(1-0.04^2)

    par_kappa_0 = -0.01
    par_kappa_1 = 0.9
    par_kappa_2 = 2.5

    parameters = [
    nu;
    alpha;
    beta;
    gamma;
    delta;
    r;
    c_1;
    c_2;
    eta_p;
    m;
    par_phi_0;
    par_phi_1;
    par_kappa_0;
    par_kappa_1;
    par_kappa_2;
    ]

    # Define the model
    # Has to return a column vector of dim 3
    function D3(t,X,p)

    #state space variables
    om = X[1]
    lam = X[2]
    d = X[3]

    #parameters
    nu = p[1]
    alpha = p[2]
    beta = p[3]
    gamma = p[4]
    delta = p[5]
    r = p[6]
    c_1 = p[7]
    c_2 = p[8]
    eta_p = p[9]
    m = p[10]
    par_phi = p[11:12]
    par_kappa = p[13:15]

    # Define the Philips curve function and its parameters
    function fun_Phi(x, p)
    return p[1] + p[2]/(1-x)^2
    end

    # Define the investment function and its parameters
    function fun_Kappa(x,p)
    return p[1] + p[2]*x*exp(p[3]*x)
    end

    #additional values
    pi = 1 - om - r*d
    y_d = c_1 * om + c_2 * d + fun_Kappa(pi, par_kappa)
    inflation_rate = gamma * (m * om - 1)
    #dynamics
    d_om = om * (fun_Phi(lam, par_phi) - alpha - (1-eta_p) * inflation_rate)
    d_lam = lam * (fun_Kappa(pi, par_kappa)/nu - alpha - beta - delta)
    d_deb = d * (r + delta -inflation_rate - fun_Kappa(pi, par_kappa)/nu) + fun_Kappa(pi, par_kappa) - (1-om)*y_d

    return([d_om;d_lam;d_deb])
    end

    # Initialise model
    t = linspace(0,500,101);
    Omega_0 = 0.70
    Lambda_0 = 0.85
    Debt_0 = 0.1
    inits=[Omega_0,Lambda_0,Debt_0];

    # Run model
    result=ode23((t,x)->D3(t,x,parameters),t,inits);

    # Collate results in DataFrame
    df=DataFrame();
    df["time"]=result[1];
    df["omega_e"]=result[2][:,1];
    df["lambda"]=result[2][:,2];
    df["d_e"]=result[2][:,3];

    HERE IS THE ANSWER :
    no method append!(DataArray{Float64,1},DataArray{Float64,1})
    in cat_aes_var! at /home/adrien/.julia/v0.2/Gadfly/src/aesthetics.jl:238
    in cat at /home/adrien/.julia/v0.2/Gadfly/src/aesthetics.jl:217
    in render at /home/adrien/.julia/v0.2/Gadfly/src/Gadfly.jl:644
    in writemime at /home/adrien/.julia/v0.2/Gadfly/src/Gadfly.jl:751
    in sprint at io.jl:434
    in display_dict at /home/adrien/.julia/v0.2/IJulia/src/execute_request.jl:35

    Do you have any clue of what happened? I understand that with semantic scope, the way I defined parameters is ok. I hope the code is clear.

    Thank You,

    ReplyDelete
  3. Hi,

    Sorry for confusion, I had to delete the last part of code generating graphs, which is what actually what generates the error. It is different :

    plot(df, layer(x="time", y="omega_e", Geom.point),
    layer(x="time", y="lambda", Geom.line),
    layer(x="time", y="d_e", Geom.line))

    I'll try to correct it myself from here : http://dcjones.github.io/Gadfly.jl/#layers

    ReplyDelete
  4. Hi, Very nice description about Video Ranking Formula. I like your web blog.
    Because whenever i come into your web blog then i always get the new interesting and important information in your web blog.

    Thank You

    Viral factor Formula

    ReplyDelete