@@ -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  #  hide
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