|
| 1 | +# ModelingToolkit v11 Release Notes |
| 2 | + |
| 3 | +## Symbolics@7 and SymbolicUtils@4 compatibility |
| 4 | + |
| 5 | +SymbolicUtils version 4 involved a major overhaul of the core symbolic infrastructure, which |
| 6 | +propagated to Symbolics as Symbolics version 7. ModelingToolkit has now updated to these versions. |
| 7 | +This includes significant type-stability improvements, enabling precompilation of large parts |
| 8 | +of the symbolic infrastructure and faster TTFX. It is highly recommended to read the |
| 9 | +[Release Notes for SymbolicUtils@4](https://github.com/JuliaSymbolics/SymbolicUtils.jl/releases/tag/v4.0.0) |
| 10 | +and the [doc page](https://docs.sciml.ai/SymbolicUtils/dev/manual/variants/) describing the new |
| 11 | +variant structure before these release notes. |
| 12 | + |
| 13 | +As part of these changes, ModelingToolkit has changed how some data is represented to allow |
| 14 | +precompilation. Notably, `variable => value` mappings (such as guesses) are stored as an |
| 15 | +`AbstractDict{SymbolicT, SymbolicT}`. Here, `SymbolicT` is a type that comes from Symbolics.jl, |
| 16 | +and is the type for all unwrapped symbolic values. This means that any non-symbolic values |
| 17 | +are stored as `SymbolicUtils.Const` variants. Mutation such as `guesses(sys)[x] = 1.0` is still |
| 18 | +possible, and values are automatically converted. However, obtaining the value back requires |
| 19 | +usage of `SymbolicUtils.unwrap_const` or `Symbolics.value`. |
| 20 | + |
| 21 | +Following is a before/after comparison of the TTFX for the most common operations in ModelingToolkit.jl. |
| 22 | +Further improvements are ongoing. Note that the timings do depend on many factors such as the exact system |
| 23 | +used, types passed to constructor functions, other packages currently loaded in the session, presence of |
| 24 | +array variables/equations, whether index reduction is required, and the behavior of various passes in |
| 25 | +`mtkcompile`. However, the numbers are good representations of the kinds of performance improvements |
| 26 | +that are possible due to the new infrastructure. There will continue to be improvements as this gets |
| 27 | +more extensive testing and we are better able to identify bottlenecks in compilation. |
| 28 | + |
| 29 | +### `System` constructor |
| 30 | + |
| 31 | +The time to call `System`, not including the time taken for `@variables` or building the equations. |
| 32 | + |
| 33 | +Before: |
| 34 | + |
| 35 | +``` |
| 36 | + 0.243758 seconds (563.80 k allocations: 30.613 MiB, 99.48% compilation time: 3% of which was recompilation) |
| 37 | +elapsed time (ns): 2.43757958e8 |
| 38 | +gc time (ns): 0 |
| 39 | +bytes allocated: 32099616 |
| 40 | +pool allocs: 563137 |
| 41 | +non-pool GC allocs: 16 |
| 42 | +malloc() calls: 651 |
| 43 | +free() calls: 0 |
| 44 | +minor collections: 0 |
| 45 | +full collections: 0 |
| 46 | +``` |
| 47 | + |
| 48 | +After: |
| 49 | + |
| 50 | +``` |
| 51 | + 0.000670 seconds (217 allocations: 10.641 KiB) |
| 52 | +elapsed time (ns): 669875.0 |
| 53 | +gc time (ns): 0 |
| 54 | +bytes allocated: 10896 |
| 55 | +pool allocs: 217 |
| 56 | +non-pool GC allocs: 0 |
| 57 | +minor collections: 0 |
| 58 | +full collections: 0 |
| 59 | +``` |
| 60 | + |
| 61 | +### `complete` |
| 62 | + |
| 63 | +Before: |
| 64 | + |
| 65 | +``` |
| 66 | + 1.795140 seconds (9.76 M allocations: 506.143 MiB, 2.67% gc time, 99.75% compilation time: 71% of which was recompilation) |
| 67 | +elapsed time (ns): 1.795140083e9 |
| 68 | +gc time (ns): 47998414 |
| 69 | +bytes allocated: 530729216 |
| 70 | +pool allocs: 9747214 |
| 71 | +non-pool GC allocs: 111 |
| 72 | +malloc() calls: 10566 |
| 73 | +free() calls: 8069 |
| 74 | +minor collections: 5 |
| 75 | +full collections: 1 |
| 76 | +``` |
| 77 | + |
| 78 | +After: |
| 79 | + |
| 80 | +``` |
| 81 | + 0.001191 seconds (1.08 k allocations: 2.554 MiB) |
| 82 | +elapsed time (ns): 1.190625e6 |
| 83 | +gc time (ns): 0 |
| 84 | +bytes allocated: 2678088 |
| 85 | +pool allocs: 1077 |
| 86 | +non-pool GC allocs: 0 |
| 87 | +malloc() calls: 3 |
| 88 | +free() calls: 0 |
| 89 | +minor collections: 0 |
| 90 | +full collections: 0 |
| 91 | +``` |
| 92 | + |
| 93 | +### `TearingState` constructor |
| 94 | + |
| 95 | +`TearingState` is an intermediary step in `mtkcompile`. It is significant enough for the impact |
| 96 | +to be worth measuring separately. |
| 97 | + |
| 98 | +Before: |
| 99 | + |
| 100 | +``` |
| 101 | + 0.374312 seconds (527.01 k allocations: 32.318 MiB, 24.13% gc time, 99.60% compilation time: 85% of which was recompilation) |
| 102 | +elapsed time (ns): 3.74312e8 |
| 103 | +gc time (ns): 90318708 |
| 104 | +bytes allocated: 33888248 |
| 105 | +pool allocs: 526440 |
| 106 | +non-pool GC allocs: 11 |
| 107 | +malloc() calls: 555 |
| 108 | +free() calls: 2923 |
| 109 | +minor collections: 1 |
| 110 | +full collections: 0 |
| 111 | +``` |
| 112 | + |
| 113 | +After: |
| 114 | + |
| 115 | +``` |
| 116 | + 0.002062 seconds (1.07 k allocations: 8.546 MiB, 50.24% compilation time) |
| 117 | +elapsed time (ns): 2.0618339999999998e6 |
| 118 | +gc time (ns): 0 |
| 119 | +bytes allocated: 8961560 |
| 120 | +pool allocs: 1064 |
| 121 | +non-pool GC allocs: 0 |
| 122 | +malloc() calls: 6 |
| 123 | +free() calls: 0 |
| 124 | +minor collections: 0 |
| 125 | +full collections: 0 |
| 126 | +``` |
| 127 | + |
| 128 | +### `mtkcompile` |
| 129 | + |
| 130 | +This measures the time taken by the first call to `mtkcompile`. This is run after the `TearingState` |
| 131 | +benchmark, and hence the compile time from that aspect of the process is not included (runtime is |
| 132 | +included). |
| 133 | + |
| 134 | +Before: |
| 135 | + |
| 136 | +``` |
| 137 | + 1.772756 seconds (3.81 M allocations: 206.068 MiB, 0.63% gc time, 99.71% compilation time: 71% of which was recompilation) |
| 138 | +elapsed time (ns): 1.772755875e9 |
| 139 | +gc time (ns): 11162292 |
| 140 | +bytes allocated: 216077752 |
| 141 | +pool allocs: 3808615 |
| 142 | +non-pool GC allocs: 61 |
| 143 | +malloc() calls: 4877 |
| 144 | +free() calls: 4844 |
| 145 | +minor collections: 2 |
| 146 | +full collections: 0 |
| 147 | +``` |
| 148 | + |
| 149 | +After: |
| 150 | + |
| 151 | +``` |
| 152 | + 0.018629 seconds (20.74 k allocations: 932.062 KiB, 89.89% compilation time) |
| 153 | +elapsed time (ns): 1.8628542e7 |
| 154 | +gc time (ns): 0 |
| 155 | +bytes allocated: 954432 |
| 156 | +pool allocs: 20727 |
| 157 | +non-pool GC allocs: 0 |
| 158 | +malloc() calls: 13 |
| 159 | +free() calls: 0 |
| 160 | +minor collections: 0 |
| 161 | +full collections: 0 |
| 162 | +``` |
| 163 | + |
| 164 | +## Semantic separation of discretes |
| 165 | + |
| 166 | +ModelingToolkit has long overloaded the meaning of `@parameters` to the point that it means |
| 167 | +"anything that isn't `@variables`." This isn't a very intuitive or clear definition. This is |
| 168 | +now improved with the introduction of `@discretes`. Any quantities that vary on a different |
| 169 | +time-scale than those in `@variables` are now `@discretes`. `@parameters` can only be used to |
| 170 | +create "time-independent parameters". For clarity, the following continues to work: |
| 171 | + |
| 172 | +```julia |
| 173 | +@parameters p q[1:3] f(::Real, ::Real) |
| 174 | +``` |
| 175 | + |
| 176 | +However, this is now disallowed: |
| 177 | + |
| 178 | +```julia |
| 179 | +@parameters value(t) |
| 180 | +``` |
| 181 | + |
| 182 | +Instead, it must be declared as: |
| 183 | + |
| 184 | +```julia |
| 185 | +@discretes value(t) |
| 186 | +``` |
| 187 | + |
| 188 | +And can be passed along with the `@variables`. Essentially, for time-varying systems |
| 189 | +the constructor syntax is |
| 190 | + |
| 191 | +```julia |
| 192 | +System(equations, independent_variable, time_varying_variables, constant_values, [brownians]) |
| 193 | +``` |
| 194 | + |
| 195 | +In the subsequent release notes and in documentation, "variables" refers to either `@variables` |
| 196 | +or `@discretes` unless explicitly mentioned otherwise. |
| 197 | + |
| 198 | +An important note is that while this is a difference in declaration, the semantics are defined |
| 199 | +by their usage in the system. More concretely, a variable declared via `@discretes` is only |
| 200 | +actually considered discrete if it is part of the variables updated in a callback in the system. |
| 201 | +Otherwise, it is treated identically to a variable declared via `@variables`. |
| 202 | + |
| 203 | +## Changes to `defaults` and initialization semantics |
| 204 | + |
| 205 | +The concept of `defaults` is a relic of earlier ModelingToolkit versions, from when initialization |
| 206 | +did not exist and they served as convenient initial conditions. The package has evolved greatly since then |
| 207 | +and `defaults` have taken on many different meanings in different contexts. This makes their usage |
| 208 | +complicated and unintuitive. |
| 209 | + |
| 210 | +`defaults` have now been removed. They are replaced by two new concepts, with simple and well-defined |
| 211 | +semantics. Firstly, `initial_conditions` is a variable-value mapping aimed solely at being a convenient |
| 212 | +way to provide initial conditions to `SciMLProblem`s constructed from the system. Specifying them is |
| 213 | +identical to providing initial values to the `ODEProblem` constructor. Secondly, `bindings` is an |
| 214 | +immutable variable-value mapping representing strong constraints between variables/parameters. |
| 215 | +A binding for a variable is a function of other variables/parameters that is enforced during initialization. |
| 216 | +A binding for a parameter is a function of other parameters that exclusively defines the value of that |
| 217 | +parameter. Bound variables or parameters cannot be given initial conditions, either through the |
| 218 | +`initial_conditions` keyword or by passing them to the problem constructor. In effect, bindings |
| 219 | +serve to mark specific variables as aliases of others during initialization, and parameters as aliases |
| 220 | +of other parameters. This supersedes the previous concept of parameter bindings, and explicit parameter |
| 221 | +equations passed along with the equations of the model. Since bound parameters are computed as functions |
| 222 | +of other parameters, they are treated akin to observed variables. They are not stored in the parameter |
| 223 | +object, and instead are computed on the fly as required. |
| 224 | + |
| 225 | +Sometimes, it is useful to enforce a relation between parameters while allowing them to be given initial |
| 226 | +values. For example, one might relate the radius `r` and area `A` of a pipe as `A ~ pi * r * r`. Users of |
| 227 | +the model should be able to provide a value for either `r` or `A`, and the other should be calculated |
| 228 | +automatically. This is done by providing the relation `A ~ pi * r * r` to the `initialization_eqs` |
| 229 | +keyword of the model and binding both `A` and `r` to `missing`. Similar to v10, the equation represents |
| 230 | +a constraint to be enforced. The bindings act similar to the `missing` defaults in v9 and v10, indicating |
| 231 | +that the parameters are to be solved for. They are part of bindings since a parameter to be solved for |
| 232 | +cannot be an alias for a different value. As such, the choice of parameters that can be solved for is |
| 233 | +an immutable property of the system. Note that making a parameter solvable no longer requires specifying a |
| 234 | +guess. If a guess is required to solve the initialization, ModelingToolkit will error with an informative |
| 235 | +message during problem construction. Note that since parameters can only be bound to other parameters, |
| 236 | +a parameter `x0` can be bound to the initial value of a variable `x` using the binding `x0 = Initial(x)`. |
| 237 | + |
| 238 | +The formulation of the initialization system can now be summarized succinctly. The system solves for: |
| 239 | + |
| 240 | +- Unknowns of the system. |
| 241 | +- Observables (observed variables) of the system. |
| 242 | +- All unknowns for which derivatives are known (differential variables, and ones for which derivative |
| 243 | + information is available due to the index reduction process). |
| 244 | +- Discrete variables (created via `@discretes`). |
| 245 | +- Parameters with a binding of `missing`. |
| 246 | + |
| 247 | +It is composed of: |
| 248 | + |
| 249 | +- Algebraic equations. |
| 250 | +- Observed equations. |
| 251 | +- Initialization equations. |
| 252 | +- The `initial_conditions` of the system. |
| 253 | +- Initial conditions passed to the problem constructor. These override values in `initial_conditions` |
| 254 | + for the same variable. |
| 255 | + |
| 256 | +Additionally, `Initial` parameters exist for the following variables: |
| 257 | + |
| 258 | +- Unknowns |
| 259 | +- Observables |
| 260 | +- First derivatives of all unknowns and observables |
| 261 | +- Discrete variables |
| 262 | +- Parameters with a binding of `missing` |
| 263 | + |
| 264 | +"Defaults" specified via variable metadata are now translated into either `initial_conditions` or |
| 265 | +`bindings` depending on the value. If the value is a constant, it is part of `initial_conditions`. |
| 266 | +If it is an expression involving other variables/parameters, it is part of `bindings`. For example, |
| 267 | +the following are `initial_conditions`: |
| 268 | + |
| 269 | +```julia |
| 270 | +@variables x(t) = 1 y(t)[1:3] = zeros(3) |
| 271 | +@parameters f(::Real) = sin |
| 272 | +``` |
| 273 | + |
| 274 | +The following are bindings: |
| 275 | + |
| 276 | +```julia |
| 277 | +@variables z(t) = x w(t)[1:2] = [1.5, z] |
| 278 | +@parameters p[1:3] = f(3) |
| 279 | +``` |
| 280 | + |
| 281 | +Notably, arrays are considered atomic. This means that if even one element of an array default is |
| 282 | +symbolic, the entire array variable is considered bound. Partial bindings can be constructed by |
| 283 | +destructuring the array: |
| 284 | + |
| 285 | +```julia |
| 286 | +@parameters par[1:3] = [par1, par2, par3] |
| 287 | +``` |
| 288 | + |
| 289 | +Where `par1`, `par2` and `par3` can independently have initial conditions or bindings. In a |
| 290 | +similar vein, `guesses`, `initial_conditions` and `bindings` are all stored in special |
| 291 | +`AbstractDict` types that disallow scalarized keys. For example, `par[1]` cannot be a key |
| 292 | +of these dictionaries. `par` is allowed as a key. Initial values can still be given to the |
| 293 | +problem constructor in scalarized form. |
| 294 | + |
| 295 | +As mentioned previously, bindings cannot be mutated. To change the bindings of a system, |
| 296 | +the following pattern can be employed: |
| 297 | + |
| 298 | +```julia |
| 299 | +binds = bindings(sys) |
| 300 | +# the `ReadOnlyDict` wrapper uses `Base.parent` to get the underlying mutable container |
| 301 | +new_binds = parent(copy(binds)) |
| 302 | + |
| 303 | +# mutate `new_binds`... |
| 304 | + |
| 305 | +using Setfield: @set! |
| 306 | + |
| 307 | +@set! sys.bindings = new_binds |
| 308 | +sys = complete(sys) # Important! |
| 309 | +``` |
| 310 | + |
| 311 | +Mutation of bindings without copying them is undefined behavior and can lead to unpredictable bugs. |
| 312 | + |
| 313 | +## Array variables as inputs |
| 314 | + |
| 315 | +Previously, ModelingToolkit allowed part of an array variable to be an input. For example, the following |
| 316 | +used to be valid: |
| 317 | + |
| 318 | +```julia |
| 319 | +@variables u(t)[1:2] [input = true] |
| 320 | +@named sys = # Some system involving `u` |
| 321 | + |
| 322 | +sys = mtkcompile(sys; inputs = [u[1]]) |
| 323 | +``` |
| 324 | + |
| 325 | +This is now disallowed. `mtkcompile` will throw an informative error if part of an array is passed as an |
| 326 | +input. |
| 327 | + |
| 328 | +## Deprecation of `@mtkmodel` |
| 329 | + |
| 330 | +The `@mtkmodel` originated as a convenient DSL for creating models. However, it has not received the same |
| 331 | +level of support as other features due to the complexity of the parsing. It is also a major source of bugs, |
| 332 | +and is thus a tripping hazard for new and old users alike. The macro is now deprecated. It is moved to a new |
| 333 | +package, SciCompDSL.jl. Enough updates have been made to allow it to create systems in v11, but it will not |
| 334 | +receive more maintenance from the core developers. It is, however, still open to community contribution. For |
| 335 | +more details, please refer to the discussion in [this Discourse thread](https://discourse.julialang.org/t/using-mtk-when-i-import-modelingtoolkit/133681/12). |
| 336 | + |
| 337 | +## Splitting into `ModelingToolkitBase` and relicensing of parts of ModelingToolkit |
| 338 | + |
| 339 | +The advanced structural simplification algorithms in ModelingToolkit, such as index reduction, structural |
| 340 | +singularity removal and tearing, are now moved to the [StateSelection.jl](https://github.com/JuliaComputing/StateSelection.jl/) |
| 341 | +and ModelingToolkitTearing.jl (`lib/ModelingToolkitTearing` in the same repo) packages. These packages are |
| 342 | +AGPL licensed. ModelingToolkitBase.jl contains the `System` representation, callbacks, all of the code-generation |
| 343 | +targets (problem constructors), and initialization infrastructure. Items that depend on structural simplification |
| 344 | +and/or require highly specialized code generation (`SCCNonlinearProblem` prominent among them) have been moved |
| 345 | +to ModelingToolkit.jl, which depends on the aforementioned AGPL packages. ModelingToolkitBase still contains |
| 346 | +a simple version of `mtkcompile` suitable for most use cases. It does not perform index reduction and requires |
| 347 | +that all differential equations are explicit in the derivative. However, it does have a simpler tearing algorithm |
| 348 | +that is capable of identifying observed equations in many scenarios. In fact, it is also able to reduce many |
| 349 | +systems created using modular components and the `connect` infrastructure. Contributions to improve this |
| 350 | +are also welcome. |
| 351 | + |
| 352 | +For more information on the split and surrounding changes, please follow the discussion in |
| 353 | +[this Discourse thread](https://discourse.julialang.org/t/modelingtoolkit-v11-library-split-and-licensing-community-feedback-requested/134396). |
| 354 | + |
1 | 355 | # ModelingToolkit v10 Release Notes |
2 | 356 |
|
3 | 357 | ## Callbacks |
|
0 commit comments