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

[Wip] Refactor "src/dsl.jl" file #985

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
efd44be
save progress
TorkelE May 27, 2024
d2985c0
save progress
TorkelE May 28, 2024
b06068f
save progress
TorkelE May 28, 2024
5cc8f41
save progress
TorkelE May 28, 2024
15273f1
save progress
TorkelE May 28, 2024
b105b3e
add additional tests
TorkelE May 28, 2024
1324428
up
TorkelE May 28, 2024
71c9b5c
fix
TorkelE Jun 2, 2024
9ab9290
Merge branch 'master' into dsl_file_improvements
TorkelE Jun 11, 2024
d45f0a7
merge fixes
TorkelE Jun 11, 2024
b6ba044
Merge branch 'master' into src___refactoring___dsl_file
TorkelE Jul 13, 2024
cdac225
merge fixes
TorkelE Jul 13, 2024
ea9aae3
up
TorkelE Jul 13, 2024
7084133
test fixes
TorkelE Jul 14, 2024
a6b4c3a
format
TorkelE Jul 14, 2024
160668f
up
TorkelE Jul 14, 2024
9c265ee
observables fix
TorkelE Jul 14, 2024
34fc2c0
spatial transport reaction dsl fix
TorkelE Jul 14, 2024
e9fa7ba
Merge branch 'master' into src___refactoring___dsl_file
TorkelE Jul 16, 2024
37ea1d9
iv is now a parmaeter
TorkelE Jul 16, 2024
87464f7
Add tests for various erroneous declarations
TorkelE Jul 17, 2024
8156eca
update old @variable test
TorkelE Jul 17, 2024
fba3f5e
Merge branch 'master' into src___refactoring___dsl_file
TorkelE Jan 16, 2025
50f8f82
save progress
TorkelE Jan 16, 2025
4e720ee
save progress
TorkelE Jan 16, 2025
a1e967a
save progress
TorkelE Jan 17, 2025
fadeb67
Merge branch 'master' into src___refactoring___dsl_file
TorkelE Jan 18, 2025
b235784
save progress
TorkelE Jan 18, 2025
18fcbf1
prepare formatting round
TorkelE Jan 18, 2025
9230667
up
TorkelE Jan 18, 2025
78ca087
multiple fixes
TorkelE Jan 18, 2025
9d62c28
docstring fix
TorkelE Jan 18, 2025
9a928fe
fixes
TorkelE Jan 18, 2025
53a6254
up
TorkelE Jan 18, 2025
27f7add
fix function expansion of differentials
TorkelE Jan 18, 2025
99e54d2
spelling fix
TorkelE Jan 18, 2025
7ec7383
better handling of forbidden symbols and more tests
TorkelE Jan 19, 2025
5f35480
improve option error handling, more tests
TorkelE Jan 19, 2025
292da20
minor upodates after read through
TorkelE Jan 19, 2025
2902101
doc fix, combinatorial_ratelaw error handling
TorkelE Jan 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Catalyst.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
TorkelE marked this conversation as resolved.
Show resolved Hide resolved
import Graphs: DiGraph, SimpleGraph, SimpleDiGraph, vertices, edges, add_vertices!, nv, ne
import DataStructures: OrderedDict, OrderedSet
import Parameters: @with_kw_noshow
Expand Down
2 changes: 1 addition & 1 deletion src/chemistry_functionality.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
TorkelE marked this conversation as resolved.
Show resolved Hide resolved
components = :([]) # Becomes something like :([C, O]).
coefficients = :([]) # Becomes something like :([1, 2]).
for comp in composition
Expand Down
792 changes: 419 additions & 373 deletions src/dsl.jl

Large diffs are not rendered by default.

44 changes: 21 additions & 23 deletions src/expression_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

# Function that handles variable interpolation.
function esc_dollars!(ex)
if ex isa Expr
if ex.head == :$
return esc(:($(ex.args[1])))
else
for i in 1:length(ex.args)
ex.args[i] = esc_dollars!(ex.args[i])
end
# If we do not have an expression: recursion has finished and we return the input.
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
(ex isa Expr) || (return ex)

# If we have encountered an interpolation, perform the appropriate modification, else recur.
if ex.head == :$
return esc(:($(ex.args[1])))
else
for i in eachindex(ex.args)
ex.args[i] = esc_dollars!(ex.args[i])
end
end

ex
end

Expand All @@ -22,28 +25,23 @@ end
### Parameters/Species/Variables Symbols Correctness Checking ###

# Throws an error when a forbidden symbol is used.
function forbidden_symbol_check(v)
!isempty(intersect(forbidden_symbols_error, v)) &&
error("The following symbol(s) are used as species or parameters: " *
((map(s -> "'" * string(s) * "', ",
intersect(forbidden_symbols_error, v))...)) *
"this is not permitted.")
nothing
function forbidden_symbol_check(sym)
isempty(intersect(forbidden_symbols_error, sym)) && return
used_forbidden_syms = intersect(forbidden_symbols_error, sym)
TorkelE marked this conversation as resolved.
Show resolved Hide resolved
error("The following symbol(s) are used as species or parameters: $used_forbidden_syms, this is not permitted.")
end

# Throws an error when a forbidden variable is used (a forbidden symbol that is not `:t`).
function forbidden_variable_check(v)
!isempty(intersect(forbidden_variables_error, v)) &&
error("The following symbol(s) are used as variables: " *
((map(s -> "'" * string(s) * "', ",
intersect(forbidden_variables_error, v))...)) *
"this is not permitted.")
Copy link
Member Author

Choose a reason for hiding this comment

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

Again, the only time this function is issued it don't actually have any use. Old remnant and thus removed.

function forbidden_variable_check(sym)
isempty(intersect(forbidden_variables_error, sym)) && return
used_forbidden_syms = intersect(forbidden_variables_error, sym)
TorkelE marked this conversation as resolved.
Show resolved Hide resolved
error("The following symbol(s) are used as variables: $used_forbidden_syms, this is not permitted.")
end

# Checks that no symbol was sued for multiple purposes.
function unique_symbol_check(syms)
allunique(syms) ||
error("Reaction network independent variables, parameters, species, and variables must all have distinct names, but a duplicate has been detected. ")
nothing
allunique(syms) && return
error("Reaction network independent variables, parameters, species, and variables must all have distinct names, but a duplicate has been detected. ")
TorkelE marked this conversation as resolved.
Show resolved Hide resolved
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.

Now only used in one place, there implemented directly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Helper function for handling error checks across multiple options.


### Catalyst-specific Expressions Manipulation ###
Expand Down
2 changes: 1 addition & 1 deletion src/spatial_reaction_systems/spatial_reactions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function make_transport_reaction(rateex, species)
forbidden_symbol_check(union([species], parameters))

# Creates expressions corresponding to actual code from the internal DSL representation.
sexprs = get_sexpr([species], Dict{Symbol, Expr}())
sexprs = get_usexpr([species], Dict{Symbol, Expr}())
pexprs = get_pexpr(parameters, Dict{Symbol, Expr}())
iv = :($(DEFAULT_IV_SYM) = default_t())
trxexpr = :(TransportReaction($rateex, $species))
Expand Down
32 changes: 25 additions & 7 deletions test/dsl/dsl_advanced_model_construction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)

end
Copy link
Member Author

@TorkelE TorkelE Jan 18, 2025

Choose a reason for hiding this comment

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

We can discuss this one, but what it actually does is kind of funny. It basically takes the internal function and evaluates it in the current scope somehow. However, when we have clarified the macro output to make it easier to read, e.g. putting

observed = $obs_eqs
continuous_events = $continuous_events_expr
discrete_events = $discrete_events_expr

in the macro output quote and then use

make_ReactionSystem_internal(rx_eq_vec, $tiv, vars, $psvar; name, spatial_ivs,
                observed, continuous_events, discrete_events, combinatoric_ratelaws)

we get the trouble that observed overwrites observed which is an exported function. Now the test is kind of artificial, so Catalyst works perfectly even if this generates an error, and it makes e.g. @macroexpand a bit easier to use. The contrary argument for keeping this is that if one wants to copy and evaluate the code then one must modify. Right now I removed the test, but we can discuss.


# Miscellaneous interpolation tests. Unsure what they do here (not related to DSL).
Expand Down Expand Up @@ -211,11 +211,11 @@ let
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.

k, 0 --> X, [md1=1.0, md1=1.0]
end
let
@test_throws Exception @eval @reaction k, 0 --> X, [md1=1.0, md1=2.0]
@test_throws Exception @eval @reaction_network begin
k, 0 --> X, [md1=1.0, md1=1.0]
end
end

# Tests for nested metadata.
Expand Down Expand Up @@ -267,6 +267,24 @@ let
@test isequal(rn1,rn2)
end

# Tests that erroneous metadata declarations yields errors.
let
# Malformed metadata/value separator.
@test_throws Exception @eval @reaction_network begin
d, X --> 0, [misc=>"Metadata should use `=`, not `=>`."]
end

# Malformed lhs
@test_throws Exception @eval @reaction_network begin
d, X --> 0, [misc,description=>"Metadata lhs should be a single symbol."]
end

# Malformed metadata separator.
@test_throws Exception @eval @reaction_network begin
d, X --> 0, [misc=>:misc; description="description"]
end
end

### Other Tests ###

# Test floating point stoichiometry work.
Expand Down Expand Up @@ -344,4 +362,4 @@ let
rx = Reaction(k[1]*a+k[2], [X[1], X[2]], [Y, C], [1, V[1]], [V[2] * W, B])
@named arrtest = ReactionSystem([rx], t)
arrtest == rn
end
end
96 changes: 95 additions & 1 deletion test/dsl/dsl_basic_model_construction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,65 @@ end

## Run Tests ###

# Tests the various network constructors. Test for `@network_component` and `@network_component`.
# Tests for combinations of reactions/no reactions, no name/name/interpolated name.
let
# Declare comparison networks programmatically.
@parameters d
@species X(t)
rx = Reaction(d, [X], [])

rs_empty = ReactionSystem([], t; name = :name)
rs = ReactionSystem([rx], t; name = :name)
rs_empty_comp = complete(rs_empty)
rs_comp = complete(rs)

# Declare empty networks.
name_sym = :name
rs_empty_1 = @network_component
rs_empty_2 = @network_component name
rs_empty_3 = @network_component $name_sym
rs_empty_comp_1 = @reaction_network
rs_empty_comp_2 = @reaction_network name
rs_empty_comp_3 = @reaction_network $name_sym

# Check that empty networks are correct.
isequivalent(rs_empty_1, rs_empty)
rs_empty_2 == rs_empty
rs_empty_3 == rs_empty
isequivalent(rs_empty_comp_1, rs_empty_comp)
rs_empty_comp_2 == rs_empty_comp
rs_empty_comp_3 == rs_empty_comp

# Declare non-empty networks.
rs_1 = @network_component begin
d, X --> 0
end
rs_2 = @network_component name begin
d, X --> 0
end
rs_3 = @network_component $name_sym begin
d, X --> 0
end
rs_comp_1 = @reaction_network begin
d, X --> 0
end
rs_comp_2 = @reaction_network name begin
d, X --> 0
end
rs_comp_3 = @reaction_network $name_sym begin
d, X --> 0
end

# Check that non-empty networks are correct.
isequivalent(rs_1, rs)
rs_2 == rs
rs_3 == rs
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.


# Test basic properties of networks.
let
basic_test(reaction_networks_standard[1], 10, [:X1, :X2, :X3],
Expand Down Expand Up @@ -404,7 +463,7 @@ let
@test rn1 == rn2
end

# Tests arrow variants in `@reaction`` macro .
# Tests arrow variants in `@reaction`` macro.
let
@test isequal((@reaction k, 0 --> X), (@reaction k, X <-- 0))
@test isequal((@reaction k, 0 --> X), (@reaction k, X ⟻ 0))
Expand Down Expand Up @@ -437,6 +496,41 @@ let
@test_throws LoadError @eval @reaction nothing, 0 --> B
@test_throws LoadError @eval @reaction k, 0 --> im
@test_throws LoadError @eval @reaction k, 0 --> nothing

# Checks that non-supported arrow type usage yields error.
@test_throws Exception @eval @reaction_network begin
d, X ⇻ 0
end
end

### Error Test ###

# Erroneous `@reaction` usage.
let
# Bi-directional reaction using the `@reaction` macro.
@test_throws Exception @eval @reaction (k1,k2), X1 <--> X2

# Bundles reactions.
@test_throws Exception @eval @reaction k, (X1,X2) --> 0
end

# Tests that malformed reactions yields errors.
let
# Checks that malformed combinations of entries yields errors.
@test_throws Exception @eval @reaction_network begin
d, X --> 0, Y --> 0
end
@test_throws Exception @eval @reaction_network begin
d, X --> 0, [misc="Ok metadata"], [description="Metadata in (erroneously) extra []."]
end

# Checks that incorrect bundling yields error.
@test_throws Exception @eval @reaction_network begin
(k1,k2,k3), (X1,X2) --> 0
end

# Checks that incorrect stoichiometric expression yields an error.
@test_throws Exception @eval @reaction_network begin
k, X^Y --> XY
end
end
62 changes: 57 additions & 5 deletions test/dsl/dsl_options.jl
Original file line number Diff line number Diff line change
Expand Up @@ -414,15 +414,44 @@ let
spcs = (A, B1, B2, C)
@test issetequal(unknowns(rn), sts)
@test issetequal(species(rn), spcs)
end

@test_throws ArgumentError begin
rn = @reaction_network begin
@variables K
k, K*A --> B
end
# Tests errors in `@variables` declarations.
let
# Variable used as species in reaction.
@test_throws Exception @eval rn = @reaction_network begin
@variables K(t)
k, K + A --> B
end

# Tests error when disallowed name is used for variable.
@test_throws Exception @eval @reaction_network begin
@variables π(t)
Copy link
Member Author

Choose a reason for hiding this comment

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

new test

end
end


# Tests that duplicate iv/parameter/species/variable names cannot be provided.
let
@test_throws Exception @eval @reaction_network begin
@spatial_ivs X
@species X(t)
end
@test_throws Exception @eval @reaction_network begin
@parameters X
@species X(t)
end
@test_throws Exception @eval @reaction_network begin
@species X(t)
@variables X(t)
end
@test_throws Exception @eval @reaction_network begin
@parameters X
@variables X(t)
end
end


### Test Independent Variable Designations ###

# Test ivs in DSL.
Expand Down Expand Up @@ -739,6 +768,14 @@ let
@observables $X ~ X1 + X2
(k1,k2), X1 <--> X2
end

# Observable metadata provided twice.
@test_throws Exception @eval @reaction_network begin
@species X2 [description="Twice the amount of X"]
@observables (X2, [description="X times two."]) ~ 2X
d, X --> 0
end

end


Expand Down Expand Up @@ -916,8 +953,15 @@ let
@equations X ~ p - S
(P,D), 0 <--> S
end

# Differential equation using a forbidden variable (in the DSL).
@test_throws Exception @eval @reaction_network begin
@equations D(π) ~ -1
end
end

### Other DSL Option Tests ###

# test combinatoric_ratelaws DSL option
let
rn = @reaction_network begin
Expand Down Expand Up @@ -951,3 +995,11 @@ let
@unpack k1, A = rn3
@test isequal(rl, k1*A^2)
end

# Erroneous `@default_noise_scaling` declaration (other noise scaling tests are mostly in the SDE file).
let
# Default noise scaling with multiple entries.
@test_throws Exception @eval @reaction_network begin
@default_noise_scaling η1 η2
end
end
Loading