Skip to content

Commit 7c41bc5

Browse files
committed
doc: long url separated from the text
1 parent 574b0a3 commit 7c41bc5

File tree

7 files changed

+87
-65
lines changed

7 files changed

+87
-65
lines changed

src/ModelPredictiveControl.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module ModelPredictiveControl
22

3-
using PrecompileTools
3+
using PrecompileTools # TODO: remove this dep if possible (with Cache of DI.jl)
44
using LinearAlgebra
55
using Random: randn
66

src/controller/nonlinmpc.jl

+59-52
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,14 @@ This controller allocates memory at each time step for the optimization.
197197
controller, provided as a [`JuMP.Model`](https://jump.dev/JuMP.jl/stable/api/JuMP/#JuMP.Model)
198198
(default to [`Ipopt`](https://github.com/jump-dev/Ipopt.jl) optimizer).
199199
- `gradient=AutoForwardDiff()` : an `AbstractADType` backend for the gradient of the objective
200-
function, see [`DifferentiationInterface`](https://juliadiff.org/DifferentiationInterface.jl/DifferentiationInterface/stable/explanation/backends/#List).
200+
function, see [`DifferentiationInterface`][1].
201201
- `jacobian=default_jacobian(transcription)` : an `AbstractADType` backend for the Jacobian
202202
of the nonlinear constraints, see `gradient` above for the options (default in Extended Help).
203203
- additional keyword arguments are passed to [`UnscentedKalmanFilter`](@ref) constructor
204204
(or [`SteadyKalmanFilter`](@ref), for [`LinModel`](@ref)).
205205
206+
[1]: https://juliadiff.org/DifferentiationInterface.jl/DifferentiationInterface/stable/explanation/backends/#List
207+
206208
# Examples
207209
```jldoctest
208210
julia> model = NonLinModel((x,u,_,_)->0.5x+u, (x,_,_)->2x, 10.0, 1, 1, 1, solver=nothing);
@@ -248,14 +250,26 @@ NonLinMPC controller with a sample time Ts = 10.0 s, Ipopt optimizer, UnscentedK
248250
The keyword argument `nc` is the number of elements in `LHS`, and `gc!`, an alias for
249251
the `gc` argument (both `gc` and `gc!` accepts non-mutating and mutating functions).
250252
251-
The optimization relies on [`JuMP`](https://github.com/jump-dev/JuMP.jl) automatic
252-
differentiation (AD) to compute the objective and constraint derivatives. Optimizers
253-
generally benefit from exact derivatives like AD. However, the [`NonLinModel`](@ref)
254-
state-space functions must be compatible with this feature. See [Automatic differentiation](https://jump.dev/JuMP.jl/stable/manual/nlp/#Automatic-differentiation)
253+
By default, the optimization relies on dense [`ForwardDiff`](https://github.com/JuliaDiff/ForwardDiff.jl)
254+
automatic differentiation (AD) to compute the objective and constraint derivatives. One
255+
exception: if `transcription` is not a [`SingleShooting`](@ref), the `jacobian` argument
256+
defaults to this [sparse backend][2]:
257+
```julia
258+
AutoSparse(
259+
AutoForwardDiff();
260+
sparsity_detector = TracerSparsityDetector(),
261+
coloring_algorithm = GreedyColoringAlgorithm()
262+
)
263+
```
264+
Optimizers generally benefit from exact derivatives like AD. However, the [`NonLinModel`](@ref)
265+
state-space functions must be compatible with this feature. See `JuMP` [documentation][3]
255266
for common mistakes when writing these functions.
256267
257268
Note that if `Cwt≠Inf`, the attribute `nlp_scaling_max_gradient` of `Ipopt` is set to
258269
`10/Cwt` (if not already set), to scale the small values of ``ϵ``.
270+
271+
[2]: https://juliadiff.org/DifferentiationInterface.jl/DifferentiationInterface/stable/explanation/advanced/#Sparsity
272+
[3]: https://jump.dev/JuMP.jl/stable/manual/nonlinear/#Common-mistakes-when-writing-a-user-defined-operator
259273
"""
260274
function NonLinMPC(
261275
model::SimModel;
@@ -567,13 +581,16 @@ This method is really intricate and I'm not proud of it. That's because of 3 ele
567581
- These functions are used inside the nonlinear optimization, so they must be type-stable
568582
and as efficient as possible.
569583
- The `JuMP` NLP syntax forces splatting for the decision variable, which implies use
570-
of `Vararg{T,N}` (see the [performance tip](https://docs.julialang.org/en/v1/manual/performance-tips/#Be-aware-of-when-Julia-avoids-specializing))
571-
and memoization to avoid redundant computations. This is already complex, but it's even
572-
worse knowing that most automatic differentiation tools do not support splatting.
584+
of `Vararg{T,N}` (see the [performance tip][1]) and memoization to avoid redundant
585+
computations. This is already complex, but it's even worse knowing that most automatic
586+
differentiation tools do not support splatting.
573587
- The signature of gradient and hessian functions is not the same for univariate (`nZ̃ == 1`)
574588
and multivariate (`nZ̃ > 1`) operators in `JuMP`. Both must be defined.
575589
576-
Inspired from: [User-defined operators with vector outputs](https://jump.dev/JuMP.jl/stable/tutorials/nonlinear/tips_and_tricks/#User-defined-operators-with-vector-outputs)
590+
Inspired from: [User-defined operators with vector outputs][2]
591+
592+
[1]: https://docs.julialang.org/en/v1/manual/performance-tips/#Be-aware-of-when-Julia-avoids-specializing
593+
[2]: https://jump.dev/JuMP.jl/stable/tutorials/nonlinear/tips_and_tricks/#User-defined-operators-with-vector-outputs
577594
"""
578595
function get_optim_functions(
579596
mpc::NonLinMPC,
@@ -583,30 +600,24 @@ function get_optim_functions(
583600
) where JNT<:Real
584601
model, transcription = mpc.estim.model, mpc.transcription
585602
# --------------------- update simulation function ------------------------------------
586-
function update_simulations!(Z̃arg, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
587-
if isdifferent(Z̃arg, Z̃)
588-
for i in eachindex(Z̃)
589-
# Z̃cache .= Z̃arg is type unstable with Z̃arg::NTuple{N, FowardDiff.Dual}
590-
Z̃[i] = Z̃arg[i]
591-
end
592-
U0 = getU0!(U0, mpc, Z̃)
593-
ΔŨ = getΔŨ!(ΔŨ, mpc, transcription, Z̃)
594-
Ŷ0, x̂0end = predict!(Ŷ0, x̂0end, X̂0, Û0, mpc, model, transcription, U0, Z̃)
595-
Ue, Ŷe = extended_vectors!(Ue, Ŷe, mpc, U0, Ŷ0)
596-
ϵ = getϵ(mpc, Z̃)
597-
gc = con_custom!(gc, mpc, Ue, Ŷe, ϵ)
598-
g = con_nonlinprog!(g, mpc, model, transcription, x̂0end, Ŷ0, gc, ϵ)
599-
geq = con_nonlinprogeq!(geq, X̂0, Û0, mpc, model, transcription, U0, Z̃)
600-
end
603+
function update_simulations!(Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
604+
U0 = getU0!(U0, mpc, Z̃)
605+
ΔŨ = getΔŨ!(ΔŨ, mpc, transcription, Z̃)
606+
Ŷ0, x̂0end = predict!(Ŷ0, x̂0end, X̂0, Û0, mpc, model, transcription, U0, Z̃)
607+
Ue, Ŷe = extended_vectors!(Ue, Ŷe, mpc, U0, Ŷ0)
608+
ϵ = getϵ(mpc, Z̃)
609+
gc = con_custom!(gc, mpc, Ue, Ŷe, ϵ)
610+
g = con_nonlinprog!(g, mpc, model, transcription, x̂0end, Ŷ0, gc, ϵ)
611+
geq = con_nonlinprogeq!(geq, X̂0, Û0, mpc, model, transcription, U0, Z̃)
601612
return nothing
602613
end
603-
# ---------------------- JNT vectors cache --------------------------------------------
614+
# ----- common cache for Jfunc, gfuncs, geqfuncs called with floats -------------------
604615
nu, ny, nx̂, nϵ, Hp, Hc = model.nu, model.ny, mpc.estim.nx̂, mpc.nϵ, mpc.Hp, mpc.Hc
605616
ng, nc, neq = length(mpc.con.i_g), mpc.con.nc, mpc.con.neq
606617
nZ̃, nU, nŶ, nX̂ = length(mpc.Z̃), Hp*nu, Hp*ny, Hp*nx̂
607618
nΔŨ, nUe, nŶe = nu*Hc + nϵ, nU + nu, nŶ + ny
608619
myNaN = convert(JNT, NaN)
609-
= fill(myNaN, nZ̃)
620+
= fill(myNaN, nZ̃) # NaN to force update_simulations! at first call
610621
ΔŨ = zeros(JNT, nΔŨ)
611622
x̂0end = zeros(JNT, nx̂)
612623
Ue, Ŷe = zeros(JNT, nUe), zeros(JNT, nŶe)
@@ -616,19 +627,20 @@ function get_optim_functions(
616627
geq = zeros(JNT, neq)
617628
# ---------------------- objective function ------------------------------------------
618629
function Jfunc(Z̃arg::Vararg{T, N}) where {N, T<:Real}
619-
update_simulations!(Z̃arg, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
630+
if isdifferent(Z̃arg, Z̃)
631+
Z̃ .= Z̃arg
632+
update_simulations!(Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
633+
end
620634
return obj_nonlinprog!(Ŷ0, U0, mpc, model, Ue, Ŷe, ΔŨ)::T
621635
end
622-
function Jfunc!(Z̃arg, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
623-
update_simulations!(Z̃arg, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
636+
function Jfunc!(Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
637+
update_simulations!(Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
624638
return obj_nonlinprog!(Ŷ0, U0, mpc, model, Ue, Ŷe, ΔŨ)
625639
end
626640
Z̃_∇J = fill(myNaN, nZ̃)
627641
∇J = zeros(JNT, nZ̃) # gradient of objective J
628642
∇J_context = (
629-
Cache(Z̃), Cache(ΔŨ), Cache(x̂0end),
630-
Cache(Ue), Cache(Ŷe),
631-
Cache(U0), Cache(Ŷ0),
643+
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
632644
Cache(Û0), Cache(X̂0),
633645
Cache(gc), Cache(g), Cache(geq)
634646
)
@@ -650,33 +662,29 @@ function get_optim_functions(
650662
gfuncs = Vector{Function}(undef, ng)
651663
for i in eachindex(gfuncs)
652664
gfunc_i = function (Z̃arg::Vararg{T, N}) where {N, T<:Real}
653-
update_simulations!(Z̃arg, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
665+
if isdifferent(Z̃arg, Z̃)
666+
Z̃ .= Z̃arg
667+
update_simulations!(Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
668+
end
654669
return g[i]::T
655670
end
656671
gfuncs[i] = gfunc_i
657672
end
658-
function gfunc!(g, Z̃arg, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, geq)
659-
update_simulations!(Z̃arg, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
660-
return g
673+
function gfunc!(g, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, geq)
674+
return update_simulations!(Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
661675
end
662676
Z̃_∇g = fill(myNaN, nZ̃)
663677
∇g = zeros(JNT, ng, nZ̃) # Jacobian of inequality constraints g
664678
∇g_context = (
665-
Cache(Z̃), Cache(ΔŨ), Cache(x̂0end),
666-
Cache(Ue), Cache(Ŷe),
667-
Cache(U0), Cache(Ŷ0),
679+
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
668680
Cache(Û0), Cache(X̂0),
669681
Cache(gc), Cache(geq)
670682
)
671-
672-
673-
# TODO: cleanup the next part:
683+
# temporarily enable all the inequality constraints for sparsity pattern detection:
674684
i_g_old = copy(mpc.con.i_g)
675685
mpc.con.i_g .= true
676686
∇g_prep = prepare_jacobian(gfunc!, g, jac_backend, Z̃_∇g, ∇g_context...)
677687
mpc.con.i_g .= i_g_old
678-
679-
680688
∇gfuncs! = Vector{Function}(undef, ng)
681689
for i in eachindex(∇gfuncs!)
682690
∇gfuncs_i! = if nZ̃ == 1
@@ -692,7 +700,6 @@ function get_optim_functions(
692700
if isdifferent(Z̃arg, Z̃_∇g)
693701
Z̃_∇g .= Z̃arg
694702
jacobian!(gfunc!, g, ∇g, ∇g_prep, jac_backend, Z̃_∇g, ∇g_context...)
695-
display(∇g)
696703
end
697704
return ∇g_i .= @views ∇g[i, :] # multivariate syntax, see JuMP.@operator doc
698705
end
@@ -703,21 +710,21 @@ function get_optim_functions(
703710
geqfuncs = Vector{Function}(undef, neq)
704711
for i in eachindex(geqfuncs)
705712
geqfunc_i = function (Z̃arg::Vararg{T, N}) where {N, T<:Real}
706-
update_simulations!(Z̃arg, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
713+
if isdifferent(Z̃arg, Z̃)
714+
Z̃ .= Z̃arg
715+
update_simulations!(Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
716+
end
707717
return geq[i]::T
708718
end
709719
geqfuncs[i] = geqfunc_i
710720
end
711-
function geqfunc!(geq, Z̃arg, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g)
712-
update_simulations!(Z̃arg, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
713-
return geq
721+
function geqfunc!(geq, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g)
722+
return update_simulations!(Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
714723
end
715724
Z̃_∇geq = fill(myNaN, nZ̃)
716725
∇geq = zeros(JNT, neq, nZ̃) # Jacobian of equality constraints geq
717726
∇geq_context = (
718-
Cache(Z̃), Cache(ΔŨ), Cache(x̂0end),
719-
Cache(Ue), Cache(Ŷe),
720-
Cache(U0), Cache(Ŷ0),
727+
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
721728
Cache(Û0), Cache(X̂0),
722729
Cache(gc), Cache(g)
723730
)

src/estimator/kalman.jl

+6-3
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,7 @@ end
8080
8181
Construct a steady-state Kalman Filter with the [`LinModel`](@ref) `model`.
8282
83-
The steady-state (or [asymptotic](https://en.wikipedia.org/wiki/Kalman_filter#Asymptotic_form))
84-
Kalman filter is based on the process model :
83+
The steady-state (or [asymptotic][1]) Kalman filter is based on the process model :
8584
```math
8685
\begin{aligned}
8786
\mathbf{x}(k+1) &=
@@ -125,6 +124,8 @@ state of the next time step ``\mathbf{x̂}_k(k+1)``. This estimator is allocatio
125124
- `direct=true`: construct with a direct transmission from ``\mathbf{y^m}`` (a.k.a. current
126125
estimator, in opposition to the delayed/predictor form).
127126
127+
[1]: https://en.wikipedia.org/wiki/Kalman_filter#Asymptotic_form
128+
128129
# Examples
129130
```jldoctest
130131
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 0.5);
@@ -160,9 +161,11 @@ SteadyKalmanFilter estimator with a sample time Ts = 0.5 s, LinModel and:
160161
!!! tip
161162
Increasing `σQint_u` and `σQint_ym` values increases the integral action "gain".
162163
163-
The constructor pre-compute the steady-state Kalman gain `K̂` with the [`kalman`](https://juliacontrol.github.io/ControlSystems.jl/stable/lib/synthesis/#ControlSystemsBase.kalman-Tuple{Any,%20Any,%20Any,%20Any,%20Any,%20Vararg{Any}})
164+
The constructor pre-compute the steady-state Kalman gain `K̂` with the [`kalman`][2]
164165
function. It can sometimes fail, for example when `model` matrices are ill-conditioned.
165166
In such a case, you can try the alternative time-varying [`KalmanFilter`](@ref).
167+
168+
[2]: https://juliacontrol.github.io/ControlSystems.jl/stable/lib/synthesis/#ControlSystemsBase.kalman-Tuple{Any,%20Any,%20Any,%20Any,%20Any,%20Vararg{Any}}
166169
"""
167170
function SteadyKalmanFilter(
168171
model::SM;

src/estimator/mhe/construct.jl

+6-2
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,14 @@ transcription for now.
261261
with a quadratic/nonlinear optimizer for solving (default to [`Ipopt`](https://github.com/jump-dev/Ipopt.jl),
262262
or [`OSQP`](https://osqp.org/docs/parsers/jump.html) if `model` is a [`LinModel`](@ref)).
263263
- `gradient=AutoForwardDiff()` : an `AbstractADType` backend for the gradient of the objective
264-
function if `model` is not a [`LinModel`](@ref), see [`DifferentiationInterface`](https://juliadiff.org/DifferentiationInterface.jl/DifferentiationInterface/stable/explanation/backends/#List).
264+
function if `model` is not a [`LinModel`](@ref), see [`DifferentiationInterface`][1].
265265
- `jacobian=AutoForwardDiff()` : an `AbstractADType` backend for the Jacobian of the
266266
constraints if `model` is not a [`LinModel`](@ref), see `gradient` above for the options.
267267
- `direct=true`: construct with a direct transmission from ``\mathbf{y^m}`` (a.k.a. current
268268
estimator, in opposition to the delayed/predictor form).
269269
270+
[1]: https://juliadiff.org/DifferentiationInterface.jl/DifferentiationInterface/stable/explanation/backends/#List
271+
270272
# Examples
271273
```jldoctest
272274
julia> model = NonLinModel((x,u,_,_)->0.1x+u, (x,_,_)->2x, 10.0, 1, 1, 1, solver=nothing);
@@ -346,9 +348,11 @@ MovingHorizonEstimator estimator with a sample time Ts = 10.0 s, Ipopt optimizer
346348
default, a [`KalmanFilter`](@ref) estimates the arrival covariance (customizable).
347349
- Else, a nonlinear program with automatic differentiation (AD) solves the optimization.
348350
Optimizers generally benefit from exact derivatives like AD. However, the `f` and `h`
349-
functions must be compatible with this feature. See [Automatic differentiation](https://jump.dev/JuMP.jl/stable/manual/nlp/#Automatic-differentiation)
351+
functions must be compatible with this feature. See the `JuMP` [documentation][3]
350352
for common mistakes when writing these functions. An [`UnscentedKalmanFilter`](@ref)
351353
estimates the arrival covariance by default.
354+
355+
[3]: https://jump.dev/JuMP.jl/stable/manual/nonlinear/#Common-mistakes-when-writing-a-user-defined-operator
352356
353357
The slack variable ``ϵ`` relaxes the constraints if enabled, see [`setconstraint!`](@ref).
354358
It is disabled by default for the MHE (from `Cwt=Inf`) but it should be activated for

src/model/linearization.jl

+3-1
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,10 @@ julia> linmodel.A
131131
equations are similar if the nonlinear model has nonzero operating points.
132132
133133
Automatic differentiation (AD) allows exact Jacobians. The [`NonLinModel`](@ref) `f` and
134-
`h` functions must be compatible with this feature though. See [Automatic differentiation](https://jump.dev/JuMP.jl/stable/manual/nlp/#Automatic-differentiation)
134+
`h` functions must be compatible with this feature though. See `JuMP` [documentation][3]
135135
for common mistakes when writing these functions.
136+
137+
[3]: https://jump.dev/JuMP.jl/stable/manual/nonlinear/#Common-mistakes-when-writing-a-user-defined-operator
136138
"""
137139
function linearize(model::SimModel{NT}; kwargs...) where NT<:Real
138140
nu, nx, ny, nd = model.nu, model.nx, model.ny, model.nd

src/model/linmodel.jl

+8-5
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,12 @@ LinModel with a sample time Ts = 0.1 s and:
112112
\mathbf{y}(k) &= \mathbf{C x}(k) + \mathbf{D z}(k)
113113
\end{aligned}
114114
```
115-
Continuous dynamics are internally discretized using [`c2d`](https://juliacontrol.github.io/ControlSystems.jl/stable/lib/constructors/#ControlSystemsBase.c2d)
116-
and `:zoh` for manipulated inputs, and `:tustin`, for measured disturbances. Lastly, if
117-
`sys` is discrete and the provided argument `Ts ≠ sys.Ts`, the system is resampled by
118-
using the aforementioned discretization methods.
115+
Continuous dynamics are internally discretized using [`c2d`][1] and `:zoh` for
116+
manipulated inputs, and `:tustin`, for measured disturbances. Lastly, if `sys` is
117+
discrete and the provided argument `Ts ≠ sys.Ts`, the system is resampled by using the
118+
aforementioned discretization methods.
119119
120-
Note that the constructor transforms the system to its minimal realization using [`minreal`](https://juliacontrol.github.io/ControlSystems.jl/stable/lib/constructors/#ControlSystemsBase.minreal)
120+
Note that the constructor transforms the system to its minimal realization using [`minreal`][2]
121121
for controllability/observability. As a consequence, the final state-space
122122
representation may be different from the one provided in `sys`. It is also converted
123123
into a more practical form (``\mathbf{D_u=0}`` because of the zero-order hold):
@@ -129,6 +129,9 @@ LinModel with a sample time Ts = 0.1 s and:
129129
```
130130
Use the syntax [`LinModel{NT}(A, Bu, C, Bd, Dd, Ts)`](@ref) to force a specific
131131
state-space representation.
132+
133+
[1]: https://juliacontrol.github.io/ControlSystems.jl/stable/lib/constructors/#ControlSystemsBase.c2d
134+
[2]: https://juliacontrol.github.io/ControlSystems.jl/stable/lib/constructors/#ControlSystemsBase.minreal
132135
"""
133136
function LinModel(
134137
sys::StateSpace{E, NT},

src/model/nonlinmodel.jl

+4-1
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,13 @@ form. The optional parameter `NT` explicitly set the number type of vectors (def
9595
9696
!!! warning
9797
The two functions must be in pure Julia to use the model in [`NonLinMPC`](@ref),
98-
[`ExtendedKalmanFilter`](@ref), [`MovingHorizonEstimator`](@ref) and [`linearize`](@ref).
98+
[`ExtendedKalmanFilter`](@ref), [`MovingHorizonEstimator`](@ref) and [`linearize`](@ref),
99+
except if a finite difference backend is used (e.g. [`AutoFiniteDiff`][1]).
99100
100101
See also [`LinModel`](@ref).
101102
103+
[1]: https://juliadiff.org/DifferentiationInterface.jl/DifferentiationInterface/stable/explanation/backends/#List
104+
102105
# Examples
103106
```jldoctest
104107
julia> f!(ẋ, x, u, _ , p) = (ẋ .= p*x .+ u; nothing);

0 commit comments

Comments
 (0)