-
-
Notifications
You must be signed in to change notification settings - Fork 78
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
Refactor "srcd/sl.jl" file #985
base: master
Are you sure you want to change the base?
Conversation
@@ -40,6 +40,7 @@ import ModelingToolkit: check_variables, | |||
|
|||
import Base: (==), hash, size, getindex, setindex, isless, Sort.defalg, length, show | |||
import MacroTools, Graphs | |||
using MacroTools: striplines |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function is used frequenty, and even more frequently when debugging code. Figured it made sense to import directly.
@@ -86,7 +86,7 @@ function make_compound(expr) | |||
# Cannot extract directly using e.g. "getfield.(composition, :reactant)" because then | |||
# we get something like :([:C, :O]), rather than :([C, O]). | |||
composition = Catalyst.recursive_find_reactants!(expr.args[3], 1, | |||
Vector{ReactantStruct}(undef, 0)) | |||
Vector{ReactantInternal}(undef, 0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ReactantInternal
and ReactionInternal
felt like way better name. Especially now with the Reaction
struct, having a struct called ReactionStruct
seemed like it could confuse new users. Since it is used internally only (during the creation of models in the DSL), I figured the name could reflect that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about DSLReactant and DSLReaction? That makes clear their intended use case.
src/expression_utils.jl
Outdated
if !isempty(intersect(forbidden_symbols_error, sym)) | ||
used_forbidden_syms = intersect(forbidden_symbols_error, sym) | ||
error("The following symbol(s) are used as species or parameters: $used_forbidden_syms, this is not permitted.") | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just rewritten nicer than what I could do things before (e.g. before I knew about interpolating into strings). No need to have unnecessarily convoluted code.
3f92a31
to
b6ba044
Compare
variables = vcat(variables_declared, vars_extracted) | ||
|
||
# handle independent variables |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Part of the organisations so that options are handled together (in two sets, some before and some after the reactions are considered, depending on whichever is possible for which option)
quote | ||
$ps | ||
$psexpr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just renamed so that everything which is an expression for the code that generates something ends with expr (and not just some of it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should put this after the declaration of ivs
as time-dependent parameters are now a thing in MTK that we should ultimately support (if it doesn't already work for us).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want to just include the changes of #1024 by hand here? Then we can close that. Probably easiest way to handle this.
symvec = gensym() | ||
ex = quote | ||
$symvec = $ex | ||
expr = quote |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in some places we used ex and some expr, here I just made it uniform
isequivalent(rs_empty_1, rs_empty) | ||
rs_empty_2 == rs_empty | ||
rs_empty_3 == rs_empty | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WHen I remade how the macor inputs are handled at the top level (nothing, name, reactions, name + reactions) I wanted to add some tests that all of this works for all cases and both macros.
@@ -212,8 +212,8 @@ end | |||
|
|||
# Checks that repeated metadata throws errors. | |||
let | |||
@test_throws LoadError @eval @reaction k, 0 --> X, [md1=1.0, md1=2.0] | |||
@test_throws LoadError @eval @reaction_network begin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was never supposed to be a LoadError
in the first place. There was a variable in the error
statement that was undeclared, so that yielded an LoadError
, which was not the true error.
@@ -131,7 +131,7 @@ let | |||
end | |||
# Line number nodes aren't ignored so have to be manually removed | |||
Base.remove_linenums!(ex) | |||
@test eval(Catalyst.make_reaction_system(ex)) isa ReactionSystem | |||
@test eval(Catalyst.make_reaction_system(ex, QuoteNode(:name))) isa ReactionSystem |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adapted to the new make_reaction_system
function (which won't generate names for the user)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For future reference, please try not to reorder functions while also modifying them. It would be a lot easier to look at the diff if you had first rewritten them in this PR, and then in a followup PR reordered.
(Obviously for a change of like 50 lines this can be ignored, but when there are hundreds of lines of diff showing it makes the review a lot longer when things get moved around too.)
@@ -86,7 +86,7 @@ function make_compound(expr) | |||
# Cannot extract directly using e.g. "getfield.(composition, :reactant)" because then | |||
# we get something like :([:C, :O]), rather than :([C, O]). | |||
composition = Catalyst.recursive_find_reactants!(expr.args[3], 1, | |||
Vector{ReactantStruct}(undef, 0)) | |||
Vector{ReactantInternal}(undef, 0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about DSLReactant and DSLReaction? That makes clear their intended use case.
isempty(intersect(forbidden_symbols_error, sym)) && return | ||
used_forbidden_syms = intersect(forbidden_symbols_error, sym) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isempty(intersect(forbidden_symbols_error, sym)) && return | |
used_forbidden_syms = intersect(forbidden_symbols_error, sym) | |
used_forbidden_syms = intersect(forbidden_symbols_error, sym) | |
isempty(used_forbidden_syms) && return |
isempty(intersect(forbidden_variables_error, sym)) && return | ||
used_forbidden_syms = intersect(forbidden_variables_error, sym) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isempty(intersect(forbidden_variables_error, sym)) && return | |
used_forbidden_syms = intersect(forbidden_variables_error, sym) | |
used_forbidden_syms = intersect(forbidden_variables_error, sym) | |
isempty(used_forbidden_syms) && return |
allunique(syms) && return | ||
error("Reaction network independent variables, parameters, species, and variables must all have distinct names, but a duplicate has been detected. ") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to do this here that is fine, but generally let's try to avoid rewriting code that uses ||
in this way if this is simply a style preference of yours.
quote | ||
$ps | ||
$psexpr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should put this after the declaration of ivs
as time-dependent parameters are now a thing in MTK that we should ultimately support (if it doesn't already work for us).
if !isempty(ivs) | ||
error("An observable ($obs_name) was given independent variable(s). These should not be given, as they are inferred automatically.") | ||
isnothing(defaults) || | ||
end | ||
if !isnothing(defaults) | ||
error("An observable ($obs_name) was given a default value. This is forbidden.") | ||
(obs_name in forbidden_symbols_error) && | ||
end | ||
if in(obs_name, forbidden_symbols_error) | ||
error("A forbidden symbol ($(obs_eq.args[2])) was used as an observable name.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comments above about dropping &&
and ||
.
if (obs_name in species_n_vars_declared) && is_escaped_expr(obs_eq.args[2]) | ||
println("HERE") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
println("HERE") |
push!(observed_vars.args, | ||
ivs_get_expr = :(unique(reduce(vcat, | ||
[arguments(MT.unwrap(dep)) for dep in $dep_var_expr]))) | ||
sort_func(iv) = findfirst(MT.getname(iv) == ivs for ivs in ivs_sorted) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sort_func(iv) = findfirst(MT.getname(iv) == ivs for ivs in ivs_sorted) | |
sort_func(iv) = findfirst(==(MT.getname(iv)), ivs_sorted) |
macro (but permiting only a single reaction). A more detailed introduction to the syntax can | ||
be found in the description of `@reaction_network`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reference documentation for more detailed description instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More generally, I'd suggest following the approach I mentioned for @reaction_network
aiming this as a reference on options for users who are looking things up, and referring users to the docs for tutorials / introductions. (Along with keeping some example usage.)
$pexprs | ||
$iv |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$pexprs | |
$iv | |
$iv | |
$pexprs |
per the other PR we should probably set the iv
first.
Also does the "src/expression_utils.jl" file, as that primarily support the dsl file (the other relevant file is the chemistry one).
Primarily four types of changes:
@reaction_network
and@network_component
macros reuse minimal amount of code (where as previously it the second was basically ctrl+copied versions of the first.I also add some tests of all the various ways to create models via the DSL, mostly stuff that was not tested before.