# Re-creating MTK problems creates side effects (ModelingToolkit.jl)

**URL:** <https://discourse.julialang.org/t/re-creating-mtk-problems-creates-side-effects-modelingtoolkit-jl/126054>\
**Category:** Modelling & Simulations\
**Tags:** question, sciml, modelingtoolkit\
**Created:** [February 19, 2025, 3:15am UTC](https://discourse.julialang.org/t/re-creating-mtk-problems-creates-side-effects-modelingtoolkit-jl/126054 "2025-02-19T03:15:20Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![ysfoo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ysfoo/32/51058_2.png) [@ysfoo](https://discourse.julialang.org/u/ysfoo)\
**Post date:** [February 19, 2025, 3:15am UTC](https://discourse.julialang.org/t/re-creating-mtk-problems-creates-side-effects-modelingtoolkit-jl/126054/1 "2025-02-19T03:15:20Z")

</div>

I am following the somewhat recent [tutorial](https://docs.sciml.ai/ModelingToolkit/dev/examples/remake/) on re-creating MTK problems. In summary, the function `loss` is defined, which remakes an `ODEProblem` by replacing the tunable portion of the parameters with the input argument, and then evaluates the loss function. Upon calling the loss function, the parameters in the original `ODEProblem` object (in the global scope) are mutated.

To reproduce, first run the code from the tutorial (can exclude the optimisation part). Then:

```julia
getter = getp(odeprob, [α, β, γ, δ])
getter(odeprob) # returns original parameter values
loss(ones(4), (odeprob, timesteps, data, setter, diffcache))
getter(odeprob) # returns ones, calling the loss function has mutated `odeprob`

```

Is this side effect intended? How can one avoid the side effect?

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [February 19, 2025, 3:56pm UTC](https://discourse.julialang.org/t/re-creating-mtk-problems-creates-side-effects-modelingtoolkit-jl/126054/2 "2025-02-19T15:56:25Z")

</div>

That’s not intended. Open a reproducer.

---

<div class="post-metadata">

**Author:** ![ysfoo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ysfoo/32/51058_2.png) [@ysfoo](https://discourse.julialang.org/u/ysfoo)\
**Post date:** [February 20, 2025, 5:52am UTC](https://discourse.julialang.org/t/re-creating-mtk-problems-creates-side-effects-modelingtoolkit-jl/126054/3 "2025-02-20T05:52:12Z")

</div>

Here it is (mostly copy-pasted from the tutorial), should I post an issue on SciMLStructures.jl or elsewhere?

```julia
using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D

@parameters α β γ δ
@variables x(t) y(t)
eqs = [D(x) ~ (α - β * y) * x
       D(y) ~ (δ * x - γ) * y]
@mtkbuild odesys = ODESystem(eqs, t)

using OrdinaryDiffEq

odeprob = ODEProblem(
    odesys, [x => 1.0, y => 1.0], (0.0, 10.0), [α => 1.5, β => 1.0, γ => 3.0, δ => 1.0])
timesteps = 0.0:0.1:10.0
sol = solve(odeprob, Tsit5(); saveat = timesteps)
data = Array(sol)
# add some random noise
data = data + 0.01 * randn(size(data))

using SymbolicIndexingInterface: parameter_values, state_values
using SciMLStructures: Tunable, canonicalize, replace, replace!
using PreallocationTools

function loss(x, p)
    odeprob = p[1] # ODEProblem stored as parameters to avoid using global variables
    ps = parameter_values(odeprob) # obtain the parameter object from the problem
    diffcache = p[5]
    # get an appropriately typed preallocated buffer to store the `x` values in
    buffer = get_tmp(diffcache, x)
    # copy the current values to this buffer
    copyto!(buffer, canonicalize(Tunable(), ps)[1])
    # create a copy of the parameter object with the buffer
    ps = replace(Tunable(), ps, buffer)
    # set the updated values in the parameter object
    setter = p[4]
    setter(ps, x)
    # remake the problem, passing in our new parameter object
    newprob = remake(odeprob; p = ps)
    timesteps = p[2]
    sol = solve(newprob, AutoTsit5(Rosenbrock23()); saveat = timesteps)
    truth = p[3]
    data = Array(sol)
    return sum((truth .- data) .^ 2) / length(truth)
end

using SymbolicIndexingInterface

setter = setp(odeprob, [α, β, γ, δ]);
# `DiffCache` to avoid allocations
diffcache = DiffCache(canonicalize(Tunable(), parameter_values(odeprob))[1]);

getter = getp(odeprob, [α, β, γ, δ])
getter(odeprob) # returns original parameter values
loss(ones(4), (odeprob, timesteps, data, setter, diffcache))
getter(odeprob) # returns ones, calling the loss function has mutated `odeprob`

```

---

<div class="post-metadata">

**Author:** ![ysfoo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ysfoo/32/51058_2.png) [@ysfoo](https://discourse.julialang.org/u/ysfoo)\
**Post date:** [February 24, 2025, 11:27pm UTC](https://discourse.julialang.org/t/re-creating-mtk-problems-creates-side-effects-modelingtoolkit-jl/126054/4 "2025-02-24T23:27:51Z")

</div>

In case anyone runs into this issue, the solution is to [create a copy when creating the `DiffCache`](https://github.com/SciML/ModelingToolkit.jl/issues/3407).
