Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot solve for dependent parameters during initialization #2665

Closed
hersle opened this issue Apr 22, 2024 · 9 comments
Closed

Cannot solve for dependent parameters during initialization #2665

hersle opened this issue Apr 22, 2024 · 9 comments
Assignees
Labels
bug Something isn't working

Comments

@hersle
Copy link
Contributor

hersle commented Apr 22, 2024

Consider this (trivial) ODE for x and y:

using Test
using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
using DifferentialEquations

@variables x(t) y(t) s(t)
sys1 = structural_simplify(ODESystem([D(x) ~ 0, D(y) ~ 0, s ~ x + y], t, [x, y, s], []; name=:sys))
prob1 = ODEProblem(sys1, [s => 1.0, x => 0.3], (0.0, 1.0))
sol1 = solve(prob1)
@test all(sol1[x] .≈ 0.3) && all(sol1[y] .≈ 0.7) # passes

This works. The constraint s(0) == x(0)+y(0) == 1 correctly initializes y(0) == 0.7. I do not have to specify a guess for y(0).


But consider an implementation of its (trivial) analytical solution, using parameters for the initial values x(0) == x0 and y(0) == y0:

@parameters x0 y0
sys2 = structural_simplify(ODESystem([x ~ x0, y ~ y0, s ~ x + y], t, [x, y, s], [x0, y0]; name=:sys))
prob2 = ODEProblem(sys2, [s => 1.0, x0 => 0.3], (0.0, 1.0))
sol2 = solve(prob2)

This does not work. The initialization gives an error that asks for a guess:

ERROR: LoadError: Initial condition underdefined. Some are missing from the variable map.
Please provide a default (`u0`), initialization equation, or guess       
for the following variables:

Any[y0]

But adding e.g. ; guesses = [y0 => 0.0] to ODEProblem(sys2, ... does not change anything.

Is it possible to make this work?

@hersle hersle added the bug Something isn't working label Apr 22, 2024
@hersle
Copy link
Contributor Author

hersle commented Apr 22, 2024

From what I can tell, this happens because InitializationProblem() calls generate_initializesystem(), which does not treat parameters as (possible) unknowns.

For example

isys2 = generate_initializesystem(sys2; u0map = [s => 1.0, x => 0.3])
equations(isys2)

shows that all the 5 necessary equations are there:

5-element Vector{Equation}:
 0 ~ 0.3 - x(t)
 0 ~ 1.0 - s(t)
 0 ~ x0 - x(t)
 0 ~ y0 - y(t)
 0 ~ x(t) + y(t) - s(t)

But

unknowns(isys2)

shows that x0 and y0 are not treated as unknowns:

3-element Vector{SymbolicUtils.BasicSymbolic{Real}}:
 x(t)
 y(t)
 s(t)

@ChrisRackauckas
Copy link
Member

This is possible to solve and we should fix this.

@AayushSabharwal
Copy link
Member

So for this we need:

  • Recognize parameters with no initial value as unknowns
  • Respect symbolic parameter defaults as equations (if no explicit value given)
  • Create and store a function for getting values from the nonlinear problem and putting them into the ODEProblem

@hersle
Copy link
Contributor Author

hersle commented May 27, 2024

If possible, it would be great to test that everything works seamlessly through autodiff as well, using recent improvements like #2686. In case it can be of use, here is one suggestion for a test which I think should pass:

using Test
using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
using DifferentialEquations
using ForwardDiff: derivative as ∇

@testset "Solve for unspecified parameters during initialization (issue #2665)" begin

@variables x(t) y(t) s(t)
@parameters x0 y0

# solve using variables
function y_of_x_vars(x0val)
    @named sys = ODESystem([D(x) ~ 0, D(y) ~ 0, s ~ x + y], t, [x, y, s], [])
    sys = structural_simplify(sys)
    prob = ODEProblem(sys, [s => 1.0, x => x0val], (0.0, 1.0))
    sol = solve(prob, Tsit5())
    return sol(0.123, idxs=y)
end

# solve using parameters
function y_of_x_pars(x0val)
    @named sys = ODESystem([x ~ x0, y ~ y0, s ~ x + y], t, [x, y, s], [x0, y0])
    sys = structural_simplify(sys)
    prob = ODEProblem(sys, [s => 1.0], (0.0, 1.0), [x0 => x0val])
    sol = solve(prob, Tsit5())
    return sol(0.123, idxs=y)
end

# solve analytically
function y_of_x_anal(x0val)
    return 1.0 - x0val
end

@test y_of_x_anal(0.3)  y_of_x_vars(0.3)  y_of_x_pars(0.3)
@test (y_of_x_anal, 0.3)  (y_of_x_vars, 0.3)  (y_of_x_pars, 0.3)

end

Thank you! 😃

@AayushSabharwal
Copy link
Member

AayushSabharwal commented May 28, 2024

I have something working, but the problem here seems to be that the initialization problem is only solved for DAEs which this example isn't. I'll try and find a solution.
EDIT: The initialization problem isn't called because the problem has no unknowns.

@ChrisRackauckas
Copy link
Member

You need to specify all parameters, that's a given. This now works:

@variables x(t) y(t) s(t)
sys1 = structural_simplify(ODESystem([D(x) ~ 0, D(y) ~ 0, s ~ x + y], t, [x, y, s], []; name=:sys))
prob1 = ODEProblem(sys1, [s => 1.0, x => 0.3], (0.0, 1.0))
sol1 = solve(prob1)
@test all(sol1[x] .≈ 0.3) && all(sol1[y] .≈ 0.7) # passes

@parameters x0 y0
sys2 = structural_simplify(ODESystem([x ~ x0, y ~ y0, s ~ x + y], t, [x, y, s], [x0, y0]; name=:sys))
prob2 = ODEProblem(sys2, [s => 1.0, x0 => 0.3, y0 => 0.3], (0.0, 1.0))
sol2 = solve(prob2)

and was fixed by #2775

@hersle
Copy link
Contributor Author

hersle commented Jun 9, 2024

But the constraint s => 1.0 is not respected in the latter case. See also #2747.

@AayushSabharwal
Copy link
Member

The functionality necessary for this is added in #2928

@hersle
Copy link
Contributor Author

hersle commented Aug 6, 2024

I am really looking forward to this 😀 Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
3 participants