Skip to content

Cherry pick doc and general improvements commits from hessian_objective #200

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 6 commits into from
May 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ Documenter = "1"
JuMP = "1"
LinearAlgebra = "1.10"
Logging = "1.10"
ModelingToolkit = "9.50"
ModelingToolkit = "9.50 - 9.76"
Plots = "1"
3 changes: 2 additions & 1 deletion docs/src/manual/mtk.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ the last section.
will work for all corner cases.

!!! compat
The example relies on features and bugfixes of `ModelingToolkit.jl` v9.50.
The example works on `ModelingToolkit.jl` v9.50 to 9.76 (corresponding to the following
`[compat]` entry: `ModelingToolkit = "9.50 - 9.76"`).

We first construct and instantiate the pendulum model:

Expand Down
8 changes: 4 additions & 4 deletions src/controller/execute.jl
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ function optim_objective!(mpc::PredictiveController{NT}) where {NT<:Real}
model, optim = mpc.estim.model, mpc.optim
nu, Hc = model.nu, mpc.Hc
Z̃var::Vector{JuMP.VariableRef} = optim[:Z̃var]
Z̃0 = set_warmstart!(mpc, mpc.transcription, Z̃var)
Z̃s = set_warmstart!(mpc, mpc.transcription, Z̃var)
set_objective_linear_coef!(mpc, Z̃var)
try
JuMP.optimize!(optim)
Expand All @@ -405,7 +405,7 @@ function optim_objective!(mpc::PredictiveController{NT}) where {NT<:Real}
MOIU.reset_optimizer(optim)
JuMP.optimize!(optim)
else
rethrow(err)
rethrow()
end
end
if !issolved(optim)
Expand All @@ -426,7 +426,7 @@ function optim_objective!(mpc::PredictiveController{NT}) where {NT<:Real}
@debug info2debugstr(getinfo(mpc))
end
if iserror(optim)
mpc.Z̃ .= Z̃0
mpc.Z̃ .= Z̃s
else
mpc.Z̃ .= JuMP.value.(Z̃var)
end
Expand Down Expand Up @@ -488,7 +488,7 @@ Call `periodsleep(mpc.estim.model)`.
periodsleep(mpc::PredictiveController, busywait=false) = periodsleep(mpc.estim.model, busywait)

"""
setstate!(mpc::PredictiveController, x̂, P̂=nothing) -> mpc
setstate!(mpc::PredictiveController, x̂[, P̂]) -> mpc

Call [`setstate!`](@ref) on `mpc.estim` [`StateEstimator`](@ref).
"""
Expand Down
36 changes: 18 additions & 18 deletions src/controller/transcription.jl
Original file line number Diff line number Diff line change
Expand Up @@ -952,13 +952,13 @@ linconstrainteq!(::PredictiveController, ::SimModel, ::SingleShooting) = nothing
linconstrainteq!(::PredictiveController, ::SimModel, ::MultipleShooting) = nothing

@doc raw"""
set_warmstart!(mpc::PredictiveController, transcription::SingleShooting, Z̃var) -> Z̃0
set_warmstart!(mpc::PredictiveController, transcription::SingleShooting, Z̃var) -> Z̃s

Set and return the warm start value of `Z̃var` for [`SingleShooting`](@ref) transcription.

If supported by `mpc.optim`, it warm-starts the solver at:
```math
\mathbf{ΔŨ} =
\mathbf{Z̃_s} =
\begin{bmatrix}
\mathbf{Δu}(k+0|k-1) \\
\mathbf{Δu}(k+1|k-1) \\
Expand All @@ -972,24 +972,24 @@ where ``\mathbf{Δu}(k+j|k-1)`` is the input increment for time ``k+j`` computed
last control period ``k-1``, and ``ϵ(k-1)``, the slack variable of the last control period.
"""
function set_warmstart!(mpc::PredictiveController, transcription::SingleShooting, Z̃var)
nu, Hc, Z̃0 = mpc.estim.model.nu, mpc.Hc, mpc.buffer.Z̃
nu, Hc, Z̃s = mpc.estim.model.nu, mpc.Hc, mpc.buffer.Z̃
# --- input increments ΔU ---
Z̃0[1:(Hc*nu-nu)] .= @views mpc.Z̃[nu+1:Hc*nu]
Z̃0[(Hc*nu-nu+1):(Hc*nu)] .= 0
Z̃s[1:(Hc*nu-nu)] .= @views mpc.Z̃[nu+1:Hc*nu]
Z̃s[(Hc*nu-nu+1):(Hc*nu)] .= 0
# --- slack variable ϵ ---
mpc.nϵ == 1 && (Z̃0[end] = mpc.Z̃[end])
JuMP.set_start_value.(Z̃var, Z̃0)
return Z̃0
mpc.nϵ == 1 && (Z̃s[end] = mpc.Z̃[end])
JuMP.set_start_value.(Z̃var, Z̃s)
return Z̃s
end

@doc raw"""
set_warmstart!(mpc::PredictiveController, transcription::MultipleShooting, Z̃var) -> Z̃0
set_warmstart!(mpc::PredictiveController, transcription::MultipleShooting, Z̃var) -> Z̃s

Set and return the warm start value of `Z̃var` for [`MultipleShooting`](@ref) transcription.

It warm-starts the solver at:
```math
\mathbf{ΔŨ} =
\mathbf{Z̃_s} =
\begin{bmatrix}
\mathbf{Δu}(k+0|k-1) \\
\mathbf{Δu}(k+1|k-1) \\
Expand All @@ -1009,17 +1009,17 @@ last control period ``k-1``, expressed as a deviation from the operating point
``\mathbf{x̂_{op}}``.
"""
function set_warmstart!(mpc::PredictiveController, transcription::MultipleShooting, Z̃var)
nu, nx̂, Hp, Hc, Z̃0 = mpc.estim.model.nu, mpc.estim.nx̂, mpc.Hp, mpc.Hc, mpc.buffer.Z̃
nu, nx̂, Hp, Hc, Z̃s = mpc.estim.model.nu, mpc.estim.nx̂, mpc.Hp, mpc.Hc, mpc.buffer.Z̃
# --- input increments ΔU ---
Z̃0[1:(Hc*nu-nu)] .= @views mpc.Z̃[nu+1:Hc*nu]
Z̃0[(Hc*nu-nu+1):(Hc*nu)] .= 0
Z̃s[1:(Hc*nu-nu)] .= @views mpc.Z̃[nu+1:Hc*nu]
Z̃s[(Hc*nu-nu+1):(Hc*nu)] .= 0
# --- predicted states X̂0 ---
Z̃0[(Hc*nu+1):(Hc*nu+Hp*nx̂-nx̂)] .= @views mpc.Z̃[(Hc*nu+nx̂+1):(Hc*nu+Hp*nx̂)]
Z̃0[(Hc*nu+Hp*nx̂-nx̂+1):(Hc*nu+Hp*nx̂)] .= @views mpc.Z̃[(Hc*nu+Hp*nx̂-nx̂+1):(Hc*nu+Hp*nx̂)]
Z̃s[(Hc*nu+1):(Hc*nu+Hp*nx̂-nx̂)] .= @views mpc.Z̃[(Hc*nu+nx̂+1):(Hc*nu+Hp*nx̂)]
Z̃s[(Hc*nu+Hp*nx̂-nx̂+1):(Hc*nu+Hp*nx̂)] .= @views mpc.Z̃[(Hc*nu+Hp*nx̂-nx̂+1):(Hc*nu+Hp*nx̂)]
# --- slack variable ϵ ---
mpc.nϵ == 1 && (Z̃0[end] = mpc.Z̃[end])
JuMP.set_start_value.(Z̃var, Z̃0)
return Z̃0
mpc.nϵ == 1 && (Z̃s[end] = mpc.Z̃[end])
JuMP.set_start_value.(Z̃var, Z̃s)
return Z̃s
end

getΔŨ!(ΔŨ, mpc::PredictiveController, ::SingleShooting, Z̃) = (ΔŨ .= Z̃) # since mpc.P̃Δu = I
Expand Down
2 changes: 1 addition & 1 deletion src/estimator/execute.jl
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ function validate_args(estim::StateEstimator, ym, d, u=nothing)
end

"""
setstate!(estim::StateEstimator, x̂, P̂=nothing) -> estim
setstate!(estim::StateEstimator, x̂[, P̂]) -> estim

Set `estim.x̂0` to `x̂ - estim.x̂op` from the argument `x̂`, and `estim.P̂` to `P̂` if applicable.

Expand Down
16 changes: 8 additions & 8 deletions src/estimator/mhe/execute.jl
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ Optimize objective of `estim` [`MovingHorizonEstimator`](@ref) and return the so

If supported by `estim.optim`, it warm-starts the solver at:
```math
\mathbf{} =
\mathbf{Z̃_s} =
\begin{bmatrix}
ϵ_{k-1} \\
\mathbf{x̂}_{k-1}(k-N_k+p) \\
Expand All @@ -391,12 +391,12 @@ function optim_objective!(estim::MovingHorizonEstimator{NT}) where NT<:Real
X̂0 = Vector{NT}(undef, nx̂*Nk)
û0, ŷ0, x̄, k0 = buffer.û, buffer.ŷ, buffer.x̂, buffer.k
ϵ_0 = estim.nϵ ≠ 0 ? estim.Z̃[begin] : empty(estim.Z̃)
Z̃_0 = [ϵ_0; estim.x̂0arr_old; estim.Ŵ]
V̂, X̂0 = predict!(V̂, X̂0, û0, k0, ŷ0, estim, model, Z̃_0)
J_0 = obj_nonlinprog!(x̄, estim, model, V̂, Z̃_0)
# initial Z̃0 with Ŵ=0 if objective or constraint function not finite :
isfinite(J_0) || (Z̃_0 = [ϵ_0; estim.x̂0arr_old; zeros(NT, nŵ*estim.He)])
JuMP.set_start_value.(Z̃var, Z̃_0)
Z̃s = [ϵ_0; estim.x̂0arr_old; estim.Ŵ]
V̂, X̂0 = predict!(V̂, X̂0, û0, k0, ŷ0, estim, model, Z̃s)
J_0 = obj_nonlinprog!(x̄, estim, model, V̂, Z̃s)
# warm-start Z̃s with Ŵ=0 if objective or constraint function not finite :
isfinite(J_0) || (Z̃s = [ϵ_0; estim.x̂0arr_old; zeros(NT, nŵ*estim.He)])
JuMP.set_start_value.(Z̃var, Z̃s)
# ------- solve optimization problem --------------
try
JuMP.optimize!(optim)
Expand Down Expand Up @@ -428,7 +428,7 @@ function optim_objective!(estim::MovingHorizonEstimator{NT}) where NT<:Real
@debug info2debugstr(getinfo(estim))
end
if iserror(optim)
estim.Z̃ .= Z̃_0
estim.Z̃ .= Z̃s
else
estim.Z̃ .= JuMP.value.(Z̃var)
end
Expand Down
2 changes: 1 addition & 1 deletion src/general.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function limit_solve_time(optim::GenericModel, Ts)
if isa(err, MOI.UnsupportedAttribute{MOI.TimeLimitSec})
@warn "Solving time limit is not supported by the optimizer."
else
rethrow(err)
rethrow()
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/model/nonlinmodel.jl
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ form. The optional parameter `NT` explicitly set the number type of vectors (def
!!! warning
The two functions must be in pure Julia to use the model in [`NonLinMPC`](@ref),
[`ExtendedKalmanFilter`](@ref), [`MovingHorizonEstimator`](@ref) and [`linearize`](@ref),
except if a finite difference backend is used (e.g. [`AutoFiniteDiff`](@extref DifferentiationInterface List).
except if a finite difference backend is used (e.g. [`AutoFiniteDiff`](@extref DifferentiationInterface List)).

See also [`LinModel`](@ref).

Expand Down
2 changes: 1 addition & 1 deletion test/2_test_state_estim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1364,7 +1364,7 @@ end
X̂_mhe = zeros(4, 6)
X̂_kf = zeros(4, 6)
for i in 1:6
y = [50,31] #+ randn(2)
y = [50,31] + randn(2)
x̂_mhe = preparestate!(mhe, y, [25])
x̂_kf = preparestate!(kf, y, [25])
X̂_mhe[:,i] = x̂_mhe
Expand Down