|
8 | 8 | ParameterizedFunctions.jl is a component of the SciML ecosystem which allows |
9 | 9 | for easily defining parameterized ODE models in a simple syntax. |
10 | 10 |
|
11 | | -## Basic Usage |
| 11 | +## Tutorials and Documentation |
12 | 12 |
|
13 | | -### ODE Macros |
| 13 | +For information on using the package, |
| 14 | +[see the stable documentation](https://parameterizedfunctions.sciml.ai/stable/). Use the |
| 15 | +[in-development documentation](https://parameterizedfunctions.sciml.ai/dev/) for the version of |
| 16 | +the documentation, which contains the unreleased features. |
14 | 17 |
|
15 | | -A helper macro is provided to make it easier to define a `ParameterizedFunction`, |
16 | | -and it will symbolically compute a bunch of extra functions to make the differential |
17 | | -equation solvers run faster. For example, to define the previous `LotkaVolterra`, |
18 | | -you can use the following command: |
| 18 | +## Example |
19 | 19 |
|
20 | | -```julia |
21 | | -f = @ode_def LotkaVolterra begin |
22 | | - dx = a*x - b*x*y |
23 | | - dy = -c*y + d*x*y |
24 | | -end a b c d |
25 | | -``` |
26 | | - |
27 | | -or you can define it anonymously: |
| 20 | +The following are valid ODE definitions. |
28 | 21 |
|
29 | 22 | ```julia |
30 | 23 | f = @ode_def begin |
31 | | - dx = a*x - b*x*y |
32 | | - dy = -c*y + d*x*y |
33 | | -end a b c d |
34 | | -``` |
35 | | - |
36 | | -The macro also defines the Jacobian `f'`. This is defined as an in-place Jacobian `f(Val{:jac},t,u,J)`. |
37 | | -This is calculated using SymEngine.jl automatically, so it's no effort on your part. |
38 | | -The symbolic inverse of the Jacobian is also computed, and an in-place function |
39 | | -for this is available as well as `f(Val{:invjac},t,u,iJ)`. If the Jacobians cannot be |
40 | | -computed, a warning is thrown and only the function itself is usable. The functions |
41 | | -`jac_exists(f)` and `invjac_exists(f)` can be used to see whether the Jacobian |
42 | | -and the function for its inverse exist. |
43 | | - |
44 | | -#### Extra Options |
45 | | - |
46 | | -In most cases the `@ode_def` macro should be sufficient. This is because by default |
47 | | -the macro will simply calculate each function symbolically, and if it can't it |
48 | | -will simply throw a warning and move on. However, in extreme cases the symbolic |
49 | | -calculations may take a long time, in which case it is necessary to turn them |
50 | | -off. To do this, use the `ode_def_opts` function. The `@ode_def` macro simply defines the specifiable options: |
51 | | - |
52 | | -```julia |
53 | | -opts = Dict{Symbol,Bool}( |
54 | | - :build_tgrad => true, |
55 | | - :build_jac => true, |
56 | | - :build_expjac => false, |
57 | | - :build_invjac => true, |
58 | | - :build_invW => true, |
59 | | - :build_invW_t => true, |
60 | | - :build_hes => false, |
61 | | - :build_invhes => false, |
62 | | - :build_dpfuncs => true) |
63 | | -``` |
64 | | - |
65 | | -and calls the function `ode_def_opts(name::Symbol,opts,ex::Expr,params)`. Note that |
66 | | -params is an iterator holding expressions for the parameters. |
| 24 | + dy₁ = -k₁*y₁+k₃*y₂*y₃ |
| 25 | + dy₂ = k₁*y₁-k₂*y₂^2-k₃*y₂*y₃ |
| 26 | + dy₃ = k₂*y₂^2 |
| 27 | +end k₁ k₂ k₃ |
67 | 28 |
|
68 | | -In addition, one can also use their own function inside of the macro. For example: |
69 | | - |
70 | | -```julia |
71 | | -f(x,y,d) = erf(x*y/d) |
72 | | -NJ = @ode_def FuncTest begin |
73 | | - dx = a*x - b*x*y |
74 | | - dy = -c*y + f(x,y,d) |
75 | | -end a b c d |
76 | | -``` |
77 | | - |
78 | | -will do fine. The symbolic derivatives will not work unless you define a derivative |
79 | | -for `f`. |
80 | | - |
81 | | -#### Extra Macros |
82 | | - |
83 | | -Instead of using `ode_def_opts` directly, one can use one of the following macros |
84 | | -to be more specific about what to not calculate. In increasing order of calculations: |
85 | | - |
86 | | -```julia |
87 | | -@ode_def_bare |
88 | | -@ode_def |
89 | | -@ode_def_all |
90 | | -``` |
91 | | - |
92 | | -### Extra Functions |
93 | | - |
94 | | -#### Jacobian Function |
95 | | - |
96 | | -The Jacobian overload is provided by overloading in the following manner: |
97 | | - |
98 | | -```julia |
99 | | -function (p::LotkaVolterra)(::Type{Val{:jac}},t,u,J) |
100 | | - J[1,1] = p.a - p.b * u[2] |
101 | | - J[1,2] = -(p.b) * u[1] |
102 | | - J[2,1] = 1 * u[2] |
103 | | - J[2,2] = -3 + u[1] |
104 | | - nothing |
105 | | -end |
106 | | -``` |
107 | | - |
108 | | -#### Inverse Jacobian |
109 | | - |
110 | | -The Inverse Jacobian overload is provided by overloading in the following manner: |
111 | | - |
112 | | -```julia |
113 | | -function (p::LotkaVolterra)(::Type{Val{:invjac}},t,u,J) |
114 | | - J[1,1] = (1 - (p.b * u[1] * u[2]) / ((p.a - p.b * u[2]) * (-3 + u[1] + (p.b * u[1] * u[2]) / (p.a - p.b * u[2])))) / (p.a - p.b * u[2]) |
115 | | - J[1,2] = (p.b * u[1]) / ((p.a - p.b * u[2]) * (-3 + u[1] + (p.b * u[1] * u[2]) / (p.a - p.b * u[2]))) |
116 | | - J[2,1] = -(u[2]) / ((p.a - p.b * u[2]) * (-3 + u[1] + (p.b * u[1] * u[2]) / (p.a - p.b * u[2]))) |
117 | | - J[2,2] = (-3 + u[1] + (p.b * u[1] * u[2]) / (p.a - p.b * u[2])) ^ -1 |
118 | | - nothing |
119 | | -end |
120 | | -``` |
121 | | - |
122 | | -#### Parameter Jacobian |
123 | | - |
124 | | -For solvers which need parameters derivatives, specifying the functions can increase |
125 | | -performance. For our example, we allow the solvers to use the explicit derivatives |
126 | | -in the parameters by: |
127 | | - |
128 | | -```julia |
129 | | -function (p::LotkaVolterra)(::Type{Val{:paramjac}},J,u,p,t) |
130 | | - J[1, 1] = u[1] * 1 |
131 | | - J[1, 2] = -(u[1]) * u[2] |
132 | | - J[1, 3] = 0 * 1 |
133 | | - J[1, 4] = 0 * 1 |
134 | | - J[2, 1] = 0 * 1 |
135 | | - J[2, 2] = 0 * 1 |
136 | | - J[2, 3] = -(u[2]) |
137 | | - J[2, 4] = u[1] * u[2] |
138 | | - nothing |
139 | | -end |
140 | | -``` |
| 29 | +f = @ode_def begin |
| 30 | + d🐁 = α*🐁 - β*🐁*🐈 |
| 31 | + d🐈 = -γ*🐈 + δ*🐁*🐈 |
| 32 | +end α β γ δ |
| 33 | +``` |
0 commit comments