From 033f2020507ed2d40363d9dfeea15b53db7153e9 Mon Sep 17 00:00:00 2001 From: Sean <10673535+slwu89@users.noreply.github.com> Date: Tue, 10 Oct 2023 08:02:45 -0700 Subject: [PATCH] Fixes for compatibility with latest Catlab/ACSets (#15) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fixes for new versions of Catlab/ACSets * drop Catlab dep, add ACSets; use parts rather than 1:nparts * fix tutorials and tests --------- Co-authored-by: Bíma, Jan --- Project.toml | 4 +- docs/build/index.html | 2 +- docs/build/search_index.js | 2 +- readme.md | 6 +- src/ReactiveDynamics.jl | 106 +++++++++++++-------------- src/compilers.jl | 2 +- src/interface/update.jl | 2 +- src/loadsave.jl | 11 ++- src/operators/equalize.jl | 2 +- src/operators/joins.jl | 8 +- src/solvers.jl | 33 +++++---- src/state.jl | 23 ++++-- test/tutorial_tests.jl | 2 + tutorial/example.jl | 30 +++----- tutorial/joins/joins.jl | 10 +-- tutorial/joins/submodel.jl | 2 +- tutorial/loadsave/loadsave.jl | 8 +- tutorial/optimize/optimize.jl | 2 +- tutorial/optimize/optimize_custom.jl | 4 +- tutorial/rd_example.jl | 4 +- tutorial/toy_pharma_model.jl | 24 ++---- 21 files changed, 143 insertions(+), 144 deletions(-) diff --git a/Project.toml b/Project.toml index 5385d89..87db175 100644 --- a/Project.toml +++ b/Project.toml @@ -3,9 +3,9 @@ uuid = "c7456e7d-545a-4b79-91ea-6e93d96dd4d4" version = "0.2.7" [deps] +ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" AlgebraicAgents = "f6eb0ae3-10fa-40e6-88dd-9006ba45093a" CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" -Catlab = "134e5e36-593f-5add-ad60-77f754baafbe" ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" Crayons = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" @@ -28,8 +28,8 @@ TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" [compat] +ACSets = "0.2.6" CSV = "0.10" -Catlab = "0.14" ComponentArrays = "0.14" Crayons = "4.1" DataFrames = "1.6" diff --git a/docs/build/index.html b/docs/build/index.html index 93c5e9e..991dc9f 100644 --- a/docs/build/index.html +++ b/docs/build/index.html @@ -1,5 +1,5 @@ -API Documentation · ReactiveDynamics.jl API Documentation

API Documentation

Create a model

ReactiveDynamics.@ReactionNetworkMacro

Macro that takes an expression corresponding to a reaction network and outputs an instance of TheoryReactionNetwork that can be converted to a DiscreteProblem or solved directly.

Most arrows accepted (both right, left, and bi-drectional arrows). Use 0 or ∅ for annihilation/creation to/from nothing.

Custom functions and sampleable objects can be used as numeric parameters. Note that these have to be accessible from ReactiveDynamics's source code.

Examples

acs = @ReactionNetwork begin
+API Documentation · ReactiveDynamics.jl API Documentation

API Documentation

Create a model

ReactiveDynamics.@ReactionNetworkMacro

Macro that takes an expression corresponding to a reaction network and outputs an instance of TheoryReactionNetwork that can be converted to a DiscreteProblem or solved directly.

Most arrows accepted (both right, left, and bi-drectional arrows). Use 0 or ∅ for annihilation/creation to/from nothing.

Custom functions and sampleable objects can be used as numeric parameters. Note that these have to be accessible from ReactiveDynamics's source code.

Examples

acs = @ReactionNetworkSchema begin
     1.0, X ⟶ Y
     1.0, X ⟶ Y, priority=>6., prob=>.7, capacity=>3.
     1.0, ∅ --> (Poisson(.3γ)X, Poisson(.5)Y)
diff --git a/docs/build/search_index.js b/docs/build/search_index.js
index addd92b..6b6953e 100644
--- a/docs/build/search_index.js
+++ b/docs/build/search_index.js
@@ -1,3 +1,3 @@
 var documenterSearchIndex = {"docs":
-[{"location":"index.html#API-Documentation","page":"API Documentation","title":"API Documentation","text":"","category":"section"},{"location":"index.html#Create-a-model","page":"API Documentation","title":"Create a model","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@ReactionNetwork","category":"page"},{"location":"index.html#ReactiveDynamics.@ReactionNetwork","page":"API Documentation","title":"ReactiveDynamics.@ReactionNetwork","text":"Macro that takes an expression corresponding to a reaction network and outputs an instance of TheoryReactionNetwork that can be converted to a DiscreteProblem or solved directly.\n\nMost arrows accepted (both right, left, and bi-drectional arrows). Use 0 or ∅ for annihilation/creation to/from nothing.\n\nCustom functions and sampleable objects can be used as numeric parameters. Note that these have to be accessible from ReactiveDynamics's source code.\n\nExamples\n\nacs = @ReactionNetwork begin\n    1.0, X ⟶ Y\n    1.0, X ⟶ Y, priority=>6., prob=>.7, capacity=>3.\n    1.0, ∅ --> (Poisson(.3γ)X, Poisson(.5)Y)\n    (XY > 100) && (XY -= 1)\nend\n@push acs 1.0 X ⟶ Y \n@prob_init acs X=1 Y=2 XY=α\n@prob_params acs γ=1 α=4\n@solve_and_plot acs\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Modify-a-model","page":"API Documentation","title":"Modify a model","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"We list common transition attributes:","category":"page"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"attribute interpretation\ntransPriority priority of a transition (influences resource allocation)\ntransProbOfSuccess probability that a transition terminates successfully\ntransCapacity maximum number of concurrent instances of the transition\ntransCycleTime duration of a transition's instance (adjusted by resource allocation)\ntransMaxLifeTime maximal duration of a transition's instance\ntransPostAction action to be executed once a transition's instance terminates\ntransName name of a transition","category":"page"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"We list common species attributes:","category":"page"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"attribute interpretation\nspecInitUncertainty uncertainty about variable's initial state (modelled as Gaussian standard deviation)\nspecInitVal initial value of a variable","category":"page"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"Moreover, it is possible to specify the semantics of the \"rate\" term. By default, at each time step n ~ Poisson(rate * dt) instances of a given transition will be spawned. If you want to specify the rate in terms of a cycle time, you may want to use @ct(cycle_time), e.g., @ct(ex), A --> B, .... This is a shorthand for 1/ex, A --> B, ....","category":"page"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"For deterministic \"rates\", use @per_step(ex). Here, ex evaluates to a deterministic number (ceiled to the nearest integer) of a transition's instances to spawn per a single integrator's step. However, note that in this case, the number doesn't scale with the step length! Moreover","category":"page"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@add_species\n@aka\n@mode\n@name_transition","category":"page"},{"location":"index.html#ReactiveDynamics.@add_species","page":"API Documentation","title":"ReactiveDynamics.@add_species","text":"Add new species to a model.\n\nExamples\n\n@add_species acs S I R\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@aka","page":"API Documentation","title":"ReactiveDynamics.@aka","text":"Alias object name in an acs.\n\nDefault names\n\nname short name\nspecies S\ntransition T\naction A\nevent E\nparam P\nmeta M\n\nExamples\n\n@aka acs species=resource transition=reaction\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@mode","page":"API Documentation","title":"ReactiveDynamics.@mode","text":"Set species modality.\n\nSupported modalities\n\nnonblock\nconserved\nrate\n\nExamples\n\n@mode acs (r\"proj\\w+\", r\"experimental\\w+\") conserved\n@mode acs (S, I) conserved\n@mode acs S conserved\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@name_transition","page":"API Documentation","title":"ReactiveDynamics.@name_transition","text":"Set name of a transition in the model.\n\nExamples\n\n@name_transition acs 1=\"name\"\n@name_transition acs name=\"transition_name\"\n@name_transition acs \"name\"=\"transition_name\"\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Resource-costs","page":"API Documentation","title":"Resource costs","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@cost\n@valuation\n@reward","category":"page"},{"location":"index.html#ReactiveDynamics.@cost","page":"API Documentation","title":"ReactiveDynamics.@cost","text":"Set cost.\n\nExamples\n\n@cost model experimental1=2 experimental2=3\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@valuation","page":"API Documentation","title":"ReactiveDynamics.@valuation","text":"Set valuation.\n\nExamples\n\n@valuation model experimental1=2 experimental2=3\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@reward","page":"API Documentation","title":"ReactiveDynamics.@reward","text":"Set reward.\n\nExamples\n\n@reward model experimental1=2 experimental2=3\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Add-reactions","page":"API Documentation","title":"Add reactions","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@push\n@jump\n@periodic","category":"page"},{"location":"index.html#ReactiveDynamics.@push","page":"API Documentation","title":"ReactiveDynamics.@push","text":"Add reactions to an acset.\n\nExamples\n\n@push sir_acs β*S*I*tdecay(@time()) S+I --> 2I name=>SI2I\n@push sir_acs begin \n    ν*I, I --> R, name=>I2R\n    γ, R --> S, name=>R2S\nend\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@jump","page":"API Documentation","title":"ReactiveDynamics.@jump","text":"Add a jump process (with specified Poisson intensity per unit time step) to a model.\n\nExamples\n\n@jump acs λ Z += rand(Poisson(1.))\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@periodic","page":"API Documentation","title":"ReactiveDynamics.@periodic","text":"Add a periodic callback to a model.\n\nExamples\n\n@periodic acs 1. X += 1\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Set-initial-values,-uncertainty,-and-solver-arguments","page":"API Documentation","title":"Set initial values, uncertainty, and solver arguments","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@prob_init\n@prob_uncertainty\n@prob_params\n@prob_meta","category":"page"},{"location":"index.html#ReactiveDynamics.@prob_init","page":"API Documentation","title":"ReactiveDynamics.@prob_init","text":"Set initial values of species in an acset.\n\nExamples\n\n@prob_init acs X=1 Y=2 Z=h(α)\n@prob_init acs [1., 2., 3.]\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@prob_uncertainty","page":"API Documentation","title":"ReactiveDynamics.@prob_uncertainty","text":"Set uncertainty in initial values of species in an acset (stderr).\n\nExamples\n\n@prob_uncertainty acs X=.1 Y=.2\n@prob_uncertainty acs [.1, .2,]\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@prob_params","page":"API Documentation","title":"ReactiveDynamics.@prob_params","text":"Set parameter values in an acset.\n\nExamples\n\n@prob_params acs α=1. β=2.\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@prob_meta","page":"API Documentation","title":"ReactiveDynamics.@prob_meta","text":"Set model metadata (e.g. solver arguments)\n\nExamples\n\n@prob_meta acs tspan=(0, 100.) schedule=schedule_weighted!\n@prob_meta sir_acs tspan=250 tstep=1\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Model-unions","page":"API Documentation","title":"Model unions","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@join\n@equalize","category":"page"},{"location":"index.html#ReactiveDynamics.@join","page":"API Documentation","title":"ReactiveDynamics.@join","text":"@join models... [equalize...]\n\nPerforms join of models and identifies model variables, as specified.\n\nModel variables / parameter values and metadata are propagated; the last model takes precedence.\n\nExamples\n\n@join acs1 acs2 @catchall(A)=acs2.Z @catchall(XY) @catchall(B)\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@equalize","page":"API Documentation","title":"ReactiveDynamics.@equalize","text":"Identify (collapse) a set of species in a model.\n\nExamples\n\n@join acs acs1.A=acs2.A B=C\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Model-import-and-export","page":"API Documentation","title":"Model import and export","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@import_network\n@export_network","category":"page"},{"location":"index.html#ReactiveDynamics.@import_network","page":"API Documentation","title":"ReactiveDynamics.@import_network","text":"Import a model from a file: this can be either a single TOML file encoding the entire model, or a batch of CSV files (a root file and a number of files, each per a class of objects).\n\nSee tutorials/loadsave for an example.\n\nExamples\n\n@import_network \"model.toml\"\n@import_network \"csv/model.toml\"\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@export_network","page":"API Documentation","title":"ReactiveDynamics.@export_network","text":"Export model to a file: this can be either a single TOML file encoding the entire model, or a batch of CSV files (a root file and a number of files, each per a class of objects).\n\nSee tutorials/loadsave for an example.\n\nExamples\n\n@export_network acs \"acs_data.toml\" # as a TOML\n@export_network acs \"csv/model.csv\" # as a CSV\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Solution-import-and-export","page":"API Documentation","title":"Solution import and export","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@import_solution\n@export_solution_as_table\n@export_solution_as_csv\n@export_solution","category":"page"},{"location":"index.html#ReactiveDynamics.@import_solution","page":"API Documentation","title":"ReactiveDynamics.@import_solution","text":"@import_solution \"sol.jld2\"\n@import_solution \"sol.jld2\" sol\n\nImport a solution from a file.\n\nExamples\n\n@import_solution \"sir_acs_sol/serialized/sol.jld2\"\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@export_solution_as_table","page":"API Documentation","title":"ReactiveDynamics.@export_solution_as_table","text":"@export_solution_as_table sol\n\nExport a solution as a DataFrame.\n\nExamples\n\n@export_solution_as_table sol\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@export_solution_as_csv","page":"API Documentation","title":"ReactiveDynamics.@export_solution_as_csv","text":"@export_solution_as_csv sol\n@export_solution_as_csv sol \"sol.csv\"\n\nExport a solution to a file.\n\nExamples\n\n@export_solution_as_csv sol \"sol.csv\"\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@export_solution","page":"API Documentation","title":"ReactiveDynamics.@export_solution","text":"@export_solution sol\n@export_solution sol \"sol.jld2\"\n\nExport a solution to a file.\n\nExamples\n\n@export_solution sol \"sol.jdl2\"\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Problematize,sSolve,-and-plot","page":"API Documentation","title":"Problematize,sSolve, and plot","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@problematize\n@solve\n@plot","category":"page"},{"location":"index.html#ReactiveDynamics.@problematize","page":"API Documentation","title":"ReactiveDynamics.@problematize","text":"Convert a model to a DiscreteProblem. If passed a problem instance, return the instance.\n\nExamples\n\n@problematize acs tspan=1:100\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@solve","page":"API Documentation","title":"ReactiveDynamics.@solve","text":"Solve the problem. Solverargs passed at the calltime take precedence.\n\nExamples\n\n@solve prob\n@solve prob tspan=1:100\n@solve prob tspan=100 trajectories=20\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@plot","page":"API Documentation","title":"ReactiveDynamics.@plot","text":"Plot the solution (summary).\n\nExamples\n\n@plot sol plot_type=summary\n@plot sol plot_type=allocation # not supported for ensemble solutions!\n@plot sol plot_type=valuations # not supported for ensemble solutions!\n@plot sol plot_type=new_transitions # not supported for ensemble solutions!\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Optimization-and-fitting","page":"API Documentation","title":"Optimization and fitting","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@optimize\n@fit\n@fit_and_plot\n@build_solver","category":"page"},{"location":"index.html#ReactiveDynamics.@optimize","page":"API Documentation","title":"ReactiveDynamics.@optimize","text":"@optimize acset objective ... ... opts...\n\nTake an acset and optimize given functional.\n\nObjective is an expression which may reference the model's variables and parameters, i.e., A+β. The values to optimized are listed using their symbolic names; unless specified, the initial value is inferred from the model. The vector of free variables passed to the NLopt solver has the form [free_vars; free_params]; order of vars and params, respectively, is preserved. \n\nBy default, the functional is minimized. Specify objective=max to perform maximization. \n\nPropagates NLopt solver arguments; see NLopt documentation.\n\nExamples\n\n@optimize acs abs(A-B) A B=20. α=2. lower_bounds=0 upper_bounds=100\n@optimize acss abs(A-B) A B=20. α=2. upper_bounds=[200,300,400] maxeval=200 objective=min\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@fit","page":"API Documentation","title":"ReactiveDynamics.@fit","text":"@fit acset data_points time_steps empiric_variables ... ... opts...\n\nTake an acset and fit initial values and parameters to empirical data.\n\nThe values to optimized are listed using their symbolic names; unless specified, the initial value is inferred from the model. The vector of free variables passed to the NLopt solver has the form [free_vars; free_params]; order of vars and params, respectively, is preserved. \n\nPropagates NLopt solver arguments; see NLopt documentation.\n\nExamples\n\nt = [1, 50, 100]\ndata = [80 30 20]\n@fit acs data t vars=A B=20 A α # fit B, A, α; empirical data is for variable A\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@fit_and_plot","page":"API Documentation","title":"ReactiveDynamics.@fit_and_plot","text":"@fit acset data_points time_steps empiric_variables ... ... opts...\n\nTake an acset, fit initial values and parameters to empirical data, and plot the result.\n\nThe values to optimized are listed using their symbolic names; unless specified, the initial value is inferred from the model. The vector of free variables passed to the NLopt solver has the form [free_vars; free_params]; order of vars and params, respectively, is preserved. \n\nPropagates NLopt solver arguments; see NLopt documentation.\n\nExamples\n\nt = [1, 50, 100]\ndata = [80 30 20]\n@fit acs data t vars=A B=20 A α # fit B, A, α; empirical data is for variable A\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@build_solver","page":"API Documentation","title":"ReactiveDynamics.@build_solver","text":"@build_solver acset ... ... opts...\n\nTake an acset and export a solution as a function of free vars and free parameters.\n\nExamples\n\nsolver = @build_solver acs S α β # function of variable S and parameters α, β\nsolver([S, α, β])\n\n\n\n\n\n","category":"macro"}]
+[{"location":"index.html#API-Documentation","page":"API Documentation","title":"API Documentation","text":"","category":"section"},{"location":"index.html#Create-a-model","page":"API Documentation","title":"Create a model","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@ReactionNetwork","category":"page"},{"location":"index.html#ReactiveDynamics.@ReactionNetwork","page":"API Documentation","title":"ReactiveDynamics.@ReactionNetwork","text":"Macro that takes an expression corresponding to a reaction network and outputs an instance of TheoryReactionNetwork that can be converted to a DiscreteProblem or solved directly.\n\nMost arrows accepted (both right, left, and bi-drectional arrows). Use 0 or ∅ for annihilation/creation to/from nothing.\n\nCustom functions and sampleable objects can be used as numeric parameters. Note that these have to be accessible from ReactiveDynamics's source code.\n\nExamples\n\nacs = @ReactionNetworkSchema begin\n    1.0, X ⟶ Y\n    1.0, X ⟶ Y, priority=>6., prob=>.7, capacity=>3.\n    1.0, ∅ --> (Poisson(.3γ)X, Poisson(.5)Y)\n    (XY > 100) && (XY -= 1)\nend\n@push acs 1.0 X ⟶ Y \n@prob_init acs X=1 Y=2 XY=α\n@prob_params acs γ=1 α=4\n@solve_and_plot acs\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Modify-a-model","page":"API Documentation","title":"Modify a model","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"We list common transition attributes:","category":"page"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"attribute interpretation\ntransPriority priority of a transition (influences resource allocation)\ntransProbOfSuccess probability that a transition terminates successfully\ntransCapacity maximum number of concurrent instances of the transition\ntransCycleTime duration of a transition's instance (adjusted by resource allocation)\ntransMaxLifeTime maximal duration of a transition's instance\ntransPostAction action to be executed once a transition's instance terminates\ntransName name of a transition","category":"page"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"We list common species attributes:","category":"page"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"attribute interpretation\nspecInitUncertainty uncertainty about variable's initial state (modelled as Gaussian standard deviation)\nspecInitVal initial value of a variable","category":"page"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"Moreover, it is possible to specify the semantics of the \"rate\" term. By default, at each time step n ~ Poisson(rate * dt) instances of a given transition will be spawned. If you want to specify the rate in terms of a cycle time, you may want to use @ct(cycle_time), e.g., @ct(ex), A --> B, .... This is a shorthand for 1/ex, A --> B, ....","category":"page"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"For deterministic \"rates\", use @per_step(ex). Here, ex evaluates to a deterministic number (ceiled to the nearest integer) of a transition's instances to spawn per a single integrator's step. However, note that in this case, the number doesn't scale with the step length! Moreover","category":"page"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@add_species\n@aka\n@mode\n@name_transition","category":"page"},{"location":"index.html#ReactiveDynamics.@add_species","page":"API Documentation","title":"ReactiveDynamics.@add_species","text":"Add new species to a model.\n\nExamples\n\n@add_species acs S I R\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@aka","page":"API Documentation","title":"ReactiveDynamics.@aka","text":"Alias object name in an acs.\n\nDefault names\n\nname short name\nspecies S\ntransition T\naction A\nevent E\nparam P\nmeta M\n\nExamples\n\n@aka acs species=resource transition=reaction\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@mode","page":"API Documentation","title":"ReactiveDynamics.@mode","text":"Set species modality.\n\nSupported modalities\n\nnonblock\nconserved\nrate\n\nExamples\n\n@mode acs (r\"proj\\w+\", r\"experimental\\w+\") conserved\n@mode acs (S, I) conserved\n@mode acs S conserved\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@name_transition","page":"API Documentation","title":"ReactiveDynamics.@name_transition","text":"Set name of a transition in the model.\n\nExamples\n\n@name_transition acs 1=\"name\"\n@name_transition acs name=\"transition_name\"\n@name_transition acs \"name\"=\"transition_name\"\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Resource-costs","page":"API Documentation","title":"Resource costs","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@cost\n@valuation\n@reward","category":"page"},{"location":"index.html#ReactiveDynamics.@cost","page":"API Documentation","title":"ReactiveDynamics.@cost","text":"Set cost.\n\nExamples\n\n@cost model experimental1=2 experimental2=3\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@valuation","page":"API Documentation","title":"ReactiveDynamics.@valuation","text":"Set valuation.\n\nExamples\n\n@valuation model experimental1=2 experimental2=3\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@reward","page":"API Documentation","title":"ReactiveDynamics.@reward","text":"Set reward.\n\nExamples\n\n@reward model experimental1=2 experimental2=3\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Add-reactions","page":"API Documentation","title":"Add reactions","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@push\n@jump\n@periodic","category":"page"},{"location":"index.html#ReactiveDynamics.@push","page":"API Documentation","title":"ReactiveDynamics.@push","text":"Add reactions to an acset.\n\nExamples\n\n@push sir_acs β*S*I*tdecay(@time()) S+I --> 2I name=>SI2I\n@push sir_acs begin \n    ν*I, I --> R, name=>I2R\n    γ, R --> S, name=>R2S\nend\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@jump","page":"API Documentation","title":"ReactiveDynamics.@jump","text":"Add a jump process (with specified Poisson intensity per unit time step) to a model.\n\nExamples\n\n@jump acs λ Z += rand(Poisson(1.))\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@periodic","page":"API Documentation","title":"ReactiveDynamics.@periodic","text":"Add a periodic callback to a model.\n\nExamples\n\n@periodic acs 1. X += 1\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Set-initial-values,-uncertainty,-and-solver-arguments","page":"API Documentation","title":"Set initial values, uncertainty, and solver arguments","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@prob_init\n@prob_uncertainty\n@prob_params\n@prob_meta","category":"page"},{"location":"index.html#ReactiveDynamics.@prob_init","page":"API Documentation","title":"ReactiveDynamics.@prob_init","text":"Set initial values of species in an acset.\n\nExamples\n\n@prob_init acs X=1 Y=2 Z=h(α)\n@prob_init acs [1., 2., 3.]\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@prob_uncertainty","page":"API Documentation","title":"ReactiveDynamics.@prob_uncertainty","text":"Set uncertainty in initial values of species in an acset (stderr).\n\nExamples\n\n@prob_uncertainty acs X=.1 Y=.2\n@prob_uncertainty acs [.1, .2,]\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@prob_params","page":"API Documentation","title":"ReactiveDynamics.@prob_params","text":"Set parameter values in an acset.\n\nExamples\n\n@prob_params acs α=1. β=2.\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@prob_meta","page":"API Documentation","title":"ReactiveDynamics.@prob_meta","text":"Set model metadata (e.g. solver arguments)\n\nExamples\n\n@prob_meta acs tspan=(0, 100.) schedule=schedule_weighted!\n@prob_meta sir_acs tspan=250 tstep=1\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Model-unions","page":"API Documentation","title":"Model unions","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@join\n@equalize","category":"page"},{"location":"index.html#ReactiveDynamics.@join","page":"API Documentation","title":"ReactiveDynamics.@join","text":"@join models... [equalize...]\n\nPerforms join of models and identifies model variables, as specified.\n\nModel variables / parameter values and metadata are propagated; the last model takes precedence.\n\nExamples\n\n@join acs1 acs2 @catchall(A)=acs2.Z @catchall(XY) @catchall(B)\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@equalize","page":"API Documentation","title":"ReactiveDynamics.@equalize","text":"Identify (collapse) a set of species in a model.\n\nExamples\n\n@join acs acs1.A=acs2.A B=C\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Model-import-and-export","page":"API Documentation","title":"Model import and export","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@import_network\n@export_network","category":"page"},{"location":"index.html#ReactiveDynamics.@import_network","page":"API Documentation","title":"ReactiveDynamics.@import_network","text":"Import a model from a file: this can be either a single TOML file encoding the entire model, or a batch of CSV files (a root file and a number of files, each per a class of objects).\n\nSee tutorials/loadsave for an example.\n\nExamples\n\n@import_network \"model.toml\"\n@import_network \"csv/model.toml\"\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@export_network","page":"API Documentation","title":"ReactiveDynamics.@export_network","text":"Export model to a file: this can be either a single TOML file encoding the entire model, or a batch of CSV files (a root file and a number of files, each per a class of objects).\n\nSee tutorials/loadsave for an example.\n\nExamples\n\n@export_network acs \"acs_data.toml\" # as a TOML\n@export_network acs \"csv/model.csv\" # as a CSV\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Solution-import-and-export","page":"API Documentation","title":"Solution import and export","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@import_solution\n@export_solution_as_table\n@export_solution_as_csv\n@export_solution","category":"page"},{"location":"index.html#ReactiveDynamics.@import_solution","page":"API Documentation","title":"ReactiveDynamics.@import_solution","text":"@import_solution \"sol.jld2\"\n@import_solution \"sol.jld2\" sol\n\nImport a solution from a file.\n\nExamples\n\n@import_solution \"sir_acs_sol/serialized/sol.jld2\"\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@export_solution_as_table","page":"API Documentation","title":"ReactiveDynamics.@export_solution_as_table","text":"@export_solution_as_table sol\n\nExport a solution as a DataFrame.\n\nExamples\n\n@export_solution_as_table sol\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@export_solution_as_csv","page":"API Documentation","title":"ReactiveDynamics.@export_solution_as_csv","text":"@export_solution_as_csv sol\n@export_solution_as_csv sol \"sol.csv\"\n\nExport a solution to a file.\n\nExamples\n\n@export_solution_as_csv sol \"sol.csv\"\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@export_solution","page":"API Documentation","title":"ReactiveDynamics.@export_solution","text":"@export_solution sol\n@export_solution sol \"sol.jld2\"\n\nExport a solution to a file.\n\nExamples\n\n@export_solution sol \"sol.jdl2\"\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Problematize,sSolve,-and-plot","page":"API Documentation","title":"Problematize,sSolve, and plot","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@problematize\n@solve\n@plot","category":"page"},{"location":"index.html#ReactiveDynamics.@problematize","page":"API Documentation","title":"ReactiveDynamics.@problematize","text":"Convert a model to a DiscreteProblem. If passed a problem instance, return the instance.\n\nExamples\n\n@problematize acs tspan=1:100\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@solve","page":"API Documentation","title":"ReactiveDynamics.@solve","text":"Solve the problem. Solverargs passed at the calltime take precedence.\n\nExamples\n\n@solve prob\n@solve prob tspan=1:100\n@solve prob tspan=100 trajectories=20\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@plot","page":"API Documentation","title":"ReactiveDynamics.@plot","text":"Plot the solution (summary).\n\nExamples\n\n@plot sol plot_type=summary\n@plot sol plot_type=allocation # not supported for ensemble solutions!\n@plot sol plot_type=valuations # not supported for ensemble solutions!\n@plot sol plot_type=new_transitions # not supported for ensemble solutions!\n\n\n\n\n\n","category":"macro"},{"location":"index.html#Optimization-and-fitting","page":"API Documentation","title":"Optimization and fitting","text":"","category":"section"},{"location":"index.html","page":"API Documentation","title":"API Documentation","text":"@optimize\n@fit\n@fit_and_plot\n@build_solver","category":"page"},{"location":"index.html#ReactiveDynamics.@optimize","page":"API Documentation","title":"ReactiveDynamics.@optimize","text":"@optimize acset objective ... ... opts...\n\nTake an acset and optimize given functional.\n\nObjective is an expression which may reference the model's variables and parameters, i.e., A+β. The values to optimized are listed using their symbolic names; unless specified, the initial value is inferred from the model. The vector of free variables passed to the NLopt solver has the form [free_vars; free_params]; order of vars and params, respectively, is preserved. \n\nBy default, the functional is minimized. Specify objective=max to perform maximization. \n\nPropagates NLopt solver arguments; see NLopt documentation.\n\nExamples\n\n@optimize acs abs(A-B) A B=20. α=2. lower_bounds=0 upper_bounds=100\n@optimize acss abs(A-B) A B=20. α=2. upper_bounds=[200,300,400] maxeval=200 objective=min\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@fit","page":"API Documentation","title":"ReactiveDynamics.@fit","text":"@fit acset data_points time_steps empiric_variables ... ... opts...\n\nTake an acset and fit initial values and parameters to empirical data.\n\nThe values to optimized are listed using their symbolic names; unless specified, the initial value is inferred from the model. The vector of free variables passed to the NLopt solver has the form [free_vars; free_params]; order of vars and params, respectively, is preserved. \n\nPropagates NLopt solver arguments; see NLopt documentation.\n\nExamples\n\nt = [1, 50, 100]\ndata = [80 30 20]\n@fit acs data t vars=A B=20 A α # fit B, A, α; empirical data is for variable A\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@fit_and_plot","page":"API Documentation","title":"ReactiveDynamics.@fit_and_plot","text":"@fit acset data_points time_steps empiric_variables ... ... opts...\n\nTake an acset, fit initial values and parameters to empirical data, and plot the result.\n\nThe values to optimized are listed using their symbolic names; unless specified, the initial value is inferred from the model. The vector of free variables passed to the NLopt solver has the form [free_vars; free_params]; order of vars and params, respectively, is preserved. \n\nPropagates NLopt solver arguments; see NLopt documentation.\n\nExamples\n\nt = [1, 50, 100]\ndata = [80 30 20]\n@fit acs data t vars=A B=20 A α # fit B, A, α; empirical data is for variable A\n\n\n\n\n\n","category":"macro"},{"location":"index.html#ReactiveDynamics.@build_solver","page":"API Documentation","title":"ReactiveDynamics.@build_solver","text":"@build_solver acset ... ... opts...\n\nTake an acset and export a solution as a function of free vars and free parameters.\n\nExamples\n\nsolver = @build_solver acs S α β # function of variable S and parameters α, β\nsolver([S, α, β])\n\n\n\n\n\n","category":"macro"}]
 }
diff --git a/readme.md b/readme.md
index e09ef3e..ae2cf07 100644
--- a/readme.md
+++ b/readme.md
@@ -68,7 +68,7 @@ Follow the SIR model's reactions:
 using ReactiveDynamics
 
 # model dynamics
-sir_acs = @ReactionNetwork begin
+sir_acs = @ReactionNetworkSchema begin
         α*S*I, S+I --> 2I, name=>I2R
         β*I, I --> R, name=>R2S 
 end
@@ -196,7 +196,7 @@ To harness the capabilities of **GeneratedExpressions.jl**, let us first declare
 end
     
 # generate submodel dynamics
-push!(rd_models, @ReactionNetwork begin
+push!(rd_models, @ReactionNetworkSchema begin
                 M[$i][$m, $n], state[$m] + {demand[$i][$m, $n, $l]*resource[$l], l=1:$r, dlm=+} --> state[$n] + 
                         {production[$i][$m, $n, $l]*resource[$l], l=1:$r, dlm=+}, cycle_time=>cycle_times[$i][$m, $n], probability_of_success=>$m*$n/(n[$i])^2
         end m=1:ReactiveDynamics.ns[$i] n=1:ReactiveDynamics.ns[$i]
@@ -292,7 +292,7 @@ end
 Next we set up a simple dynamics and supply initial parameters.
 
 ```julia
-acs = @ReactionNetwork begin
+acs = @ReactionNetworkSchema begin
     function_to_learn(A, B, C, params), A --> B+C
     1., B --> C
     2., C --> B
diff --git a/src/ReactiveDynamics.jl b/src/ReactiveDynamics.jl
index b0eef6f..8a52ed1 100644
--- a/src/ReactiveDynamics.jl
+++ b/src/ReactiveDynamics.jl
@@ -1,6 +1,6 @@
 module ReactiveDynamics
 
-using Catlab, Catlab.CategoricalAlgebra, Catlab.Present
+using ACSets
 using Reexport
 using MacroTools
 using ComponentArrays
@@ -27,50 +27,50 @@ Base.@kwdef mutable struct FoldedObservable
     on::Vector{SampleableValues} = SampleableValues[]
 end
 
-@present TheoryReactionNetwork(FreeSchema) begin
-    (S, T)::Ob # species, transitions
-
-    (
-        SymbolicAttributeT,
-        DescriptiveAttributeT,
-        SampleableAttributeT,
-        ModalityAttributeT,
-        PcsOptT,
-        PrmAttributeT,
-    )::AttrType
-
-    specName::Attr(S, SymbolicAttributeT)
-    specModality::Attr(S, ModalityAttributeT)
-    specInitVal::Attr(S, SampleableAttributeT)
-    specInitUncertainty::Attr(S, SampleableAttributeT)
-    (specCost, specReward, specValuation)::Attr(S, SampleableAttributeT)
-
-    trans::Attr(T, SampleableAttributeT)
-    transPriority::Attr(T, SampleableAttributeT)
-    transRate::Attr(T, SampleableAttributeT)
-    transCycleTime::Attr(T, SampleableAttributeT)
-    transProbOfSuccess::Attr(T, SampleableAttributeT)
-    transCapacity::Attr(T, SampleableAttributeT)
-    transMaxLifeTime::Attr(T, SampleableAttributeT)
-    transPostAction::Attr(T, SampleableAttributeT)
-    transMultiplier::Attr(T, SampleableAttributeT)
-    transName::Attr(T, DescriptiveAttributeT)
-
-    E::Ob # events
-    (eventTrigger, eventAction)::Attr(E, SampleableAttributeT)
-
-    obs::Ob # processes (observables)
-    obsName::Attr(obs, SymbolicAttributeT)
-    obsOpts::Attr(obs, PcsOptT)
-
-    (P, M)::Ob # model params, solver args
-
-    prmName::Attr(P, SymbolicAttributeT)
-    prmVal::Attr(P, PrmAttributeT)
-
-    metaKeyword::Attr(M, SymbolicAttributeT)
-    metaVal::Attr(M, SampleableAttributeT)
-end
+TheoryReactionNetwork = BasicSchema(
+    [:S, :T, :E, :obs, :P, :M], # species, transitions, events, processes (observables), model params, solver args
+    [], # no homs
+    [
+        :SymbolicAttributeT,
+        :DescriptiveAttributeT,
+        :SampleableAttributeT,
+        :ModalityAttributeT,
+        :PcsOptT,
+        :PrmAttributeT,
+    ], # AttrTypes
+    [
+        # species
+        (:specName, :S, :SymbolicAttributeT),
+        (:specModality, :S, :ModalityAttributeT),
+        (:specInitVal, :S, :SampleableAttributeT),
+        (:specInitUncertainty, :S, :SampleableAttributeT),
+        (:specCost, :S, :SampleableAttributeT),
+        (:specReward, :S, :SampleableAttributeT),
+        (:specValuation, :S, :SampleableAttributeT),
+        # transitions
+        (:trans, :T, :SampleableAttributeT),
+        (:transPriority, :T, :SampleableAttributeT),
+        (:transRate, :T, :SampleableAttributeT),
+        (:transCycleTime, :T, :SampleableAttributeT),
+        (:transProbOfSuccess, :T, :SampleableAttributeT),
+        (:transCapacity, :T, :SampleableAttributeT),
+        (:transMaxLifeTime, :T, :SampleableAttributeT),
+        (:transPostAction, :T, :SampleableAttributeT),
+        (:transMultiplier, :T, :SampleableAttributeT),
+        (:transName, :T, :DescriptiveAttributeT),
+        # events
+        (:eventTrigger, :E, :SampleableAttributeT),
+        (:eventAction, :E, :SampleableAttributeT),
+        # observables
+        (:obsName, :obs, :SymbolicAttributeT),
+        (:obsOpts, :obs, :PcsOptT),
+        # params, args
+        (:prmName, :P, :SymbolicAttributeT),
+        (:prmVal, :P, :PrmAttributeT),
+        (:metaKeyword, :M, :SymbolicAttributeT),
+        (:metaVal, :M, :SampleableAttributeT),
+    ],
+)
 
 @acset_type FoldedReactionNetworkType(TheoryReactionNetwork)
 
@@ -93,6 +93,7 @@ Base.convert(::Type{Union{String,Symbol,Missing}}, ex::String) =
     end
 
 Base.convert(::Type{SampleableValues}, ex::String) = MacroTools.striplines(Meta.parse(ex))
+
 Base.convert(::Type{Set{Symbol}}, ex::String) = eval(Meta.parse(ex))
 Base.convert(::Type{FoldedObservable}, ex::String) = eval(Meta.parse(ex))
 
@@ -137,23 +138,18 @@ species_modalities = [:nonblock, :conserved, :rate]
 
 function assign_defaults!(acs::ReactionNetworkSchema)
     for (_, v_) in defargs, (k, v) in v_
-        for i = 1:length(subpart(acs, k))
-            isnothing(acs[i, k]) && (subpart(acs, k)[i] = v)
+        for i in dom_parts(acs, k)
+            isnothing(acs[i, k]) && (acs[i, k] = v)
         end
     end
 
     foreach(
-        i ->
-            !isnothing(acs[i, :specModality]) ||
-                (subpart(acs, :specModality)[i] = Set{Symbol}()),
-        1:nparts(acs, :S),
+        i -> !isnothing(acs[i, :specModality]) || (acs[i, :specModality] = Set{Symbol}()),
+        parts(acs, :S),
     )
     k = [:specCost, :specReward, :specValuation]
     foreach(
-        k -> foreach(
-            i -> !isnothing(acs[i, k]) || (subpart(acs, k)[i] = 0.0),
-            1:nparts(acs, :S),
-        ),
+        k -> foreach(i -> !isnothing(acs[i, k]) || (acs[i, k] = 0.0), parts(acs, :S)),
         k,
     )
 
diff --git a/src/compilers.jl b/src/compilers.jl
index 9edccc6..46cb842 100644
--- a/src/compilers.jl
+++ b/src/compilers.jl
@@ -164,7 +164,7 @@ function compile_attrs(acs::ReactionNetworkSchema)
     transitions[:transActivated] = fill(true, nparts(acs, :T))
     transitions[:transToSpawn] = zeros(nparts(acs, :T))
     transitions[:transHash] =
-        [coalesce(acs[i, :transName], gensym()) for i = 1:nparts(acs, :T)]
+        [coalesce(acs[i, :transName], gensym()) for i in parts(acs, :T)]
 
     return attrs, transitions, wrap_fun
 end
diff --git a/src/interface/update.jl b/src/interface/update.jl
index ce4e811..cf3c354 100644
--- a/src/interface/update.jl
+++ b/src/interface/update.jl
@@ -70,7 +70,7 @@ macro name_transition(acsex, exs...)
                 acs = $(esc(acsex))
                 ixs = findall(
                     i -> string(acs[i, :transName]) == $(string(ex.args[1])),
-                    1:nparts(acs, :T),
+                    parts(acs, :T),
                 )
                 foreach(i -> acs[i, :transName] = $(string(ex.args[2])), ixs)
             end
diff --git a/src/loadsave.jl b/src/loadsave.jl
index 89571fb..4002a24 100644
--- a/src/loadsave.jl
+++ b/src/loadsave.jl
@@ -28,7 +28,7 @@ function export_network(acs::ReactionNetworkSchema)
     dict = Dict()
     for (key, val) in objects_aliases
         push!(dict, val => [])
-        for i = 1:nparts(acs, key)
+        for i in parts(acs, key)
             dict_ = Dict()
             for attr in get_attrs(val)
                 attr_val = acs[i, Symbol(attr)]
@@ -44,13 +44,16 @@ function export_network(acs::ReactionNetworkSchema)
 end
 
 function load_network(dict::Dict)
-    acs = ReactionNetwork()
+    acs = ReactionNetworkSchema()
     for (key, val) in objects_aliases
         val == "prm" && continue
         for row in get(dict, val, [])
             i = add_part!(acs, key)
             for (attr, attrval) in row
                 set_subpart!(acs, i, Symbol(attr), attrval)
+                if (acs[i, Symbol(attr)] isa String && !(contains(attr, "name")))
+                    acs[i, Symbol(attr)] = MacroTools.striplines(Meta.parse(attrval))
+                end
             end
         end
     end
@@ -230,10 +233,10 @@ Export a solution as a `DataFrame`.
 ```
 """
 macro export_solution_as_table(solex, pathex = "sol.jld2")
-    return :(DataFrame($(esc(solex))))
+    return :(DataFrame($(esc(solex)).sol))
 end
 
-get_DataFrame(sol) = sol isa EnsembleSolution ? DataFrame(sol)[!, [:u, :t]] : DataFrame(sol)
+get_DataFrame(sol) = sol.sol
 
 """
     @export_solution_as_csv sol
diff --git a/src/operators/equalize.jl b/src/operators/equalize.jl
index e679ec9..c8e5724 100644
--- a/src/operators/equalize.jl
+++ b/src/operators/equalize.jl
@@ -27,7 +27,7 @@ function equalize!(acs::ReactionNetworkSchema, eqs = [])
         block_alias = findfirst(e -> e[1] == :alias, block)
         block_alias = !isnothing(block_alias) ? block[block_alias][2] : first(block)[2]
         species_ixs = Int64[]
-        for e in block, i = 1:nparts(acs, :S)
+        for e in block, i in parts(acs, :S)
             (
                 (i == e[2]) ||
                 (
diff --git a/src/operators/joins.jl b/src/operators/joins.jl
index f5c3276..5ac9808 100644
--- a/src/operators/joins.jl
+++ b/src/operators/joins.jl
@@ -10,7 +10,7 @@ Merge `acs2` onto `acs1`, the attributes in `acs2` taking precedence. Identify r
 function union_acs!(acs1, acs2, name = gensym("acs_"), eqs = [])
     acs2 = deepcopy(acs2)
     prepend!(acs2, name, eqs)
-    for i = 1:nparts(acs2, :S)
+    for i in parts(acs2, :S)
         inc = incident(acs1, acs2[i, :specName], :specName)
         isempty(inc) && (inc = add_part!(acs1, :S; specName = acs2[i, :specName]);
         assign_defaults!(acs1))
@@ -41,13 +41,13 @@ function union_acs!(acs1, acs2, name = gensym("acs_"), eqs = [])
         new_trans_ix,
     )
 
-    for i = 1:nparts(acs2, :P)
+    for i in parts(acs2, :P)
         inc = incident(acs1, acs2[i, :prmName], :prmName)
         isempty(inc) && (inc = add_part!(acs1, :P; prmName = acs2[i, :prmName]))
         !ismissing(acs2[i, :prmVal]) && (acs1[first(inc), :prmVal] = acs2[i, :prmVal])
     end
 
-    for i = 1:nparts(acs2, :M)
+    for i in parts(acs2, :M)
         inc = incident(acs1, acs2[i, :metaKeyword], :metaKeyword)
         isempty(inc) && (inc = add_part!(acs1, :M; metaKeyword = acs2[i, :metaKeyword]))
         !ismissing(acs2[i, :metaVal]) && (acs1[first(inc), :metaVal] = acs2[i, :metaVal])
@@ -61,7 +61,7 @@ Prepend species names with a model identifier (unless a global species name).
 """
 function prepend!(acs::ReactionNetworkSchema, name = gensym("acs"), eqs = [])
     specmap = Dict()
-    for i = 1:nparts(acs, :S)
+    for i in parts(acs, :S)
         new_name = normalize_name(name, i, acs[i, :specName], eqs)
         push!(specmap, acs[i, :specName] => (acs[i, :specName] = new_name))
     end
diff --git a/src/solvers.jl b/src/solvers.jl
index f53b001..270e8c7 100644
--- a/src/solvers.jl
+++ b/src/solvers.jl
@@ -132,11 +132,11 @@ function evolve!(state)
 
     foreach(
         i -> qs[i] = state[i, :transRate] * state[i, :transMultiplier],
-        1:nparts(state, :T),
+        parts(state, :T),
     )
     qs .= ceil.(Ref(Int), qs)
 
-    for i = 1:nparts(state, :T)
+    for i in parts(state, :T)
         new_instances = qs[i] + state[i, :transToSpawn]
         capacity =
             state[i, :transCapacity] -
@@ -164,7 +164,7 @@ function evolve!(state)
     actual_allocs .+= sum(allocs; dims = 2)
 
     # add spawned transitions to the heap
-    for i = 1:nparts(state, :T)
+    for i in parts(state, :T)
         qs[i] != 0 && push!(
             state.ongoing_transitions,
             Transition(
@@ -215,14 +215,14 @@ function evolve!(state)
         (
             :valuation_cost,
             state.t,
-            actual_allocs' * [state[i, :specCost] for i = 1:nparts(state, :S)],
+            actual_allocs' * [state[i, :specCost] for i in parts(state, :S)],
         ),
     )
 end
 
 # execute callbacks
 function event_action!(state)
-    for i = 1:nparts(state, :E)
+    for i in parts(state, :E)
         !isnothing(state[i, :eventTrigger]) && !isnothing(state[i, :eventAction]) ||
             continue
         v = state[i, :eventTrigger]
@@ -280,10 +280,10 @@ function finish!(state)
         )
 
         context_eval(state, trans_.trans[:transPostAction])
-        terminated_all[trans_[:transHash]] =
-            get(terminated_all, trans_[:transHash], 0) + trans_.q
-        terminated_success[trans_[:transHash]] =
-            get(terminated_success, trans_[:transHash], 0) + q
+        terminated_all[Symbol(trans_[:transHash])] =
+            get(terminated_all, Symbol(trans_[:transHash]), 0) + trans_.q
+        terminated_success[Symbol(trans_[:transHash])] =
+            get(terminated_success, Symbol(trans_[:transHash]), 0) + q
 
         ix += 1
     end
@@ -324,7 +324,7 @@ function ReactionNetworkProblem(
 )
     assign_defaults!(acs)
     keywords = Dict{Symbol,Any}([
-        acs[i, :metaKeyword] => acs[i, :metaVal] for i = 1:nparts(acs, :M) if
+        acs[i, :metaKeyword] => acs[i, :metaVal] for i in parts(acs, :M) if
         !isnothing(acs[i, :metaKeyword]) && !isnothing(acs[i, :metaVal])
     ])
     merge!(keywords, Dict(collect(kwargs)))
@@ -337,7 +337,7 @@ function ReactionNetworkProblem(
     transition_recipes = transitions
     u0_init = zeros(nparts(acs, :S))
 
-    for i = 1:nparts(acs, :S)
+    for i in parts(acs, :S)
         if !isnothing(acs[i, :specName]) && haskey(u0, acs[i, :specName])
             u0_init[i] = u0[acs[i, :specName]]
         else
@@ -345,6 +345,13 @@ function ReactionNetworkProblem(
         end
     end
 
+    prms = Dict{Symbol,Any}((
+        acs[i, :prmName] => acs[i, :prmVal] for
+        i in Iterators.filter(i -> !isnothing(acs[i, :prmVal]), 1:nparts(acs, :P))
+    ))
+
+    merge!(p, prms)
+
     ongoing_transitions = Transition[]
     log = NamedTuple[]
     observables = compile_observables(acs)
@@ -406,7 +413,7 @@ function AlgebraicAgents._step!(state::ReactionNetworkProblem)
         (
             :valuation,
             state.t,
-            state.u' * [state[i, :specValuation] for i = 1:nparts(state, :S)],
+            state.u' * [state[i, :specValuation] for i in parts(state, :S)],
         ),
     )
 
@@ -421,6 +428,6 @@ end
 function fetch_params(acs::ReactionNetworkSchema)
     return Dict{Symbol,Any}((
         acs[i, :prmName] => acs[i, :prmVal] for
-        i in Iterators.filter(i -> !isnothing(acs[i, :prmVal]), 1:nparts(acs, :P))
+        i in Iterators.filter(i -> !isnothing(acs[i, :prmVal]), parts(acs, :P))
     ))
 end
diff --git a/src/state.jl b/src/state.jl
index 8d43eef..97e85db 100644
--- a/src/state.jl
+++ b/src/state.jl
@@ -70,7 +70,7 @@ end
 
 function init_u!(state::ReactionNetworkProblem)
     return (u = fill(0.0, nparts(state, :S));
-    foreach(i -> u[i] = state[i, :specInitVal], 1:nparts(state, :S));
+    foreach(i -> u[i] = state[i, :specInitVal], parts(state, :S));
     state.u = u)
 end
 save!(state::ReactionNetworkProblem) = push!(state.sol, (state.t, state.u[:]...))
@@ -91,7 +91,10 @@ function compile_observables(acs::ReactionNetworkSchema)
             opts.range,
         )
 
-        push!(observables, name => Observable(name, -Inf, range, opts.every, on, missing))
+        push!(
+            observables,
+            name => Observable(string(name), -Inf, range, opts.every, on, missing),
+        )
     end
 
     return observables
@@ -131,7 +134,7 @@ end
 
 function prune_r_line(r_line)
     return if r_line isa Expr && r_line.args[1] ∈ fwd_arrows
-        r_line.args[2:3]
+        r_line.args[[2, 3]]
     elseif r_line isa Expr && r_line.args[1] ∈ bwd_arrows
         r_line.args[[3, 2]]
     elseif isexpr(r_line, :macrocall) && (macroname(r_line) == :choose)
@@ -149,7 +152,7 @@ function prune_r_line(r_line)
 end
 
 function find_index(species::Symbol, state::ReactionNetworkProblem)
-    return findfirst(i -> state[i, :specName] == species, 1:nparts(state, :S))
+    return findfirst(i -> state[i, :specName] == species, parts(state, :S))
 end
 
 function sample_transitions!(state::ReactionNetworkProblem)
@@ -195,8 +198,12 @@ function as_state(u, t, state::ReactionNetworkProblem)
     return (state = deepcopy(state); state.u .= u; state.t = t; state)
 end
 
-function Catlab.CategoricalAlgebra.nparts(state::ReactionNetworkProblem, obj::Symbol)
-    return obj == :T ? length(state.transitions[:transLHS]) : nparts(state.acs, obj)
+function ACSets.ACSetInterface.nparts(state::ReactionNetworkProblem, obj::Symbol)
+    return nparts(state.acs, obj)
+end
+
+function ACSets.ACSetInterface.parts(state::ReactionNetworkProblem, obj::Symbol)
+    return parts(state.acs, obj)
 end
 
 ## query the state
@@ -209,8 +216,8 @@ state(state::ReactionNetworkProblem) = state
 
 function periodic(state::ReactionNetworkProblem, period)
     return period == 0.0 || (
-        length(state.history_t) > 1 &&
-        (fld(state.t, period) - fld(state.history_t[end-1], period) > 0)
+        length(state.sol.t) > 1 &&
+        (fld(state.t, period) - fld(state.sol.t[end-1], period) > 0)
     )
 end
 
diff --git a/test/tutorial_tests.jl b/test/tutorial_tests.jl
index 03e4215..5f55109 100644
--- a/test/tutorial_tests.jl
+++ b/test/tutorial_tests.jl
@@ -1,5 +1,7 @@
 using ReactiveDynamics
 
+include("safeinclude.jl")
+
 @safeinclude "example" "../tutorial/example.jl"
 @safeinclude "joins" "../tutorial/joins/joins.jl"
 @safeinclude "loadsave" "../tutorial/loadsave/loadsave.jl"
diff --git a/tutorial/example.jl b/tutorial/example.jl
index b7fb3d1..c41c1cc 100644
--- a/tutorial/example.jl
+++ b/tutorial/example.jl
@@ -1,7 +1,7 @@
 using ReactiveDynamics
 
 # acs as a model : incomplete dynamics
-sir_acs = @ReactionNetwork
+sir_acs = @ReactionNetworkSchema
 
 # set up ontology: try ?@aka
 @aka sir_acs transition = reaction species = population_group
@@ -30,18 +30,14 @@ u0 = [999, 10, 0] # alternative specification
 @prob_params sir_acs β = 0.0001 ν = 0.01 γ = 5
 @prob_meta sir_acs tspan = 100
 
-#prob = @problematize sir_acs
-prob = @problematize sir_acs tspan = 200
+prob = ReactionNetworkProblem(sir_acs; tspan = 200)
 
-sol = @solve prob trajectories = 20
-@plot sol plot_type = summary
-sol = @solve prob
-@plot sol plot_type = allocation
-@plot sol plot_type = valuations
-@plot sol plot_type = new_transitions
+sol = simulate(prob)
+
+draw(prob)
 
 # acs as a model : incomplete dynamics
-sir_acs = @ReactionNetwork
+sir_acs = @ReactionNetworkSchema
 
 # set up ontology: try ?@aka
 @aka sir_acs transition = reaction species = population_group
@@ -65,7 +61,7 @@ u0 = [999, 10, 0] # alternative specification
 @prob_params sir_acs β = 0.0001 ν = 0.01 γ = 5
 
 # model dynamics
-sir_acs = @ReactionNetwork begin
+sir_acs = @ReactionNetworkSchema begin
     α * S * I, S + I --> 2I, cycle_time => 0, name => I2R
     β * I, I --> R, cycle_time => 0, name => R2S
 end
@@ -77,16 +73,14 @@ end
 @prob_meta sir_acs tspan = 250 dt = 0.1
 
 # batch simulation
-prob = @problematize sir_acs
-sol = @solve prob trajectories = 20
-@plot sol plot_type = summary
-@plot sol plot_type = summary show = :S
-@plot sol plot_type = summary c = :green xlimits = (0.0, 100.0)
+prob = ReactionNetworkProblem(sir_acs)
+sol = simulate(prob)
+draw(prob)
 
-acs_1 = @ReactionNetwork begin
+acs_1 = @ReactionNetworkSchema begin
     1.0, A --> B + C
 end
-acs_2 = @ReactionNetwork begin
+acs_2 = @ReactionNetworkSchema begin
     1.0, A --> B + C
 end
 
diff --git a/tutorial/joins/joins.jl b/tutorial/joins/joins.jl
index ee87d7a..e7789a9 100644
--- a/tutorial/joins/joins.jl
+++ b/tutorial/joins/joins.jl
@@ -2,7 +2,7 @@ using ReactiveDynamics
 ## setup the environment
 n_models = 5;
 r = 2; # number of submodels, resources
-rd_models = ReactiveDynamics.ReactionNetwork[] # submodels
+rd_models = ReactiveDynamics.ReactionNetworkSchema[] # submodels
 
 @register begin
     ns = Int[] # size of submodels
@@ -51,8 +51,6 @@ u0 = rand(1:1000, nparts(rd_model, :S))
 
 @prob_meta rd_model tspan = 10
 
-prob = @problematize rd_model
-sol = @solve prob trajectories = 2
-
-# plot "state" species only
-@plot sol plot_type = summary show = r"state"
+prob = ReactionNetworkProblem(sir_acs)
+sol = simulate(prob)
+draw(sol)
diff --git a/tutorial/joins/submodel.jl b/tutorial/joins/submodel.jl
index 1e8fc43..faf370a 100644
--- a/tutorial/joins/submodel.jl
+++ b/tutorial/joins/submodel.jl
@@ -13,7 +13,7 @@ end
 # generate submodel dynamics
 push!(
     rd_models,
-    @ReactionNetwork begin
+    @ReactionNetworkSchema begin
         M[$i][$m, $n],
         state[$m] + {demand[$i][$m, $n, $l] * resource[$l], l = 1:($r), dlm = +} -->
         state[$n] + {production[$i][$m, $n, $l] * resource[$l], l = 1:($r), dlm = +},
diff --git a/tutorial/loadsave/loadsave.jl b/tutorial/loadsave/loadsave.jl
index 23d2be7..d6c8ee6 100644
--- a/tutorial/loadsave/loadsave.jl
+++ b/tutorial/loadsave/loadsave.jl
@@ -5,15 +5,15 @@ using ReactiveDynamics
 @assert @isdefined sir_acs
 @assert isdefined(ReactiveDynamics, :tdecay)
 
-prob = @problematize sir_acs
-sol = @solve prob trajectories = 20
+prob = ReactionNetworkProblem(sir_acs)
+sol = simulate(prob)
 
 @import_network "csv/model.csv" sir_acs_
 @assert @isdefined sir_acs_
 @assert isdefined(ReactiveDynamics, :foo)
 
-prob_ = @problematize sir_acs_
-sol_ = @solve prob_ trajectories = 20
+prob_ = ReactionNetworkProblem(sir_acs_)
+sol_ = simulate(prob_)
 
 # export, import the solution
 @export_solution sol
diff --git a/tutorial/optimize/optimize.jl b/tutorial/optimize/optimize.jl
index 0766369..d8af528 100644
--- a/tutorial/optimize/optimize.jl
+++ b/tutorial/optimize/optimize.jl
@@ -1,7 +1,7 @@
 using ReactiveDynamics
 
 # solve for steady state
-acss = @ReactionNetwork begin
+acss = @ReactionNetworkSchema begin
     3.0, A --> A, priority => 0.6, name => aa
     1.0, B + 0.2 * A --> 2 * α * B, prob => 0.7, priority => 0.6, name => bb
     3.0, A + 2 * B --> 2 * C, prob => 0.7, priority => 0.7, name => cc
diff --git a/tutorial/optimize/optimize_custom.jl b/tutorial/optimize/optimize_custom.jl
index 47dc489..a9cb780 100644
--- a/tutorial/optimize/optimize_custom.jl
+++ b/tutorial/optimize/optimize_custom.jl
@@ -8,7 +8,7 @@ using ReactiveDynamics
     end
 end
 
-acs = @ReactionNetwork begin
+acs = @ReactionNetworkSchema begin
     function_to_learn(A, B, C, params), A --> B + C
     1.0, B --> C
     2.0, C --> B
@@ -35,7 +35,7 @@ data = [60 30 5]
         return [A, B, C]' * params + α # params: 3-element vector
     end
 end
-acs = @ReactionNetwork begin
+acs = @ReactionNetworkSchema begin
     learnt_function(A, B, C, params, α), A --> B + C, priority => 0.6
     1.0, B --> C
     2.0, C --> B
diff --git a/tutorial/rd_example.jl b/tutorial/rd_example.jl
index 64638b3..a9b1644 100644
--- a/tutorial/rd_example.jl
+++ b/tutorial/rd_example.jl
@@ -15,7 +15,7 @@ rd_model = @ReactionNetwork
     transitions .+= 0.1 * rand(n_phase, n_phase)
 end
 
-rd_model = @ReactionNetwork begin
+rd_model = @ReactionNetworkSchema begin
     $i, phase[$i] --> phase[$j], cycle_time => $i * $j
 end i = 1:3 j = 1:($i)
 
@@ -46,7 +46,7 @@ sol = @solve prob trajectories = 20
     resource = rand(1:10, k, k, r)
 end
 
-rd_model = @ReactionNetwork begin
+rd_model = @ReactionNetworkSchema begin
     M[$i, $j],
     mod[$i] +
     {resource[$i, $j, $k] * resource[$k], k = rand(1:(ReactiveDynamics.r)), dlm = +} -->
diff --git a/tutorial/toy_pharma_model.jl b/tutorial/toy_pharma_model.jl
index 4fc6c7e..e536d19 100644
--- a/tutorial/toy_pharma_model.jl
+++ b/tutorial/toy_pharma_model.jl
@@ -1,7 +1,7 @@
 using ReactiveDynamics
 
 # model dynamics
-toy_pharma_model = @ReactionNetwork begin
+toy_pharma_model = @ReactionNetworkSchema begin
     α(candidate_compound, marketed_drug, κ),
     3 * @conserved(scientist) + @rate(budget) --> candidate_compound,
     name => discovery,
@@ -35,20 +35,16 @@ end
 ## other arguments passed to the solver
 @prob_meta toy_pharma_model tspan = 250 dt = 0.1
 
-prob = @problematize toy_pharma_model
+prob = ReactionNetworkProblem(toy_pharma_model)
 
-sol = @solve prob trajectories = 20
+sol = simulate(prob)
 
-using Plots
-
-@plot sol plot_type = summary
-
-@plot sol plot_type = summary show = :marketed_drug
+draw(sol)
 
 ## for deterministic rates 
 
 # model dynamics
-toy_pharma_model = @ReactionNetwork begin
+toy_pharma_model = @ReactionNetworkSchema begin
     @per_step(α(candidate_compound, marketed_drug, κ)),
     3 * @conserved(scientist) + @rate(budget) --> candidate_compound,
     name => discovery,
@@ -82,12 +78,8 @@ end
 ## other arguments passed to the solver
 @prob_meta toy_pharma_model tspan = 250
 
-prob = @problematize toy_pharma_model
-
-sol = @solve prob trajectories = 20
-
-using Plots
+prob = ReactionNetworkProblem(toy_pharma_model)
 
-@plot sol plot_type = summary
+sol = simulate(prob)
 
-@plot sol plot_type = summary show = :marketed_drug
+draw(sol)