@@ -57,12 +57,17 @@ n_u = size(g, 2)
5757using SumOfSquares
5858rectangle = [1.7 , 0.85 , 0.8 , 1 , π/ 12 , π/ 2 , 1.5 , π/ 12 ]
5959X = BasicSemialgebraicSet (FullSpace (), typeof (x[1 ] + 1.0 )[])
60- for i in eachindex (x)
61- lower = x[i] + rectangle[i] # x[i] >= -rectangle[i]
62- upper = rectangle[i] - x[i] # x[i] <= rectangle[i]
63- addinequality! (X, lower * upper) # -rectangle[i] <= x[i] <= rectangle[i]
60+ function rect (vars, bounds)
61+ R = BasicSemialgebraicSet (FullSpace (), typeof (vars[1 ] + 1.0 )[])
62+ for (var, bound) in zip (vars, bounds)
63+ lower = var + bound # var >= -bound
64+ upper = bound - var # var <= bound
65+ addinequality! (R, lower * upper) # -bound <= var <= bound
66+ end
67+ return R
6468end
65- X
69+ X = rect (x, rectangle[1 : n_x])
70+ U = rect (u, rectangle[n_x .+ (1 : n_u)])
6671
6772# # Controller for the linearized system
6873
@@ -101,19 +106,25 @@ P, _, _ = MatrixEquations.arec(A - B * K, 0.0, 10.0)
101106
102107V0 = x' * P * x
103108
109+ # We can see that many terms have a coefficient that is almost zero:
110+
111+ zero_V0 = [monomial (t) for t in terms (V0) if abs (DynamicPolynomials. coefficient (t)) < 1e-8 ] # src
112+ [monomial (t) for t in terms (V0) if abs (DynamicPolynomials. coefficient (t)) < 1e-8 ]
113+
114+ # This might cause troubles in the optimization so let's drop them:
115+
116+ V0 = mapcoefficients (c -> (abs (c) < 1e-8 ? 0.0 : c), V0)
117+
104118# ## γ-step
105119
106120# It is a Lyapunov function for the linear system but not necessarily for the nonlinear system as well.
107121# We can however say that the γ-sublevel set `{x | x' P x ≤ γ}` is a (trivial) controlled invariant set for `γ = 0` (since it is empty).
108122# We can try to see if there a larger `γ` such that the γ-sublevel set is also controlled invariant using
109123# the γ step of [YAP21, Algorithm 1]
110124
111- function _create (model, d, P )
125+ function _create (model, d)
112126 if d isa Int
113- if P == SOSPoly # `d` is the maxdegree while for `SOSPoly`,
114- d = div (d, 2 ) # it is the monomial basis of the squares
115- end
116- return @variable (model, variable_type = P (monomials (x, 0 : d)))
127+ return @variable (model, variable_type = Poly (monomials (x, 0 : d)))
117128 else
118129 return d
119130 end
@@ -122,11 +133,16 @@ end
122133using LinearAlgebra
123134function base_model (solver, V, k, s3, γ)
124135 model = SOSModel (solver)
125- V = _create (model, V, Poly)
126- k = _create .(model, k, Poly)
127- s3 = _create (model, s3, SOSPoly)
136+ V = _create (model, V)
137+ k = _create .(model, k)
128138 ∂ = differentiate # Let's use a fancy shortcut
129- @constraint (model, ∂ (V, x) ⋅ (f + g * k) <= s3 * (V - γ)) # [YAP21, (E.2)]
139+ dV = ∂ (V, x) ⋅ (f + g * k)
140+ # [YAP21, (E.2)]
141+ if s3 isa Int
142+ @constraint (model, con, dV <= 0 , domain = @set (V <= γ))
143+ else
144+ @constraint (model, dV <= s3 * (V - γ))
145+ end
130146 for r in inequalities (X) # `{V ≤ γ} ⊆ {r ≥ 0}` iff `r ≤ 0 => V ≥ γ`
131147 @constraint (model, V >= γ, domain = @set (r <= 0 )) # [YAP21, (E.3)]
132148 end
@@ -152,7 +168,7 @@ function γ_step(solver, V, γ_min, degree_k, degree_s3; γ_tol = 1e-1, max_iter
152168 else
153169 break
154170 end
155- model, V, k, s3 = base_model (solver, V, degree_k, degree_s3, γ)
171+ model, V, k = base_model (solver, V, degree_k, degree_s3, γ)
156172 num_iters += 1
157173 @info (" Iteration $num_iters /$max_iters : Solving with $(solver_name (model)) for `γ = $γ `" )
158174 optimize! (model)
@@ -161,7 +177,7 @@ function γ_step(solver, V, γ_min, degree_k, degree_s3; γ_tol = 1e-1, max_iter
161177 @info (" Feasible solution found : primal is $(primal_status (model)) " )
162178 γ_min = γ
163179 k_best = value .(k)
164- s3_best = value (s3)
180+ s3_best = lagrangian_multipliers (model[ :con ])[ 1 ]
165181 elseif dual_status (model) == MOI. INFEASIBILITY_CERTIFICATE
166182 @info (" Infeasibility certificate found : dual is $(dual_status (model)) " )
167183 if γ == γ0_min # This corresponds to the case above where we reached the tol or max iteration and we just did a last run at the value of `γ_min` provided by the user
@@ -186,6 +202,15 @@ import CSDP
186202solver = optimizer_with_attributes (CSDP. Optimizer, MOI. Silent () => true )
187203γ1, κ1, s3_1 = γ_step (solver, V0, 0.0 , [2 , 2 ], 2 )
188204@test γ1 ≈ 0.5 atol = 1.e-1 # src
205+ nothing
206+
207+ # The best `γ` found is
208+
209+ γ1
210+
211+ # with state feedback:
212+
213+ κ1
189214
190215# Let's visualize now the controlled invariant set we have found:
191216
@@ -231,9 +256,12 @@ function V_step(solver, V0, γ, k, s3)
231256end
232257
233258model, V1 = V_step (solver, V0, γ1, κ1, s3_1)
234- solution_summary (model)
259+ nothing # hide
235260
236261# We can see that the solver found a feasible solution.
262+
263+ solution_summary (model)
264+
237265# Let's compare it:
238266
239267push! (Vs, V1 - γ1)
@@ -246,6 +274,7 @@ plot_lyapunovs(Vs, [1, 2])
246274# want to find a better `κ` so let's set `max_iters` to zero
247275
248276γ2, κ2, s3_2 = γ_step (solver, V0, γ1, [2 , 2 ], 2 , max_iters = 0 )
277+ nothing # hide
249278
250279# Let's see if this new controller allows to find a better Lyapunov.
251280
@@ -330,6 +359,17 @@ eigen(Symmetric(Px * (A + B * K) + (A + B * K)' * Px)).values
330359
331360support_V0 = x' * Px * x
332361
362+ # We can see that `support_V0` shares the same monomial with almost zero coefficient
363+ # with `V0`.
364+
365+ zero_support_V0 = [monomial (t) for t in terms (support_V0) if abs (DynamicPolynomials. coefficient (t)) < 1e-8 ] # src
366+ @test zero_support_V0 == zero_V0
367+ [monomial (t) for t in terms (support_V0) if abs (DynamicPolynomials. coefficient (t)) < 1e-8 ]
368+
369+ # Let's drop them for `support_V0` as well:
370+
371+ support_V0 = mapcoefficients (c -> (abs (c) < 1e-8 ? 0.0 : c), support_V0)
372+
333373# `support_V0 - 1` is a controlled invariant set for the linearized system
334374# but not for the nonlinear one. We can plot it to visualize but
335375# it is not comparable to the ones found before which were
0 commit comments