-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Catalyst/PEtab integration #61
Comments
This looks like a good first proposal, and I think this integration would be great for allowing smooth parameter estimation in Julia. First. some general comments first (see comments on the struct): # I think this a good structure (might be worth to call it CatalystPEtabModel at technically it is not a full
# PEtab model as it does not follow the full PEtab format). Would also allow me to then apply an extra
# parsing step before creating ODE-problem, so I could have
# createPEtabODEProblem(model::CatalystPEtabModel, ...)
struct CatalystPEtabModel
system::ReactionSystem
experimental_conditions::Vector{PEtabExperimentalCondition}
observables::Vector{PEtabObservable}
meassurments::Vector{PEtabMeassurment}
parameters::Vector{PEtabParameter}
end
#=
Looks good - but each experimental condition should have a string-id (or similar). Similar for observable
id
=#
struct PEtabExperimentalCondition
parameter_values::Dict{Num, Float64}
end
#=
Each observable should be given by a string ID
=#
struct PEtabObservable
obs::Num
end
#=
I think a DataFrame is the best option here (but we can keep this struct to allow anyone to simulate data).
See my comments on the fields. There are also observable parameters and PreEq ID:s (if we have a steady
state simulation), but I think we can add those later (should be straightforward).
=#
struct PEtabMeassurment
exp_id::String # Should be a text id
obs_id::String # Should be a text id
value::Float64
time_point::Float64
#=
The sigma-parameter (measurement noise) can either be a value or a parameter in the parameters-struct.
Has to be here, as different measurement-series (even of the same observable) can have different
measurement noise.
=#
noise_parameter::Union{Float64, Num}
end
#=
# For all of these, good defaults in PEtab.jl exists. Maybe should make each type Union{Nothing,...},
with Nothing indicating that PEtabl.jl selects its default. Nothing is a good thing here for defaults :),
note that prior should be allowed to be nothing (if no prior is given).
This looks good, but by PEtab standard replace isconstant with estimate
=#
struct PEtabParameter
parameter::Num
isconstant::Bool
value::Union{Nothing,Float64} # if isconstant==false, value=Nothing.
lb::Float64
ub::Float64
prior::Distribution{Univariate, Continuous}
scale::Symbol # :log10, :linear and :log supported.
end Overall I think the following would work. If catalyst provides a CatalystPEtabModel then PEtab can use the information to basically create the PEtab-files (they do not have to explicitly created). Given the PEtab-files a PEtabODEProblem can easily be created. So I imagine a workflow like petab_model = CatalystPEtabModel(...)
# I expand createPEtabODEProblem to handle CatalystPEtabModel in PEtab.jl
petab_problem = createPEtabODEProblem(petab_model::CatalystPEtabModel, ...) So if you could provide a CatalystPEtabModel on the formt above for a simple model, say; Later in order to create a robust test-suite we can recreate the PEtab test-suite using reaction-system. Overall, what do you think? |
This looks good, here is the altered struct CatalystPEtabModel
system::ReactionSystem
experimental_conditions::Dict{String,PEtabExperimentalCondition}
observables::Dict{String,PEtabObservable}
meassurments::DataFrame
parameters::Vector{PEtabParameter}
end
struct PEtabExperimentalCondition
parameter_values::Dict{Num, Float64}
id::String
end
struct PEtabObservable
obs::Num
id::String
end
struct PEtabParameter
parameter::Num
estimate::Bool
value::Union{Nothing,Float64}
lb::Union{Nothing,Float64}
ub::Union{Nothing,Float64}
prior::Union{Nothing,Distribution{Univariate, Continuous}}
scale::Union{Nothing,Symbol} # :log10, :linear and :log supported.
end I will create an example as well that we could use to try and fit data. I agree, let's do steady state after we have a working methodology without. My only question is regarding having |
Also, exactly how do you want the |
I don't think we can restrict a DataFrame, on the other side, writing a function to check the DataFrames is straightforward. Good question, preferably as a state_map (which is already how we do it), i.e on the format: state_map = [x => 207.6*k1, y => 0.0] That is, for each model state either a numerical value, or an equation (depending on model parameters, which can be an initial value) are okay to supply. |
Sounds good. Should I make the initial conditions part of the struct PEtabExperimentalCondition
parameter_values::Dict{Num, Float64}
u0::Vector{Pair{Num.Union{Float64,Num}}}
end ? Here I put the first value to a |
I think actually u0 should be something entirely separate (which is then provided into CatalystPEtabModel). I am thinking about doing as is done in ModellingToolkit where when building on ODEProblem the user can if they want to provide a state-map (as here https://docs.sciml.ai/ModelingToolkit/stable/tutorials/ode_modeling/). And for parameter values the first value being Num looks good. |
I am not entirely sure what you mean, You mean something like this where struct CatalystPEtabModel
system::ReactionSystem
experimental_conditions::Vector{PEtabExperimentalCondition}
observables::Vector{PEtabObservable}
meassurments::Vector{PEtabMeassurment}
parameters::Vector{PEtabParameter}
u0::Vector{Pair{Num.Union{Float64,Num}}}
end Then if one wants Also, a second question. If you have e.g. parameter |
Yes, this is how I see the struct. And yes, if we want some u0 to change between conditions it is better if we make that specific u0 value to change a parameter. I think this is a good solution as, at least in my experience, typically only a few u0 values are changed between experimental conditions, so instead of having to provide an u0 per experimental condition, it is probably better to specify the u0:s that change by a parameter value. Now a complication here, which I think you touch on with the second question, is that if an initial value is a a parameter how do we specify that? If it is a parameter that is constant or we want to estimate is should be specified in PEtabParameter. However, if it is a control-parameter (dictating an experimental condition) it should not be specified in PEtabParameter. In the PEtab standard control parameters are not a part of PEtabParameter (as they are control parameters and not yet allowed to be estimated), rather the user must provide a value for the control parameter for each experimental condition. So I am thinking that if a user provide a new parameter as initial value control parameter, then we should enforce the user provides a value on that parameter for each experimental condition. What do you think? |
(I work in a category of systems where the only difference between experimental conditions is that one changes all the non-trivial (non-0) u0 values, however, for the greater good I don't mind going with your solution, seems like it should work out) So, in essence, we have 3 types of parameter values?
Still, I think we have a fully working format here, so maybe we go with this to make something that works, and then we can discuss whenever it makes sense to throw things around a bit later on? |
I see what you mean with 2 and 3, and typically in the PEtab standard 2 does not have to specified explicitly (as often the constant values for these are already set in the SBML file), so if it could be possible to set constant parameter when specifying the reaction system then given that, it would not be needed to specify all constant parameters in Parameters? And I agree, we can go with this, get something that works, and then make potential changes- |
I see what you mean with 2 and 3, and typically in the PEtab standard 2 does not have to specified explicitly (as often the constant values for these are already set in the SBML file), so if it could be possible to set constant parameter when specifying the reaction system then given that, it would not be needed to specify all constant parameters in Parameters? And I agree, we can go with this, get something that works, and then make potential changes. |
Technically it would be possible to parse the provided I will keep things as suggested now, and then we can add this to the list of things to consider once we have a methodology sorted out. |
Does this work? ### Preparations ###
# Fetch required packages.
using Catalyst, OrdinaryDiffEq, Distributions, DataFrames, Plots
# Prepares the model simulation conditions.
rn = @reaction_network begin
(p,d), 0 <--> X
(1,k), X <--> Y
end
u0 = [1.0, 0.0]
tspan = (0.0, 10.0)
p = [1.0, 0.2, 0.5]
### Declares CatalystPEtabModel Structure ###
# The conditiosn for an experiment.
struct PEtabExperimentalCondition
parameter_values::Dict{Num, Float64}
id::String
end
# An observable value.
struct PEtabObservable
obs::Num
id::String
end
# A parameter.
struct PEtabParameter
parameter::Num
estimate::Bool
value::Union{Nothing,Float64}
lb::Union{Nothing,Float64}
ub::Union{Nothing,Float64}
prior::Union{Nothing,Distribution{Univariate, Continuous}}
scale::Union{Nothing,Symbol} # :log10, :linear and :log supported.
end
# A full PEtab model.
struct CatalystPEtabModel
system::ReactionSystem
experimental_conditions::Dict{String,PEtabExperimentalCondition}
observables::Dict{String,PEtabObservable}
meassurments::DataFrame
parameters::Vector{PEtabParameter}
u0::Vector{Pair{Num,Union{Float64,Num}}}
end
### Performs Experiment ###
# Meassurment settings.
meassurment_times = 2.0:2.0:10.0
meassurment_error_dist = Normal(0.0, 0.4)
# Make simulation.
oprob = ODEProblem(rn, u0, tspan, p)
sol = solve(oprob, Tsit5(); tstops=meassurment_times)
# Get meassurment values.
meassured_values_true = [sol[2, findfirst(sol.t .== mt)] for mt in meassurment_times]
meassured_values = meassured_values_true .+ rand(meassurment_error_dist, length(meassured_values_true))
# Plot the experiment an meassurments.
plot(sol; idxs=2)
plot!(meassurment_times, meassured_values; seriestype=:scatter)
### Prepares the PEtab Structure ###
@unpack X, Y, p, d, k = rn
# The system field.
system = rn
# The experimental conditions field.
exp1 = PEtabExperimentalCondition(Dict([X=>1.0, Y=>0.0, d=>0.2, k=>2.0]), "Obs1")
experimental_conditions = Dict(["Exp1" => exp1])
# The observables field.
obs1 = PEtabObservable(Y, "Obs1")
observables = Dict(["Obs1" => obs1])
# The meassurments field
nM = length(meassured_values)
meassurments = DataFrame(exp_id=fill("Exp1", nM), obs_id=fill("Obs1", nM), value=meassured_values, time_point=meassurment_times, noise_parameter=fill(0.4,nM))
# The parameters field.
par_p = PEtabParameter(p, false, nothing, 1e-2, 1e2, nothing, :log10)
par_d = PEtabParameter(d, true, 0.2, nothing, nothing, nothing, nothing)
par_p = PEtabParameter(k, true, 2.0, nothing, nothing, nothing, nothing)
parameters =[par_X, par_Y, par_p, par_d, par_p]
# The initial conditions field.
u0 = [X => 1.0, Y => 0.0]
# Creates the model
petab_model = CatalystPEtabModel(system, experimental_conditions, observables, meassurments, parameters, u0)
### Saves Model to File ###
using Serialization
serialize("petab_model.jls", petab_model) Currently, there is only one parameter to estimate, however, if that works we should be able to make an additional parameter unknown, or something that is carried in experiments. I'm also sending the petab_model.jls file (although the |
Thanks, this looks like it would work. Will try to take a look and come back this or early next week :) |
This turned out to be easier than expected. (Note I made some small alterations to the Structs) include(joinpath(@__DIR__, "Catalyst_functions.jl"))
# Prepares the model simulation conditions.
rn = @reaction_network begin
(p,d), 0 <--> X
(1,k), X <--> Y
end
### Performs Experiment (simulate data) ###
u0 = [1.0, 0.0]
tspan = (0.0, 10.0)
p = [1.0, 0.2, 0.5]
meassurment_times = 2.0:2.0:10.0
meassurment_error_dist = Normal(0.0, 0.4)
oprob = ODEProblem(rn, u0, tspan, p)
sol = solve(oprob, Tsit5(); tstops=meassurment_times)
meassured_values_true = [sol[2, findfirst(sol.t .== mt)] for mt in meassurment_times]
meassured_values = meassured_values_true .+ rand(meassurment_error_dist, length(meassured_values_true))
### Prepares the PEtab Structure ###
@unpack X, Y, p, d, k = rn
# The system field.
system = rn
# The experimental conditions field.
exp1 = PEtabExperimentalCondition(Dict([d=>0.2, k=>2.0]))
experimental_conditions = Dict(["Exp1" => exp1])
# The observables field.
obs1 = PEtabObservable(Y, :lin, 0.4)
observables = Dict(["Obs1" => obs1])
# The meassurments field
nM = length(meassured_values)
meassurments = DataFrame(exp_id=fill("Exp1", nM), obs_id=fill("Obs1", nM), value=meassured_values, time_point=meassurment_times, noise_parameter=fill(0.4,nM))
# The parameters field (we are going to create a nice constructor here)
par_p = PEtabParameter(p, true, nothing, 1e-2, 1e2, nothing, :log10)
par_d = PEtabParameter(d, false, 0.2, nothing, nothing, nothing, nothing)
par_k = PEtabParameter(k, false, 2.0, nothing, nothing, nothing, nothing)
petab_parameters = [par_p, par_d, par_k]
# The initial conditions field.
state_map = [X => 1.0, Y => 0.0]
# Creates the model
petab_model = readPEtabModel(system, experimental_conditions, observables, meassurments,
petab_parameters, state_map, verbose=true)
# Can now easily be made into PEtabODEProblem
petabProblem = createPEtabODEProblem(petab_model)
p = [log10(20)]
f = petabProblem.computeCost(p)
∇f = petabProblem.computeGradient(p)
Δf = petabProblem.computeHessian(p) I added functions to parse the PEtab-input structs into the PEtab-file format (as DataFrames). Given that, I could build a PEtabODEProblem from the reaction system. Overall, with this approach it should also be easy to add additional features such as stady-state conditions, noise parameters etc. (basically recreate the PEtab test-suite). Going forward, in order to create a smooth user experience I think we need to figure out how to specify initial values, and how to set default parameter values (parameters which are constant and are not control-parameters). An example, currently if I want to estimate initial-value I have to do the following when defining the reaction system: rn = @reaction_network begin
@parameters a0 b0 # Initial values to estimate must be parameters
(k1), A --> B
(k2), B --> A
end
@unpack A, B, k1, k2, a0, b0 = rn
state_map = [A => a0, B => b0] While I imagine something like this would be smoother rn = @reaction_network begin
@parameters a0 b0 # Initial values to estimate must be parameters
@species A(t)=a0 B(t)=b0 # Only specify for species having non-zero initial value
(k1), A --> B
(k2), B --> A
end Likewise, instead of setting constant non-control parameter values via PEtabParameter I think it would be nice to set these directly when defining the reaction system. This is how it is done typically for PEtab-model (default parameter values or the initial-value equation is set in the SBML-file). For this to work, however, I need to be able to extract the default values from the reaction system (to properly setup the functions needed by PEtab.jl), is it something that can be done? Further, any more input on this? |
Looks good. Is there somewhere where I can check the updated It is possible to set default values of parameters and initial conditions already: https://docs.sciml.ai/Catalyst/stable/catalyst_functionality/dsl_description/#dsl_description_defaults I'm mixed with using default parameter values within the
|
It should also be noted that some parameter properties can be defined for parameters via metadata: https://docs.sciml.ai/ModelingToolkit/stable/basics/Variable_metadata/#Bounds
For now we might want to ignore these fields and make it work without, and then we can add functionality that infers these properties if they exist. |
Yes, the updated structs can be found here: https://github.com/sebapersson/PEtab.jl/blob/Catalyst_integration/test/Catalyst_functions.jl. (note I removed PEtabCatalystModel as I figured out how to process these structs directly into a PEtabModel) Regarding your points on parameter values I see what you mean. So we have that setting default values in non-ideal for workflows, while setting constant parameters via PEtabParameter is to clunky. Would it then be possible to set values for constant parameters via a parameter-map - like lets say we would want to set a constant value for k1 (and set k2 later via PEtabParameter) rn = @reaction_network begin
@parameters a0 b0 # Initial values to estimate must be parameters
(k1), A --> B
(k2), B --> A
end
@unpack A, B, k1, k2, a0, b0 = rn
state_map = [A => a0, B => b0]
parameter_map [k1 => 3.0] Then I could accept parameter_map when building the PEtab model and take this constant into consideration when building the ODEProblem. Regarding parameter dependent initial values, do you think it is better to use a state-map, or specify the relationship in the reaction system? |
Setting it via a parameter map like that seems good. It is also similar to how it is done with e.g. For the states, I am currently leaning towards defining this in the reaction system. Having initial conditions being defined by parameters is something I can see being useful under other circumstances as well, so seems good to make it a general thing. I'm checking how this should work within the ModelingToolkit framework right now though. If it turns out to be a mess we might have to go with defining it in the state map and it being a feature within PETab only. |
Sounds good, I will then add a parameter-map. I will also code up the PEtab test-cases using Catalyst (too see if there is any useful feature we do not currently cover). |
Setting intial conditions as parameters should work well:
The value can be retrieved via e.g.
or found in
|
Then I say we go with setting initial-values directly in the reaction system, I also managed to this approach working with PEtab.jl and I think it looks nice (below is the first PEtab test-case, note I added constructor for PEtabParameter): #=
Recrating PEtab test-suite for Catalyst integration to robustly test that
we support a wide arrange of features for PEtab integration
=#
using Test
include(joinpath(@__DIR__, "..", "Catalyst_functions.jl"))
# Define reaction network model
rn = @reaction_network begin
@parameters a0 b0
@species A(t)=a0 B(t)=b0
(k1, k2), A <--> B
end
# Measurement data
measurements = DataFrame(exp_id=["c0", "c0"],
obs_id=["obs_a", "obs_a"],
time_point=[0, 10.0],
value=[0.7, 0.1],
noise_parameter=0.5)
# Single experimental condition
experimental_conditions = Dict(["c0" => PEtabExperimentalCondition(Dict())])
# PEtab-parameter to "estimate"
petab_parameters = [PEtabParameter(:a0, value=1.0, scale=:lin),
PEtabParameter(:b0, value=0.0, scale=:lin),
PEtabParameter(:k1, value=0.8, scale=:lin),
PEtabParameter(:k2, value=0.6, scale=:lin)]
# Observable equation
@unpack A = rn
observables = Dict(["obs_a" => PEtabObservable(A, :lin, 0.5)])
# Create a PEtabODEProblem
petab_model = readPEtabModel(rn, experimental_conditions, observables, measurements,
petab_parameters, verbose=true)
petab_problem = createPEtabODEProblem(petab_model)
# Compute negative log-likelihood
nll = petab_problem.computeCost(petab_problem.θ_nominalT)
@test nll ≈ 0.84750169713188 atol=1e-3 Overall, integrating PEtab with Catalyst seem to work nicely. Next I could expand the importer and structs to cover additional features (e.g. steady-state simulations) by recreating the PEtab test-suite, and after that I propose we see if we should change some of the structs? (I am currently not entirely happy with how we specify measurement data, feels a bit clunky). |
Looks good! Quick questions:
|
Good questions.
|
Sounds good. I am still a bit uncertain about (1) through. If With regard to (2). Yes, it seems that when we are done we might want to have a series of checks to ensure nothing is missing. Your suggested approach here seems good (provide the noise formula in the observables, and any quantity here that carries between measurements is given as noise parameters there. Right now, the number ( (3) To be honest, linear and log-normally distributed noise will probably cover almost all cases, and I cannot directly say there is another type of noise I'd want right now. I think it is more a question if we want to future-proof us so that the approach can work with other noise distributions when/if they get added. Would it make sense to add a field to |
|
Excellent, yes, putting such a note about other distributions sounds like the way to go. Didn't even realise that there was customized code for normal distributions. If we do not want to let initial conditions be parameters that are estimated, we can provide their values just using a map when we run |
Yes, providing the the constant initial values like a map sounds like a good plan. And in the case the user does not provide anything for initial value I say we assume it is zero. |
Assuming 0 if missing sounds good. I think the pieces are falling into place? I will start drafting documentation for Catalyst. Once there is a version of the interface working in PEtab.jl I might be able to help with the coding there to cover corner cases and similar stuff. |
I agree the pieces are falling into place. I will work through the PEtab test-cases (to make sure there is nothing we miss in term of support), and add things like steady-state simulations. Have some other things this week, but should be done with this latest by the end of the week. |
I see. Most optimization packages take a Vector though, e.g. Optmisation.jl as here https://docs.sciml.ai/Optimization/stable/examples/rosenbrock/ which I think is the most scalable solution if there are many (>30) parameters to estimate. But for smaller problems I do like the syntax with the parameter map better. So I think the best solution is to allow both - that if a vector is provided then in calibrateModel function the parameter map is parsed into a Vector{Float64}. Would be grateful if you could help with the implementation |
I agree. if you make the version that acts on |
I have now merged the Catalyst integration with main, and following #69 I have also changed the naming to better follow the Julia code-standard, as can be seen in the updated docs https://sebapersson.github.io/PEtab.jl/dev/Define_in_julia/. Next release will be breaking (as naming changes), so before that if you (@TorkelE ) have any feedback on naming etc please tell. The only thing left now is |
Still traveling, but will be back tomorrow (Sunday) at which point I will make some additional tutorials, should hopefully be done quite quickly. |
I'm going through things, and only really have two thoughts: Single simulation experimentThere are a decent number of cases when there is only really is a single experiment (e.g. modelling epidemics, where each epidemic is distinct and only happens once). Would it make sense to create a dispatch for ObservablesI'd say in the vast majority of cases, the observable is a species of the system. In these cases, it would be pleasant to be able to designate this without having to explicitly rn = @reaction_network begin
(p, d), 0 <--> X
end where
would be useful. When rn = @reaction_network begin
(k1,k2), X1 <--> X2
end where we now would run
my goal is that observables should instead be incorporated into the reaction system: rn = @reaction_network begin
@observed Xtot = X1+X2 # Not actually implemented yet, the .observed field exist though, but cannot be filled this way).
(k1,k2), X1 <--> X2
end and one could run
However, I the current approach is more general. For a starter, it does give the flexibility of being able to define stuff like I'm not sure what is best. I can imagine that it is a bit messy to support both cases. If there is an elegant solution I think it would be worth it, however, depends on what you think is best. |
It can also be noted that Catalyst exports ModelingToolkit, so |
Thanks for the input! Simulation conditionI agree, and I will write a method where I create a ObservableHmm, this one is a bit tricky. As a starting point, I think we need to keep the current system. Firstly, because of the noise-formula. Secondly, because it makes it possible to for example define noise and observable parameters as here while allowing me to clearly keeps track on which parameters are a part of the dynamic model. This is important, as computing the gradient for parameter not in the dynamic system is substantially faster (so tracking these parameter is important). I also like the approach with observable in the obs_X = PEtabObservable(:X, 0.1) as I am afraid it gets a bit messy if there are too many ways to define the observable, I think either via reaction-system and in advanced cases as currently should be sufficient? LastlyI loaded ModellingToolkit just to highlight it has to be loaded if the user wants to give the model as ODESystem (and not Catalyst) :) |
All sounds excellent! |
I had a thought (and I am currently throwing out most thoughts I have, so you can do what you want with them). Currently, using this interface, parameters can be given in 3 different ways:
Currently, if a parameter is not given in any of the three inputs, (1) is the default. Would it make sense to force the user to provide each parameter in each input? It is basically a trade-off, it is nice not to have to define all the parameters that one wants to fit. However, sometimes the user might forget some parameter somewhere by mistake, and then throwing an error might be good. This problem is probably more pronounced in the new interface where option (3) was added. An intermediate solution is to kwarg ( |
Also, another thing. I've talked to people, and apparently when representing ModelingToolkit type equation expressions (e.g. Sorry for not catching this earlier! I am not sure how much it is going to matter, but it might be worth considering making this change befor the breaking release. I tried to check where struct PEtabObservable
obs::Num
transformation::Union{Nothing, Symbol}
noise_formula::Union{Nothing, Num}
end
function PEtabObservable(obs::Num,
noise_formula::Union{Nothing, Num, T};
transformation::Symbol=:lin)::PEtabObservable where T<:Real
return PEtabObservable(obs, transformation, noise_formula)
end Where I think both The |
Thanks for the input! I will change to Any! I see what you mean with the parameter being forgotten which easily can happen. How about throwing a warning if a parameter is forgotten ? Or do you think users might ignore the warning and only act in case an error is thrown? |
From a user's point of view, I think a warning should be good. |
The Catalyst/PEtab tutorial at Catalyst docs is up now: SciML/Catalyst.jl#695 It's basically a focused version of the PEtab one, just focusing on options for fitting a Catalyst ODE to data. Will probably be a bit until it gets merged. |
This is great! I will take a look at it later. Also, regarding |
I'm having a 3 hour workshop on CRN modelling with Catalyst at https://icsb2023.bioscience-ct.net/ on October 8th as well. Ideally, I would like yo include a short section on parameter fitting with PEtab.jl, just to show a very basic workflow. |
Congrats to getting a workshop at ICSB! And if you want to include a part on PEtab.jl I would be happy to help with creating material - and since most things are in place now to do parameter estimation with Catalyst before 8:th October I can make next PEtab.jl release before then. |
Great! Yes, I'm making a push for quite a couple of things in Catalyst now as well, aiming to have as much as possible in place for the workshop. I plan to sketch out the workshop this weekend, for the PEtab stuff I will probably make an example I want to use, and then send it to you (if that's ok) for checking/modifications. |
Sounds good! And no problem, when you have an example send it and I will be happy to provide feedback. |
Happy to inform there is now a struct PEtabEvent
condition
affect
target
end Basically condition species the trigger, affect the affect of the trigger, and target what is affected. For example: PEtabEvent(3.0, S + 3, S) means that at time PEtabEvent(S > 3.0, 3, :c1) Means that when state S crosses (from below 3) the limit 3 parameter The target can be states or model parameters, and affect can also be a parameter (e.g. a control parameter). Under the hood If you want any additional feature just tell, I still have to add the docs for this (but it is tested). The thing lacking is a dosage time, this is more complicated to pull off so it will have to wait a bit. |
Sounds excellent, should cover all cases I'm familiar with |
You can now also find event documentation (https://sebapersson.github.io/PEtab.jl/dev/Julia_event/), will add plots in the future, but now it currently holds all the information needed to make events. |
Also, did you see my comment on changing e.g. It should be fine not to append PEtab to all extension names, but just wanted to double-check in case you missed it (since if you want to add it, it probably should be done before 2.0). (if you did see the comment, just ignore this!) |
Thank you for reminding me about this, totally slipped my mind. Will address it |
I would say this issue has been addressed as PEtab.jl now integrates with Catalyst, and there is event support. There are some things left like;
But these should likely be separate issues. If you do not have anything more that should be added @TorkelE I will close this and #77 , and then release next version |
Yes, I consider this done with extra on top. |
Me and @sebapersson talked today about linking PEtab.jl and Catalyst.jl (https://github.com/SciML/Catalyst.jl) together.
The idea is that someone who has defined a Catalyst.jl reaction system, should be able to define the information required by PEtab.jl to create optimization problems for fitting cost functions. Basically, I would want to create an intermediary
struct
,PEtabModel
(made-up name), so that one can runThe hope is that someone who is using Catalyst.jl should be able to circumvent using files to utilise PEtab entirely. I'm really excited about this. Hopefully, we will be able to create really smooth workflows for parameter optimization of CRNs using Julia.
After our discussion, we decided that I should create a proposed structure that holds the information typically stored in the PEtab.jl files. We can then discuss how this one can be improved to better accommodate what PEtab.jl needs. Ultimately, I think having some form of
struct PEtabModel
will be useful. the equation is whenever we should:readPEtabModel
create this structure from the file.Right now this is maybe not super important, once we have a fully working workflow changing to a preferred approach should be relatively simple.
Currently, I am thinking of creating something like this:
I think that it should be easy to, within Catalyst, create something like this and send to PEtab. What would be the ideal way for PEtab to receive it in though? If you have a format that you think you could work in, and ensure that that works as input to
createPEtabODEProblem
, then I can make a link to that, and we should be able to put an example together.The text was updated successfully, but these errors were encountered: