Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into update_smol_tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
isaacsas committed Jun 7, 2024
2 parents 7ec86bb + 670c5be commit 1c56c60
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 78 deletions.
6 changes: 6 additions & 0 deletions src/reaction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ end
function Reaction(rate, subs, prods, substoich, prodstoich;
netstoich = nothing, metadata = Pair{Symbol, Any}[],
only_use_rate = metadata_only_use_rate_check(metadata), kwargs...)
# Handles empty/nothing vectors.
isnothing(subs) || isempty(subs) && (subs = nothing)
isnothing(prods) || isempty(prods) && (prods = nothing)
isnothing(substoich) || isempty(substoich) && (substoich = nothing)
isnothing(prodstoich) || isempty(prodstoich) && (prodstoich = nothing)

(isnothing(prods) && isnothing(subs)) &&
throw(ArgumentError("A reaction requires a non-nothing substrate or product vector."))
(isnothing(prodstoich) && isnothing(substoich)) &&
Expand Down
23 changes: 12 additions & 11 deletions src/reactionsystem_serialisation/serialisation_support.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function push_field(file_text::String, rn::ReactionSystem, annotate::Bool, top_l
has_component, get_comp_string, get_comp_annotation = comp_funcs
has_component(rn) || (return (file_text, false))

# Prepares the text creating the field. For non-top level systems, adds `local `. Observables
# Prepares the text creating the field. For non-top level systems, add `local `. Observables
# must be handled differently (as the declaration is not at the beginning of the code for these).
# The independent variables is not declared as a variable, and also should not have a `1ocal `.
write_string = get_comp_string(rn)
Expand All @@ -54,7 +54,7 @@ function push_field(file_text::String, rn::ReactionSystem, annotate::Bool, top_l
return (file_text * write_string, true)
end

# Generic function for creating an string for an unsupported argument.
# Generic function for creating an string for a unsupported argument.
function get_unsupported_comp_string(component::String)
@warn "Writing ReactionSystem models with $(component) is currently not supported. This field is not written to the file."
return ""
Expand Down Expand Up @@ -82,9 +82,10 @@ function syms_2_strings(syms)
return get_substring_end("$(convert(Vector{Any}, strip_called_syms))", 4, 0)
end

# Converts a vector of symbolics (e.g. the species or parameter vectors) to a string corresponding to
# the code required to declare them (potential @parameters or @species commands must still be added).
# The `multiline_format` option formats it with a `begin ... end` block and declarations on separate lines.
# Converts a vector of symbolic variables (e.g. the species or parameter vectors) to a string
# corresponding to the code required to declare them (potential @parameters or @species commands
# must still be added). The `multiline_format` option formats it with a `begin ... end` block
# and declarations on separate lines.
function syms_2_declaration_string(syms; multiline_format = false)
decs_string = (multiline_format ? " begin" : "")
for sym in syms
Expand All @@ -102,7 +103,7 @@ function sym_2_declaration_string(sym; multiline_format = false)
# Creates the basic symbol. The `"$(sym)"` ensures that we get e.g. "X(t)" and not "X".
dec_string = "$(sym)"

# If the symbol have a non-default type, appends the declaration of this.
# If the symbol has a non-default type, appends the declaration of this.
# Assumes that the type is on the form `SymbolicUtils.BasicSymbolic{X}`. Contain error checks
# to ensure that this is the case.
if !(sym isa SymbolicUtils.BasicSymbolic{Real})
Expand Down Expand Up @@ -136,7 +137,7 @@ end

# Converts a generic value to a String. Handles each type of value separately. Unsupported values might
# not necessarily generate valid code, and hence throw errors. Primarily used to write default values
# and metadata values (which hopefully almost exclusively) has simple, supported, types. Ideally,
# and metadata values (which hopefully almost exclusively) have simple, supported, types. Ideally,
# more supported types can be added here.
x_2_string(x::Num) = expression_2_string(x)
x_2_string(x::SymbolicUtils.BasicSymbolic{<:Real}) = expression_2_string(x)
Expand Down Expand Up @@ -261,7 +262,7 @@ function get_dep_syms(sym)
return Symbolics.get_variables(ModelingToolkit.getdefault(sym))
end

# Checks if a symbolic depends on an symbolics in a vector being declared.
# Checks if a symbolic depends on a symbolics in a vector being declared.
# Because Symbolics has to utilise `isequal`, the `isdisjoint` function cannot be used.
function depends_on(sym, syms)
dep_syms = get_dep_syms(sym)
Expand All @@ -275,8 +276,8 @@ end

# For a set of remaining parameters/species/variables (remaining_syms), return this split into
# two sets:
# One with those that does not depend on any sym in `all_remaining_syms`.
# One with those that does depend on at least one sym in `all_remaining_syms`.
# One with those that do not depend on any sym in `all_remaining_syms`.
# One with those that do depend on at least one sym in `all_remaining_syms`.
# The first set is returned. Next `remaining_syms` is updated to be the second set.
function dependency_split!(remaining_syms, all_remaining_syms)
writable_syms = filter(sym -> !depends_on(sym, all_remaining_syms), remaining_syms)
Expand All @@ -289,7 +290,7 @@ end


# Checks if a symbolic's declaration is "complicated". The declaration is considered complicated
# if it have metadata, default value, or type designation that must be declared.
# if it has metadata, default value, or type designation that must be declared.
function complicated_declaration(sym)
isempty(get_metadata_to_declare(sym)) || (return true)
ModelingToolkit.hasdefault(sym) && (return true)
Expand Down
98 changes: 49 additions & 49 deletions src/reactionsystem_serialisation/serialise_fields.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
### Handles Independent Variables ###

# Checks if the reaction system have any independent variable. True for all valid reaction systems.
function has_iv(rn::ReactionSystem)
# Checks if the reaction system has any independent variable. True for all valid reaction systems.
function seri_has_iv(rn::ReactionSystem)
return true
end

Expand All @@ -17,13 +17,13 @@ function get_iv_annotation(rn::ReactionSystem)
end

# Combines the 3 independent variable-related functions in a constant tuple.
IV_FS = (has_iv, get_iv_string, get_iv_annotation)
IV_FS = (seri_has_iv, get_iv_string, get_iv_annotation)


### Handles Spatial Independent Variables ###

# Checks if the reaction system have any spatial independent variables.
function has_sivs(rn::ReactionSystem)
# Checks if the reaction system has any spatial independent variables.
function seri_has_sivs(rn::ReactionSystem)
return !isempty(get_sivs(rn))
end

Expand All @@ -38,23 +38,23 @@ function get_sivs_annotation(rn::ReactionSystem)
end

# Combines the 3 independent variables-related functions in a constant tuple.
SIVS_FS = (has_sivs, get_sivs_string, get_sivs_annotation)
SIVS_FS = (seri_has_sivs, get_sivs_string, get_sivs_annotation)


### Handles Species, Variables, and Parameters ###

# Function which handles the addition of species, variable, and parameter declarations to the file
# text. These must be handled as a unity in case there are default value dependencies between these.
function handle_us_n_ps(file_text::String, rn::ReactionSystem, annotate::Bool, top_level::Bool)
# Fetches the systems parameters, species, and variables. Computes the `has_` `Bool`s.
# Fetches the system's parameters, species, and variables. Computes the `has_` `Bool`s.
ps_all = get_ps(rn)
sps_all = get_species(rn)
vars_all = filter(!isspecies, get_unknowns(rn))
has_ps = has_parameters(rn)
has_sps = has_species(rn)
has_vars = has_variables(rn)
has_ps = seri_has_parameters(rn)
has_sps = seri_has_species(rn)
has_vars = seri_has_variables(rn)

# Checks which sets have dependencies which requires managing.
# Checks which sets have dependencies which require managing.
p_deps = any(depends_on(p, [ps_all; sps_all; vars_all]) for p in ps_all)
sp_deps = any(depends_on(sp, [sps_all; vars_all]) for sp in sps_all)
var_deps = any(depends_on(var, vars_all) for var in vars_all)
Expand Down Expand Up @@ -93,7 +93,7 @@ function handle_us_n_ps(file_text::String, rn::ReactionSystem, annotate::Bool, t

# Pre-declares the sets with written/remaining parameters/species/variables.
# Whenever all/none are written depends on whether there were any initial dependencies.
# `deepcopy` is required as these gets mutated by `dependency_split!`.
# `deepcopy` is required as these get mutated by `dependency_split!`.
remaining_ps = (p_deps ? deepcopy(ps_all) : [])
remaining_sps = (sp_deps ? deepcopy(sps_all) : [])
remaining_vars = (var_deps ? deepcopy(vars_all) : [])
Expand All @@ -113,7 +113,7 @@ function handle_us_n_ps(file_text::String, rn::ReactionSystem, annotate::Bool, t
isempty(writable_vars) || @string_append! us_n_ps_string get_variables_string(writable_vars) "\n"
end

# For parameters, species, and/or variables with dependencies, creates final vectors.
# For parameters, species, and/or variables with dependencies, create final vectors.
p_deps && (@string_append! us_n_ps_string "ps = " syms_2_strings(ps_all) "\n")
sp_deps && (@string_append! us_n_ps_string "sps = " syms_2_strings(sps_all) "\n")
var_deps && (@string_append! us_n_ps_string "vars = " syms_2_strings(vars_all) "\n")
Expand All @@ -127,17 +127,17 @@ function handle_us_n_ps(file_text::String, rn::ReactionSystem, annotate::Bool, t
us_n_ps_string = replace(us_n_ps_string, "\nvars = " => "\nlocal vars = ")
end

# Merges the file text with `us_n_ps_string` and return the final outputs.
# Merges the file text with `us_n_ps_string` and returns the final outputs.
return file_text * us_n_ps_string, has_ps, has_sps, has_vars
end


### Handles Parameters ###
# Unlike most other fields, there are not called via `push_field`, but rather via `handle_us_n_ps`.
# Unlike most other fields, these are not called via `push_field`, but rather via `handle_us_n_ps`.
# Hence they work slightly differently.

# Checks if the reaction system have any parameters.
function has_parameters(rn::ReactionSystem)
# Checks if the reaction system has any parameters.
function seri_has_parameters(rn::ReactionSystem)
return !isempty(get_ps(rn))
end

Expand All @@ -156,11 +156,11 @@ end


### Handles Species ###
# Unlike most other fields, there are not called via `push_field`, but rather via `handle_us_n_ps`.
# Unlike most other fields, these are not called via `push_field`, but rather via `handle_us_n_ps`.
# Hence they work slightly differently.

# Checks if the reaction system have any species.
function has_species(rn::ReactionSystem)
# Checks if the reaction system has any species.
function seri_has_species(rn::ReactionSystem)
return !isempty(get_species(rn))
end

Expand All @@ -179,11 +179,11 @@ end


### Handles Variables ###
# Unlike most other fields, there are not called via `push_field`, but rather via `handle_us_n_ps`.
# Unlike most other fields, these are not called via `push_field`, but rather via `handle_us_n_ps`.
# Hence they work slightly differently.

# Checks if the reaction system have any variables.
function has_variables(rn::ReactionSystem)
# Checks if the reaction system has any variables.
function seri_has_variables(rn::ReactionSystem)
return length(get_unknowns(rn)) > length(get_species(rn))
end

Expand All @@ -201,13 +201,13 @@ function get_variables_annotation(rn::ReactionSystem)
end

# Combines the 3 variables-related functions in a constant tuple.
VARIABLES_FS = (has_variables, get_variables_string, get_variables_annotation)
VARIABLES_FS = (seri_has_variables, get_variables_string, get_variables_annotation)


### Handles Reactions ###

# Checks if the reaction system have any reactions.
function has_reactions(rn::ReactionSystem)
# Checks if the reaction system has any reactions.
function seri_has_reactions(rn::ReactionSystem)
return length(reactions(rn)) != 0
end

Expand Down Expand Up @@ -265,14 +265,14 @@ function get_reactions_annotation(rn::ReactionSystem)
return "Reactions:"
end

# Combines the 3 reactions-related functions in a constant tuple.
REACTIONS_FS = (has_reactions, get_reactions_string, get_reactions_annotation)
# Combines the 3 reaction-related functions in a constant tuple.
REACTIONS_FS = (seri_has_reactions, get_reactions_string, get_reactions_annotation)


### Handles Equations ###

# Checks if the reaction system have any equations.
function has_equations(rn::ReactionSystem)
# Checks if the reaction system has any equations.
function seri_has_equations(rn::ReactionSystem)
return length(get_eqs(rn)) > length(get_rxs(rn))
end

Expand Down Expand Up @@ -302,13 +302,13 @@ function get_equations_annotation(rn::ReactionSystem)
end

# Combines the 3 equations-related functions in a constant tuple.
EQUATIONS_FS = (has_equations, get_equations_string, get_equations_annotation)
EQUATIONS_FS = (seri_has_equations, get_equations_string, get_equations_annotation)


### Handles Observables ###

# Checks if the reaction system have any observables.
function has_observed(rn::ReactionSystem)
# Checks if the reaction system has any observables.
function seri_has_observed(rn::ReactionSystem)
return !isempty(observed(rn))
end

Expand Down Expand Up @@ -353,13 +353,13 @@ function get_observed_annotation(rn::ReactionSystem)
end

# Combines the 3 -related functions in a constant tuple.
OBSERVED_FS = (has_observed, get_observed_string, get_observed_annotation)
OBSERVED_FS = (seri_has_observed, get_observed_string, get_observed_annotation)


### Handles Continuous Events ###

# Checks if the reaction system have any continuous events.
function has_continuous_events(rn::ReactionSystem)
# Checks if the reaction system have has continuous events.
function seri_has_continuous_events(rn::ReactionSystem)
return !isempty(MT.get_continuous_events(rn))
end

Expand Down Expand Up @@ -410,13 +410,13 @@ function get_continuous_events_annotation(rn::ReactionSystem)
end

# Combines the 3 -related functions in a constant tuple.
CONTINUOUS_EVENTS_FS = (has_continuous_events, get_continuous_events_string, get_continuous_events_annotation)
CONTINUOUS_EVENTS_FS = (seri_has_continuous_events, get_continuous_events_string, get_continuous_events_annotation)


### Handles Discrete Events ###

# Checks if the reaction system have any discrete events.
function has_discrete_events(rn::ReactionSystem)
# Checks if the reaction system has any discrete events.
function seri_has_discrete_events(rn::ReactionSystem)
return !isempty(MT.get_discrete_events(rn))
end

Expand Down Expand Up @@ -466,17 +466,17 @@ function get_discrete_events_annotation(rn::ReactionSystem)
end

# Combines the 3 -related functions in a constant tuple.
DISCRETE_EVENTS_FS = (has_discrete_events, get_discrete_events_string, get_discrete_events_annotation)
DISCRETE_EVENTS_FS = (seri_has_discrete_events, get_discrete_events_string, get_discrete_events_annotation)


### Handles Systems ###

# Specific `push_field` function, which is used for the system field (where the annotation option
# must be passed to the `get_component_string` function). Since non-ReactionSystem systems cannot be
# written to file, this functions throws an error if any such systems are encountered.
# written to file, this function throws an error if any such systems are encountered.
function push_systems_field(file_text::String, rn::ReactionSystem, annotate::Bool, top_level::Bool)
# Checks whther there are any subsystems, and if these are ReactionSystems.
has_systems(rn) || (return (file_text, false))
# Checks whether there are any subsystems, and if these are ReactionSystems.
seri_has_systems(rn) || (return (file_text, false))
if any(!(system isa ReactionSystem) for system in MT.get_systems(rn))
error("Tries to write a ReactionSystem to file which have non-ReactionSystem subs-systems. This is currently not possible.")
end
Expand All @@ -489,8 +489,8 @@ function push_systems_field(file_text::String, rn::ReactionSystem, annotate::Boo
return (file_text * write_string, true)
end

# Checks if the reaction system have any systems.
function has_systems(rn::ReactionSystem)
# Checks if the reaction system has any systems.
function seri_has_systems(rn::ReactionSystem)
return !isempty(MT.get_systems(rn))
end

Expand Down Expand Up @@ -519,13 +519,13 @@ function get_systems_annotation(rn::ReactionSystem)
end

# Combines the 3 systems-related functions in a constant tuple.
SYSTEMS_FS = (has_systems, get_systems_string, get_systems_annotation)
SYSTEMS_FS = (seri_has_systems, get_systems_string, get_systems_annotation)


### Handles Connection Types ###

# Checks if the reaction system have any connection types.
function has_connection_type(rn::ReactionSystem)
# Checks if the reaction system has any connection types.
function seri_has_connection_type(rn::ReactionSystem)
return false
end

Expand All @@ -540,4 +540,4 @@ function get_connection_type_annotation(rn::ReactionSystem)
end

# Combines the 3 connection types-related functions in a constant tuple.
CONNECTION_TYPE_FS = (has_connection_type, get_connection_type_string, get_connection_type_annotation)
CONNECTION_TYPE_FS = (seri_has_connection_type, get_connection_type_string, get_connection_type_annotation)
10 changes: 5 additions & 5 deletions src/reactionsystem_serialisation/serialise_reactionsystem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ function get_full_system_string(rn::ReactionSystem, annotate::Bool, top_level::B
file_text = ""

# Goes through each type of system component, potentially adding it to the string.
# Species, variables, and parameters must be handled differently in case there is default-values
# Species, variables, and parameters must be handled differently in case there are default values
# dependencies between them.
# Systems uses custom `push_field` function as these requires the annotation `Bool`to be passed
# Systems use custom `push_field` function as these require the annotation `Bool`to be passed
# to the function that creates the next sub-system declarations.
file_text, _ = push_field(file_text, rn, annotate, top_level, IV_FS)
file_text, has_sivs = push_field(file_text, rn, annotate, top_level, SIVS_FS)
Expand All @@ -68,8 +68,8 @@ function get_full_system_string(rn::ReactionSystem, annotate::Bool, top_level::B
file_text, has_systems = push_systems_field(file_text, rn, annotate, top_level)
file_text, has_connection_type = push_field(file_text, rn, annotate, top_level, CONNECTION_TYPE_FS)

# Finalises the system. Creates the final `ReactionSystem` call.
# Enclose everything ing a `let ... end` block.
# Finalise the system. Creates the final `ReactionSystem` call.
# Enclose everything in a `let ... end` block.
rs_creation_code = make_reaction_system_call(rn, annotate, top_level, has_sivs, has_species,
has_variables, has_parameters, has_reactions,
has_equations, has_observed, has_continuous_events,
Expand All @@ -82,7 +82,7 @@ function get_full_system_string(rn::ReactionSystem, annotate::Bool, top_level::B
end

# Creates a ReactionSystem call for creating the model. Adds all the correct inputs to it. The input
# `has_` `Bool`s described which inputs are used. If model is `complete`, this is handled here.
# `has_` `Bool`s described which inputs are used. If the model is `complete`, this is handled here.
function make_reaction_system_call(rs::ReactionSystem, annotate, top_level, has_sivs, has_species,
has_variables, has_parameters, has_reactions, has_equations,
has_observed, has_continuous_events, has_discrete_events,
Expand Down
Loading

0 comments on commit 1c56c60

Please sign in to comment.