diffeqr icon indicating copy to clipboard operation
diffeqr copied to clipboard

named vectors in Julia function

Open fkrauer opened this issue 5 years ago • 7 comments

Hi

I just started using diffeqr to fit ODE models in R. Is there a way to have named vectors in the function? Indexing works:

library(diffeqr)
theta <-  c(beta=.25, gamma=1/5, mu=1/(70*365), sigma=1/365)
maxtime <- 3650.0
init <- c(100000,0,0)

func_julia <- function(init, param, times) {
  beta <- param[1]
  gamma <- param[2]
  mu <- param[3]
  sigma <- param[4]
  S = state[1]
  I = state[2]
  R = state[3] 
  N = S + I + R
  dSdt = -beta*I*S/N + mu*N - mu*S + sigma*R
  dIdt = beta*I*S/N*S - gamma*I - mu*I 
  dRdt = gamma*I - mu*R - sigma*R
  return(c(dSdt, dIdt, dRdt))
}

enviro <- diffeqr::diffeq_setup()
problem <- enviro$ODEProblem(func_julia, init, c(0.0, maxtime), theta)
solution <- enviro$solve(problem , enviro$AutoVern7(enviro$KenCarp4()), 
                        saveat=1.0, abstol=1e-8, reltol=1e-6)

but using named vectors in R throws an error:

func_julia <- function(init, param, times) {
  beta <- param[["beta"]]
  gamma <- param[["gamma]]
  mu <- param[["mu"]]
  sigma <- param[["sigma"]]
  S = state[1]
  I = state[2]
  R = state[3] 
  N = S + I + R
  dSdt = -beta*I*S/N + mu*N - mu*S + sigma*R
  dIdt = beta*I*S/N*S - gamma*I - mu*I 
  dRdt = gamma*I - mu*R - sigma*R
  return(c(dSdt, dIdt, dRdt))
}
Error in param[["beta"]] : subscript out of bounds
Error: Error happens in Julia.

Named vectors would be great especially for inference and if there are dozens of parameters in the model. Thanks Fabienne

fkrauer avatar Sep 26 '20 12:09 fkrauer

@Non-Contradiction how do named vectors convert in JuliaCall?

ChrisRackauckas avatar Sep 27 '20 00:09 ChrisRackauckas

Currently, named vectors will have their names dropped by JuliaCall, and I'm not sure whether Julia has any data structure (similar) corresponding to named vectors in R?

I also wonders whether it is possible to use named list instead of named vectors in this example. For example, the following seems to work for me and have the same result with the unnamed version:

func_julia_named <- function(init, param, times) {
  beta <- param[["beta"]]
  gamma <- param[["gamma"]]
  mu <- param[["mu"]]
  sigma <- param[["sigma"]]
  S = state[1]
  I = state[2]
  R = state[3] 
  N = S + I + R
  dSdt = -beta*I*S/N + mu*N - mu*S + sigma*R
  dIdt = beta*I*S/N*S - gamma*I - mu*I 
  dRdt = gamma*I - mu*R - sigma*R
  return(c(dSdt, dIdt, dRdt))
}

problem_named <- enviro$ODEProblem(func_julia_named, init, c(0.0, maxtime), as.list(theta))
solution_named <- enviro$solve(problem_named , enviro$AutoVern7(enviro$KenCarp4()), 
                         saveat=1.0, abstol=1e-8, reltol=1e-6)

Notice the usage of as.list(theta) in construction of the problem_named. This should work as long as theta itself is not involved in arithmetic operation as a whole.

Non-Contradiction avatar Sep 27 '20 18:09 Non-Contradiction

Thanks. It works for me as well if I solve the problem as is. But if I "accelerate" the problem with diffeqr::jitoptimize_ode() it throws me an error:

Error: Error happens in Julia.
KeyError: key 'b' not found
Stacktrace:
 [1] getindex at .\dict.jl:467 [inlined]
 [2] #1 at .\none:0 [inlined]
 [3] iterate at .\generator.jl:47 [inlined]
 [4] join(::Base.GenericIOBuffer{Array{UInt8,1}}, ::Base.Generator{String,ModelingToolkit.var"#1#2"}, ::String) at .\strings\io.jl:294
 [5] join(::Base.GenericIOBuffer{Array{UInt8,1}}, ::Base.Generator{String,ModelingToolkit.var"#1#2"}) at .\strings\io.jl:293
 [6] sprint(::Function, ::Base.Generator{String,ModelingToolkit.var"#1#2"}; context::Nothing, sizehint::Int64) at .\strings\io.jl:105
 [7] sprint at .\strings\io.jl:101 [inlined]
 [8] join at .\strings\io.jl:300 [inlined]
 [9] map_subscripts at C:\Users\fabie\.julia\packages\ModelingToolkit\CEB9b\src\variables.jl:14 [inlined]
 [10] _broadcast_getindex_evalf at .\broadcast.jl:648 [inlined]
 [11] _broadcast_getindex at .\broadcast.jl:621 [inlined]
 [12] #19 at .\broadcast.jl:1046 [inlined]
 [13] ntuple at .\ntuple.jl:41 [inlined]
 [14] copy at .\bro

Is there a way around this? This would be a huge advantage when fitting complex models. Thanks, Fabienne

fkrauer avatar Sep 27 '20 20:09 fkrauer

Currently, named vectors will have their names dropped by JuliaCall, and I'm not sure whether Julia has any data structure (similar) corresponding to named vectors in R?

https://github.com/SciML/LabelledArrays.jl

ChrisRackauckas avatar Sep 27 '20 20:09 ChrisRackauckas

@fkrauer I have some intuition for what causes the error: the key "beta" of the list in R (will be dict in Julia) somehow gets iterated and be decomposed into 'b'+'e'+'t'+'a', and diffeqr looks for elements with keys 'b', 'e', 't', and 'a' in the list, which does not exist... so the error occurs. The behavior makes sense, because by numerical index, like a[1:3], what we usually mean is actually [a[1], a[2], a[3]], but this is not true with string keys.

Non-Contradiction avatar Sep 27 '20 22:09 Non-Contradiction

I implemented an "uggly" version using LabelledArrays. The version can be "beautified" if some code are integrated into JuliaCall. However, it seems that this version still doesn't work with diffeqr::jitoptimize_ode(). I guess it may be due to the julia_call("getindex" ....) thing. But I also feel that even I fix this, a problem similar with as.list(theta) will still be prevent diffeqr::jitoptimize_ode().

func_julia_LabelledArrays <- function(init, param, times) {
  beta <- julia_call("getindex", param, quote(beta)) ## param[["beta"]]
  gamma <- julia_call("getindex", param, quote(gamma)) ## param[["gamma"]]
  mu <- julia_call("getindex", param, quote(mu))  ## param[["mu"]]
  sigma <- julia_call("getindex", param, quote(sigma)) ## param[["sigma"]]
  S = state[1]
  I = state[2]
  R = state[3] 
  N = S + I + R
  dSdt = -beta*I*S/N + mu*N - mu*S + sigma*R
  dIdt = beta*I*S/N*S - gamma*I - mu*I 
  dRdt = gamma*I - mu*R - sigma*R
  return(c(dSdt, dIdt, dRdt))
}

library(JuliaCall)
julia_command("using LabelledArrays")
autowrap("LabelledArrays.LArray")
problem_LabelledArrays <- enviro$ODEProblem(func_julia_LabelledArrays, init, c(0.0, maxtime), 
                                            julia_eval("LVector(beta=.25, gamma=1/5, mu=1/(70*365), sigma=1/365)"))
solution_LabelledArrays <- enviro$solve(problem_LabelledArrays, enviro$AutoVern7(enviro$KenCarp4()), 
                               saveat=1.0, abstol=1e-8, reltol=1e-6)

Non-Contradiction avatar Sep 27 '20 23:09 Non-Contradiction

Running:

library(diffeqr)
enviro <- diffeqr::diffeq_setup()

theta <-  c(beta=.25, gamma=1/5, mu=1/(70*365), sigma=1/365)
maxtime <- 3650.0
init <- c(100000,0,0)

func_julia_LabelledArrays <- function(init, param, times) {
  beta <- julia_call("getindex", param, quote(beta)) ## param[["beta"]]
  gamma <- julia_call("getindex", param, quote(gamma)) ## param[["gamma"]]
  mu <- julia_call("getindex", param, quote(mu))  ## param[["mu"]]
  sigma <- julia_call("getindex", param, quote(sigma)) ## param[["sigma"]]
  S = state[1]
  I = state[2]
  R = state[3] 
  N = S + I + R
  dSdt = -beta*I*S/N + mu*N - mu*S + sigma*R
  dIdt = beta*I*S/N*S - gamma*I - mu*I 
  dRdt = gamma*I - mu*R - sigma*R
  return(c(dSdt, dIdt, dRdt))
}

library(JuliaCall)
julia_command("using LabelledArrays")
autowrap("LabelledArrays.LArray")
problem_LabelledArrays <- enviro$ODEProblem(func_julia_LabelledArrays, init, c(0.0, maxtime), 
                                            julia_eval("LVector(beta=.25, gamma=1/5, mu=1/(70*365), sigma=1/365)"))
solution_LabelledArrays <- enviro$solve(problem_LabelledArrays, enviro$AutoVern7(enviro$KenCarp4()), 
                                        saveat=1.0, abstol=1e-8, reltol=1e-6)
diffeqr::jitoptimize_ode(enviro,problem_LabelledArrays)

fails with

 Error: Error happens in Julia.
ArgumentError: invalid index: :beta of type Symbol
Stacktrace:
 [1] to_index(::Symbol) at .\indices.jl:297
 [2] to_index(::Array{Operation,1}, ::Symbol) at .\indices.jl:274
 [3] to_indices at .\indices.jl:325 [inlined]
 [4] to_indices at .\indices.jl:322 [inlined]
 [5] getindex(::Array{Operation,1}, ::Symbol) at .\abstractarray.jl:1060
 [6] docall(::Ptr{Nothing}) at D:\OneDrive\Computer\Documents\R\win-library\4.0\JuliaCall\julia\setup.jl:168
 [7] (::RCall.var"#32#33"{LangSxp,Ptr{LangSxp},Ptr{EnvSxp},Base.RefValue{Int32}})() at C:\Users\accou\.julia\packages\RCall\Qzssx\src\eval.jl:84
 [8] disable_sigint at .\c.jl:446 [inlined]
 [9] tryEval at C:\Users\accou\.julia\packages\RCall\Qzssx\src\eval.jl:81 [inlined]
 [10] reval_p(::Ptr{LangSxp}, ::Ptr{EnvSxp}) at C:\Users\accou\.julia\packages\RCall\Qzssx\src\eval.jl:96
 [11] reval_p at C:\Users\accou\.julia\packages\RCall\Qzssx\src\eval.jl:95 [inlined]
 [12] rcall_p(::RObject{ClosSxp}, ::Array{Operation,1}, : 

The error seems to be because A.x in a LabelledArray lowers to A[:x], but I think JuliaCall might have a wrapper over the type so this dispatch is lost in the autowrap version?

ChrisRackauckas avatar Sep 28 '20 00:09 ChrisRackauckas