From 9c491fcfd3fcd1f6a5b151587c677659266d4b07 Mon Sep 17 00:00:00 2001 From: Thomas Purdy Date: Tue, 5 Sep 2023 20:11:40 -0600 Subject: [PATCH 1/7] Foot syntax returns errors; changed \@foot and \@feet macros to return quote blocks of the call, rather than directly calling the functions to create feet. Removed testing code. --- src/Syntax.jl | 25 +++++++++++++++---------- test/Syntax.jl | 24 +++++++++++------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/Syntax.jl b/src/Syntax.jl index 457d59ee..a3c18092 100644 --- a/src/Syntax.jl +++ b/src/Syntax.jl @@ -949,8 +949,10 @@ Create a foot with S => N syntax, where S is stock, N is sum variable. ``` """ macro foot(block::Expr) - Base.remove_linenums!(block) - return create_foot(block) + escaped_block = Expr(:quote, block) + quote + create_foot($(escaped_block)) + end end @@ -974,11 +976,15 @@ end """ macro feet(block::Expr) Base.remove_linenums!(block) - @match block begin - quote - $((block...)) - end => map(create_foot, block) # also matches empty - Expr(e, _...) => [create_foot(block)] # this also matches the above, so it's necessary this comes second. + escaped_block = Expr(:quote, block) + quote + inner_block = $escaped_block + @match inner_block begin + quote + $((inner_block...)) + end => map(create_foot, inner_block) # also matches empty + Expr(e, _...) => [create_foot(inner_block)] # this also matches the above, so it's necessary this comes second. + end end end @@ -1002,10 +1008,9 @@ multiple_links = @foot A => B, A => B # will have two links from A to B. """ function create_foot(block::Expr) @match block.head begin - :tuple => begin - if isempty(block.args) # case for create_foot(:()) - error("Cannot create foot with no arguments.") + if length(block.args) < 2 # case for create_foot(:()) + return error("Cannot create foot with no arguments.") end foot_s = Vector{Symbol}() foot_sv = Vector{Symbol}() diff --git a/test/Syntax.jl b/test/Syntax.jl index 34f12526..67b9a2ad 100644 --- a/test/Syntax.jl +++ b/test/Syntax.jl @@ -288,16 +288,14 @@ end end @testset "foot syntax disallows invalid feet" begin # note, @feet calls create_foot for each line, so this should apply to both @foot and @feet - @test_throws Exception create_foot(:(A => B => C)) # Invalid syntax for second argument of foot: B => C - @test_throws Exception create_foot(:(oooo2 + f => C)) # Invalid syntax for first argument of foot: oooo2 + f - @test_throws Exception create_foot(:(A + B)) # Invalid syntax function for foot: + - @test_throws Exception create_foot(:(=>)) # no method matching create_foot(::Symbol) - @test_throws Exception create_foot(:(=>(A, B, C, D))) - @test_throws Exception create_foot(:()) - @test_throws Exception create_foot(:(([]) => ())) - - @test_throws Exception create_foot(:(A => B, P => Q, C)) - @test_throws Exception create_foot(:(() => E, () => (K,))) + @test_throws ErrorException @foot A => B => C # Invalid syntax for second argument of foot: B => C + @test_throws ErrorException @foot oooo2 + f => C # Invalid syntax for first argument of foot: oooo2 + f + @test_throws ErrorException @foot A + B # Invalid syntax function for foot: + + @test_throws ErrorException @foot =>(A, B, C, D) + @test_throws ErrorException @foot () + @test_throws ErrorException @foot ([]) => () + @test_throws ErrorException @foot A => B, P => Q, C + @test_throws ErrorException @foot () => E, () => (K,) end @@ -339,7 +337,7 @@ end end @testset "feet syntax fails on invalid feet" begin # mostly to check that an exception is thrown even if some of the feet are valid. - @test_throws Exception @eval @feet A => B => C # eval required so the errors occur at runtime, rather than at compilation - @test_throws Exception @eval @feet begin A => B; =>(D,E,F) end - @test_throws Exception @eval @feet begin A => B; 1 => 2; end + @test_throws ErrorException @feet A => B => C # eval required so the errors occur at runtime, rather than at compilation + @test_throws ErrorException @feet begin A => B; =>(D,E,F) end + @test_throws ErrorException @feet begin A => B; 1 => 2; end end From cc6fc13a60c83b365d05d5e9b61de1eef40f21b5 Mon Sep 17 00:00:00 2001 From: Thomas Purdy Date: Tue, 5 Sep 2023 21:11:32 -0600 Subject: [PATCH 2/7] @foot A => B, P => Q, C now has the correct error specified (method error, since it's trying to call a match_foot_format on :C, where the function is only defined on expressions.) removed testing code --- test/Syntax.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Syntax.jl b/test/Syntax.jl index 67b9a2ad..1c151691 100644 --- a/test/Syntax.jl +++ b/test/Syntax.jl @@ -294,7 +294,7 @@ end @test_throws ErrorException @foot =>(A, B, C, D) @test_throws ErrorException @foot () @test_throws ErrorException @foot ([]) => () - @test_throws ErrorException @foot A => B, P => Q, C + @test_throws MethodError @foot A => B, P => Q, C # Issue here is it tries calling match_foot_format with a symbol @test_throws ErrorException @foot () => E, () => (K,) end From 8720599818df119abf21b2398402e9f81a2f7890 Mon Sep 17 00:00:00 2001 From: Thomas Purdy Date: Tue, 26 Sep 2023 22:02:13 -0600 Subject: [PATCH 3/7] @feet with no arguments gives an empty Vector{StockAndFlow0}. Errors in @foot and @feet no longer occur at the macro replace stage. --- src/Syntax.jl | 9 +++++++-- test/Syntax.jl | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Syntax.jl b/src/Syntax.jl index 0f4c89e5..b14566bb 100644 --- a/src/Syntax.jl +++ b/src/Syntax.jl @@ -990,6 +990,11 @@ macro feet(block::Expr) end end +macro feet() + quote + Vector{StockAndFlow0}() + end +end """ create_foot(block :: Expr) @@ -1011,8 +1016,8 @@ multiple_links = @foot A => B, A => B # will have two links from A to B. function create_foot(block::Expr) @match block.head begin :tuple => begin - if length(block.args) < 2 # case for create_foot(:()) - return error("Cannot create foot with no arguments.") + if isempty(block.args) # case for create_foot(:()) + error("Cannot create foot with zero arguments.") end foot_s = Vector{Symbol}() foot_sv = Vector{Symbol}() diff --git a/test/Syntax.jl b/test/Syntax.jl index 64ee0308..22c55e64 100755 --- a/test/Syntax.jl +++ b/test/Syntax.jl @@ -2,7 +2,7 @@ using Base: is_unary_and_binary_operator using Test using StockFlow using StockFlow.Syntax -using StockFlow.Syntax: is_binop_or_unary, sum_variables, infix_expression_to_binops, fnone_value_or_vector, extract_function_name_and_args_expr, is_recursive_dyvar, create_foot, apply_flags, substitute_symbols +using StockFlow.Syntax: is_binop_or_unary, sum_variables, infix_expression_to_binops, fnone_value_or_vector, extract_function_name_and_args_expr, is_recursive_dyvar, create_foot, apply_flags, substitute_symbols, DSLArgument @testset "Stratification DSL" begin include("syntax/Stratification.jl") @@ -342,10 +342,11 @@ end foot(:J, (:K, :Q), (:J => :K, :J => :Q)) ] + @test (@feet ) == Vector{StockAndFlow0}(); end @testset "feet syntax fails on invalid feet" begin # mostly to check that an exception is thrown even if some of the feet are valid. - @test_throws ErrorException @feet A => B => C # eval required so the errors occur at runtime, rather than at compilation + @test_throws ErrorException @feet A => B => C @test_throws ErrorException @feet begin A => B; =>(D,E,F) end @test_throws ErrorException @feet begin A => B; 1 => 2; end end From 7d607c65ddf385e7074cdf0a9b7071f52115d605 Mon Sep 17 00:00:00 2001 From: Thomas Purdy Date: Tue, 26 Sep 2023 22:30:52 -0600 Subject: [PATCH 4/7] empty commit From 35260b2dd05095bd6b39339b2782445d711c56b8 Mon Sep 17 00:00:00 2001 From: Micah Halter Date: Thu, 26 Oct 2023 11:59:35 -0400 Subject: [PATCH 5/7] update default branch to `main` --- .appveyor.yml | 2 +- .github/workflows/julia_ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index f7f514dc..39e05537 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -13,7 +13,7 @@ matrix: - julia_version: nightly branches: only: - - master + - main - /release-.*/ notifications: - provider: Email diff --git a/.github/workflows/julia_ci.yml b/.github/workflows/julia_ci.yml index 0052545d..9dabe35f 100644 --- a/.github/workflows/julia_ci.yml +++ b/.github/workflows/julia_ci.yml @@ -4,7 +4,7 @@ on: schedule: - cron: 0 0 * * * push: - branches: ["master"] + branches: ["main"] tags: ["*"] pull_request: workflow_dispatch: From a445a2f6e9767c9549f6a2b8cf5fecdfddd3297a Mon Sep 17 00:00:00 2001 From: Micah Halter Date: Thu, 26 Oct 2023 12:03:02 -0400 Subject: [PATCH 6/7] update badges --- README.md | 136 +++++++++++++++++++++++++++--------------------------- 1 file changed, 67 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 3dab3879..b886b636 100644 --- a/README.md +++ b/README.md @@ -1,81 +1,79 @@ # AlgebraicStockFlow [![Stable Documentation](https://img.shields.io/badge/docs-stable-blue.svg)](https://algebraicjulia.github.io/StockFlow.jl/dev/) -[![Tests](https://github.com/AlgebraicJulia/StockFlow.jl/actions/workflows/tests.yml/badge.svg)](https://github.com/AlgebraicJulia/StockFlow.jl/actions/workflows/tests.yml) - +[![CI/CD](https://github.com/AlgebraicJulia/StockFlow.jl/actions/workflows/julia_ci.yml/badge.svg)](https://github.com/AlgebraicJulia/StockFlow.jl/actions/workflows/julia_ci.yml) +[![Code Coverage](https://codecov.io/gh/AlgebraicJulia/StockFlow.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/AlgebraicJulia/StockFlow.jl) StockFlow.jl is a Julia library for the creation and execution of [stock and flow modeling diagrams](https://en.wikipedia.org/wiki/System_dynamics#Stock_and_flow_diagrams), designed with model composition and stratification in mind through a [categorical approach](https://arxiv.org/abs/2211.01290). -Stock-flow diagrams are used to represent systems where its population can move between different states, such as tracking how many people have been infected or recovered from a disease. By providing initial values, and values for the parameters, we can solve the differential equation which the stock-flow diagram represents, which gives us a graph for how the population varies over time. +Stock-flow diagrams are used to represent systems where its population can move between different states, such as tracking how many people have been infected or recovered from a disease. By providing initial values, and values for the parameters, we can solve the differential equation which the stock-flow diagram represents, which gives us a graph for how the population varies over time. In this particular schema, stock-flow diagrams consist of stocks, flows, dynamic variables, sum variables, parameters, and the links between them. -* Stocks represent accumulations of a population in a particular state, such as 'susceptible', 'infected', 'recovered', etc. -* Flows represent transitions between states. A flow can go from a stock to a stock, from nothing to a stock, or from a stock to nothing, the latter two representing a population entering or leaving a model. Flows are dependent on a dynamic variable, which determines its rate. -* Dynamic variables are the equations which determine flow rates. For instance, if the rate at which people move from uninfected to infected is the uninfected stock S times the parameter c, we can say v\_infectionRate = c * S, and use this as a flow variable. Dynamic variables can contain stocks, sum variables, parameters and other dynamic variables (but the definition can't be circular; you can't set a dynamic variable equal to itself, for instance). Currently supports common binary and unary operations such as addition, multiplication, log, exp. -* Sum variables represent the sum of a subpopulation. The value of a sum variable is the sum of all the stocks which link to it. Common examples include the sum of the entire population N, the sum of infected individuals NI, or the sum of a particular subpopulation NS. -* Parameters are variables for which the particular values are defined outside the model definition. You can provide parameters and initial values, then solve the differential equation. +- Stocks represent accumulations of a population in a particular state, such as 'susceptible', 'infected', 'recovered', etc. +- Flows represent transitions between states. A flow can go from a stock to a stock, from nothing to a stock, or from a stock to nothing, the latter two representing a population entering or leaving a model. Flows are dependent on a dynamic variable, which determines its rate. +- Dynamic variables are the equations which determine flow rates. For instance, if the rate at which people move from uninfected to infected is the uninfected stock S times the parameter c, we can say v_infectionRate = c \* S, and use this as a flow variable. Dynamic variables can contain stocks, sum variables, parameters and other dynamic variables (but the definition can't be circular; you can't set a dynamic variable equal to itself, for instance). Currently supports common binary and unary operations such as addition, multiplication, log, exp. +- Sum variables represent the sum of a subpopulation. The value of a sum variable is the sum of all the stocks which link to it. Common examples include the sum of the entire population N, the sum of infected individuals NI, or the sum of a particular subpopulation NS. +- Parameters are variables for which the particular values are defined outside the model definition. You can provide parameters and initial values, then solve the differential equation. Stock-flow diagrams can be created and manipulated using the `StockFlow` and `StockFlow.Syntax` modules, the latter of which includes the domain specific language for easier creation. -Stock-flow diagrams can be composed and stratified - that is, combined on stocks and sum variables or split into subpopulations. One can also do algebraic rewriting for more manual, specific fixes, such as preventing a dynamic variable from unnecessarily being computed twice, or substituting one parameter for another. - -Examples of the domain specific language, composition, stratification and algebraic rewriting can be seen in the [examples](examples) folder. In particular, full\_fledged\_schema\_examples\_new contains examples which use the domain specific language. - - - - ## Example interpretation of a stock and flow diagram using an ODE solver - - From the [SEIR Composition Example](examples/full_fledged_schema_examples_new/composition/SEIR_full_model_measles_chickenpox.ipynb): - - ```julia - seir = @stock_and_flow begin - :stocks - S - E - I - R - - :parameters - μ - β - tlatent - trecovery - δ - - :flows - ☁ => fbirth(μ * N) => S # dynamic variables can be defined implicitly or with :dynamic_variables - S => fincid(β * S * I / N) => E - S => fdeathS(S * δ) => ☁ - E => finf(E / tlatent) => I - E => fdeathE(E * δ) => ☁ - I => frec(I / trecovery) => R - I => fdeathI(I * δ) => ☁ - R => fdeathR(R * δ) => ☁ - - :sums - N = [S, E, I, R] - - end - - # define parameter values and initial values of stocks - # define constant parameters - p_measles = LVector( - β=49.598, μ=0.03/12, δ=0.03/12, tlatent=8.0/30, trecovery=5.0/30 - ) - - # define initial values for stocks - u0_measles = LVector( - S=90000.0-930.0, E=0.0, I=930.0, R=773545.0 - ) - - prob_measles = ODEProblem(vectorfield(seir),u0_measles,(0.0,120.0),p_measles); - sol_measles = solve(prob_measles,Tsit5(),abstol=1e-8); - plot(sol_measles) - ``` - - See the full example for additional comments and the chickenpox model. - ## Note - - This library is under active development and does not yet have an official release. - +Stock-flow diagrams can be composed and stratified - that is, combined on stocks and sum variables or split into subpopulations. One can also do algebraic rewriting for more manual, specific fixes, such as preventing a dynamic variable from unnecessarily being computed twice, or substituting one parameter for another. + +Examples of the domain specific language, composition, stratification and algebraic rewriting can be seen in the [examples](examples) folder. In particular, full_fledged_schema_examples_new contains examples which use the domain specific language. + +## Example interpretation of a stock and flow diagram using an ODE solver + +From the [SEIR Composition Example](examples/full_fledged_schema_examples_new/composition/SEIR_full_model_measles_chickenpox.ipynb): + +```julia +seir = @stock_and_flow begin + :stocks + S + E + I + R + + :parameters + μ + β + tlatent + trecovery + δ + + :flows + ☁ => fbirth(μ * N) => S # dynamic variables can be defined implicitly or with :dynamic_variables + S => fincid(β * S * I / N) => E + S => fdeathS(S * δ) => ☁ + E => finf(E / tlatent) => I + E => fdeathE(E * δ) => ☁ + I => frec(I / trecovery) => R + I => fdeathI(I * δ) => ☁ + R => fdeathR(R * δ) => ☁ + + :sums + N = [S, E, I, R] + +end + +# define parameter values and initial values of stocks +# define constant parameters +p_measles = LVector( + β=49.598, μ=0.03/12, δ=0.03/12, tlatent=8.0/30, trecovery=5.0/30 +) + +# define initial values for stocks +u0_measles = LVector( + S=90000.0-930.0, E=0.0, I=930.0, R=773545.0 +) + +prob_measles = ODEProblem(vectorfield(seir),u0_measles,(0.0,120.0),p_measles); +sol_measles = solve(prob_measles,Tsit5(),abstol=1e-8); +plot(sol_measles) +``` + +See the full example for additional comments and the chickenpox model. + +## Note + +This library is under active development and does not yet have an official release. From e2b3ca8e03630e6f2546a70de9947f0340f92167 Mon Sep 17 00:00:00 2001 From: Micah Halter Date: Mon, 30 Oct 2023 09:52:29 -0400 Subject: [PATCH 7/7] CI: update TagBot --- .github/workflows/TagBot.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index f49313b6..b2e776dd 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -4,6 +4,22 @@ on: types: - created workflow_dispatch: + inputs: + lookback: + default: 3 +permissions: + actions: read + checks: read + contents: write + deployments: read + issues: read + discussions: read + packages: read + pages: read + pull-requests: read + repository-projects: read + security-events: read + statuses: read jobs: TagBot: if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' @@ -11,5 +27,4 @@ jobs: steps: - uses: JuliaRegistries/TagBot@v1 with: - token: ${{ secrets.GITHUB_TOKEN }} - ssh: ${{ secrets.DOCUMENTER_KEY }} + token: ${{ secrets.SERVICE_TOKEN }}