Skip to content

WIP: Add linearize function #1675

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

Merged
merged 16 commits into from
Jul 11, 2022
Merged

WIP: Add linearize function #1675

merged 16 commits into from
Jul 11, 2022

Conversation

YingboMa
Copy link
Member

No description provided.

Co-authored-by: Fredrik Bagge Carlson <baggepinnen@gmail.com>
@baggepinnen
Copy link
Contributor

The test failure appears to be Coveralls having a bad day :/
https://github.com/SciML/ModelingToolkit.jl/runs/7129277462?check_suite_focus=true#step:9:8

Comment on lines +94 to +97
@test lsys.A == [0 0; 0 -10]
@test lsys.B == [2 -2; 10 -10]
@test lsys.C == [400 -4000]
@test lsys.D == [4400 -4400]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to get a symbolic version of it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WSMLinearize is able to do this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can get a function that calculates the matrices based on an operating point, but the differentiation is done using ForwardDiff so you can not get Symbolic Jacobians. In some cases, it would be possible to provide symbolic jacobians, but right now there is no support for it since symbolic differentiation is less applicable than numeric.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by less applicable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are functions that are not differentiable symbolically (at least not by Symbolics.jl), many of which are differentiable with ForwardDiff. Examples are user-registered functions, stuff with ifelse etc.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WSM also defaults to numeric diff, probably for the same reason
image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is nothing preventing symbolic jacobians being added in a separate PR, and just error if there is something that can not be differentiated by Symbolics

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, there is calculate_jacobian already that can be used for symbolic linearization of simple systems.
image

Copy link
Member Author

@YingboMa YingboMa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use JuliaFormatter to format the code automatically BTW. Ah, you did run it. It's just that it gives weird formatting.

function linearize(sys, lin_fun; op = Dict(), allow_input_derivatives = false)
x0 = merge(defaults(sys), op)
prob = ODEProblem(sys, x0, (0.0, 1.0))
linres = lin_fun(prob.u0, prob.p, 0.0)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that in this case, we only use the initial condition and parameters from the ODEProblem. You can use the internal function to compute that instead of creating an ODEProblem each time which would be pretty wasteful.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hopefully solved by 23c81c5

@baggepinnen
Copy link
Contributor

I'm quite happy with the interface by now, and tests pass for simple systems. The Cartesian pendulum below does, however, not work to linearize

## Model with high index
@parameters t L g
@variables x(t)=0 y(t)=0 w(t)=0 z(t)=0 T(t) u(t)=0 [input=true]
D = Differential(t)

# Simple pendulum in cartesian coordinates
eqs = [D(x) ~ w,
    D(y) ~ z,
    D(w) ~ T * x + u,
    D(z) ~ T * y - g,
    0 ~ x^2 + y^2 - L^2]
pendulum = ODESystem(eqs, t, name = :pendulum)

lsys, ssys = linearize(pendulum, [u], [x,y])
julia> lsys, ssys = linearize(pendulum, [u], [x,y])
ERROR: The LHS operator must be unique. Please run `structural_simplify` or use the DAE form. Differential(t)(y(t)) appears in LHS more than once.
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] check_operator_variables(eqs::Vector{Equation}, op::Type{Differential})
   @ ModelingToolkit ~/.julia/dev/ModelingToolkit/src/utils.jl:296
 [3] linearization_function(sys::ODESystem, inputs::Vector{Num}, outputs::Vector{Num}; simplify::Bool, kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ ModelingToolkit ~/.julia/dev/ModelingToolkit/src/systems/abstractsystem.jl:1015
 [4] linearization_function
   @ ~/.julia/dev/ModelingToolkit/src/systems/abstractsystem.jl:994 [inlined]
 [5] linearize(sys::ODESystem, inputs::Vector{Num}, outputs::Vector{Num}; op::Dict{Any, Any}, allow_input_derivatives::Bool, kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ ModelingToolkit ~/.julia/dev/ModelingToolkit/src/systems/abstractsystem.jl:1228
 [6] linearize(sys::ODESystem, inputs::Vector{Num}, outputs::Vector{Num})
   @ ModelingToolkit ~/.julia/dev/ModelingToolkit/src/systems/abstractsystem.jl:1226
 [7] top-level scope
   @ REPL[12]:1

@YingboMa
Copy link
Member Author

Yeah, because it's a DAE. It will be handled later.

@baggepinnen
Copy link
Contributor

Then this PR should be good to go for now

function linearize(sys, lin_fun; t = 0.0, op = Dict(), allow_input_derivatives = false,
p = DiffEqBase.NullParameters())
x0 = merge(defaults(sys), op)
f, u0, p = process_DEProblem(ODEFunction{true}, sys, x0, p)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual wasteful part is the function construction, since f is exactly lin_fun. Also, beware that ODEProblem/process_DEProblem does NOT necessarily give consistent initial condition.

@YingboMa
Copy link
Member Author

We can merge this for now and add improvements later.

@YingboMa YingboMa merged commit bb0fa0a into master Jul 11, 2022
@YingboMa YingboMa deleted the myb/linearize branch July 11, 2022 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants