Skip to content
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

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open

Conversation

TorkelE
Copy link
Member

@TorkelE TorkelE commented Jul 13, 2024

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:

  • Added comment to code which did not have (primarily older code).
  • Rewrite some old code (a lot of stuff in the dsl file is stuff I wrote over 6 years ago, which simply can be rewritten better now).
  • Do some organisations for where the options are handled in the main function. Been meaning to do this pretty much since I introduced them, and now when we have all *for the foreseeable future) it seemed like a good time to do it. Now it should also be easier to see where to add new options in the future.
  • Rewrite so that the @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.

@@ -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
Copy link
Member Author

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))
Copy link
Member Author

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.

Copy link
Member

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.

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
Copy link
Member Author

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.

@TorkelE TorkelE changed the title Refactor "srcd/sl.jl" file [wip] Refactor "srcd/sl.jl" file Jul 13, 2024
@TorkelE TorkelE force-pushed the src___refactoring___dsl_file branch from 3f92a31 to b6ba044 Compare July 13, 2024 23:09
src/dsl.jl Show resolved Hide resolved
src/dsl.jl Show resolved Hide resolved
src/dsl.jl Show resolved Hide resolved
src/dsl.jl Show resolved Hide resolved
src/dsl.jl Show resolved Hide resolved
variables = vcat(variables_declared, vars_extracted)

# handle independent variables
Copy link
Member Author

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
Copy link
Member Author

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)

Copy link
Member

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).

Copy link
Member

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.

src/dsl.jl Show resolved Hide resolved
src/dsl.jl Show resolved Hide resolved
symvec = gensym()
ex = quote
$symvec = $ex
expr = quote
Copy link
Member Author

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
Copy link
Member Author

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
Copy link
Member Author

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
Copy link
Member Author

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)

@TorkelE TorkelE changed the title [wip] Refactor "srcd/sl.jl" file Refactor "srcd/sl.jl" file Jul 14, 2024
Copy link
Member

@isaacsas isaacsas left a 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))
Copy link
Member

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.

Comment on lines +29 to +30
isempty(intersect(forbidden_symbols_error, sym)) && return
used_forbidden_syms = intersect(forbidden_symbols_error, sym)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Comment on lines +36 to +37
isempty(intersect(forbidden_variables_error, sym)) && return
used_forbidden_syms = intersect(forbidden_variables_error, sym)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Comment on lines +43 to +44
allunique(syms) && return
error("Reaction network independent variables, parameters, species, and variables must all have distinct names, but a duplicate has been detected. ")
Copy link
Member

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
Copy link
Member

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).

Comment on lines +776 to 783
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.")
Copy link
Member

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")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sort_func(iv) = findfirst(MT.getname(iv) == ivs for ivs in ivs_sorted)
sort_func(iv) = findfirst(==(MT.getname(iv)), ivs_sorted)

Comment on lines +854 to +855
macro (but permiting only a single reaction). A more detailed introduction to the syntax can
be found in the description of `@reaction_network`.
Copy link
Member

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.

Copy link
Member

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.)

Comment on lines 927 to 928
$pexprs
$iv
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$pexprs
$iv
$iv
$pexprs

per the other PR we should probably set the iv first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants