From 3b71817b5c281d662737709b609cb29b867d5612 Mon Sep 17 00:00:00 2001 From: arnauddeza Date: Mon, 3 Nov 2025 16:17:07 -0500 Subject: [PATCH 1/3] Class 2: 11/03 Checkpoint --- class02/class02.jl | 1333 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1333 insertions(+) create mode 100644 class02/class02.jl diff --git a/class02/class02.jl b/class02/class02.jl new file mode 100644 index 0000000..63200b9 --- /dev/null +++ b/class02/class02.jl @@ -0,0 +1,1333 @@ +### A Pluto.jl notebook ### +# v0.20.13 + +using Markdown +using InteractiveUtils + +# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error). +macro bind(def, element) + #! format: off + return quote + local iv = try Base.loaded_modules[Base.PkgId(Base.UUID("6e696c72-6542-2067-7265-42206c756150"), "AbstractPlutoDingetjes")].Bonds.initial_value catch; b -> missing; end + local el = $(esc(element)) + global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el) + el + end + #! format: on +end + +# ╔═╡ 7b896268-4336-47e2-a8b5-f985bfde51f5 +begin + import Pkg + + try + Pkg.activate(@__DIR__) + catch + # Fallback if @__DIR__ not available in your setup + end + Pkg.instantiate() + + # Add any missing packages + for pkg in [ + "PlutoUI", + "PlutoTeachingTools", + "MarkdownLiteral", + "ForwardDiff", + "Plots" + ] + if Base.find_package(pkg) === nothing + Pkg.add(pkg) + end + end + + using Markdown, InteractiveUtils, PlutoUI, PlutoTeachingTools, MarkdownLiteral, LinearAlgebra, ForwardDiff + import Plots + const plt = Plots + plt.default(size=(700,420)) +end + + +# ╔═╡ 81ebc291-89f0-4c1e-ac34-d5715977dd86 +md"""| | | | +|-----------:|:--|:------------------| +| Lecturer | : | Arnaud Deza | +| Date | : | 29 of August, 2025 |""" + +# ╔═╡ 9543f7bc-ab36-46ff-b471-9aa3db9739e4 +ChooseDisplayMode() + +# ╔═╡ 8969e78a-29b0-46d3-b6ba-59980208fe5b +md""" +#### Reference Material + +[^cmu11]: Z. Manchester, Optimal Control (CMU 16-745) 2025 Lecture 3, Carnegie Mellon University, YouTube, 2025. [Online]. Available: [https://www.youtube.com/watch?v=f7yF0KOV-sI](https://www.youtube.com/watch?v=f7yF0KOV-sI) + +[^cmu13]: Z. Manchester, Optimal Control (CMU 16-745) 2025 Lecture 4, Carnegie Mellon University, YouTube, 2025. [Online]. Available: [https://www.youtube.com/watch?v=lIuPIlDxLNU](https://www.youtube.com/watch?v=lIuPIlDxLNU) + +[^cmu13]: Z. Manchester, Optimal Control (CMU 16-745) 2025 Lecture 5, Carnegie Mellon University, YouTube, 2025. [Online]. Available: [https://www.youtube.com/watch?v=bsBXk17rff4](https://www.youtube.com/watch?v=bsBXk17rff4) +""" + +# ╔═╡ d90e9be0-7b68-4139-b185-6cbaad0d307e +md""" +### Some imports +""" + +# ╔═╡ 342decc1-43fa-432a-9a9c-757a10ba6a5d +md""" +# Part 0: Overview of Lecture 2 + +Lecture 2 is the course’s optimization backbone: it makes explicit the idea that most control problems are optimization problems in disguise. +We set the common language (gradients/Hessians, KKT systems, globalization) and the “solver toolbox” (penalty, augmented Lagrangian, primal–dual interior-point, SQP) that shows up everywhere else: +* MPC is a QP/NLP solved online; +* trajectory optimization is an NLP with sparse structure; +* distributed control leans on operator splitting; + +and modern differentiable controllers backprop through these solvers. + +The point isn’t to prove every theorem—it’s to give you a reliable recipe for turning a control task into a well-posed QP/NLP and picking/configuring a solver that actually converges. + +Positionally, this lecture is the hinge between “dynamics & modeling” (Class 1) and everything that follows: PMP/LQR reframed via KKT and Riccati; nonlinear trajectory optimization and collocation (Class 5); distributed MPC/ADMM (Class 8); GPU-accelerated solves (Class 9); and the learning side—adjoints for Neural DEs (Class 10) and optimization-as-a-layer for PINNs/neural operators (Classes 11–13). + +Concretely, you leave with (i) a map from control formulations to QP/NLP templates, (ii) rules of thumb for choosing between ALM, IPM, and SQP, and (iii) practical globalization (regularization + line search) so demos and projects are reproducible and fast. + +### General structure of lecture + +- **Root finding:** how implicit time-stepping (Backward Euler) turns into solving $r(x)=0$ each step. +- **Unconstrained minimization:** Newton’s method and why we need *globalization* (regularization + line search). +- **Constrained minimization:** KKT conditions in action for equality constraints. +- **Interior Point Method:** IPM, augmented lagragnian method. +- **SQP (Sequential Quadratic Programming):** how to reduce a nonlinear program to a sequence of easy QPs. +""" + +# ╔═╡ fd1ad74b-cb74-49a7-80b8-1a282abfdff2 +md""" +# Part I — Unconstrained Minimization as Root Finding + +In control, *implicit* simulators and optimizers show up everywhere: + +- **Implicit time stepping** (e.g., Backward Euler) for stability on stiff dynamics. +- **KKT systems** and **Newton steps** inside constrained optimization. +- **Adjoints** in learning-to-control are solutions of linearized/adjoint equations. + +All three reduce to solving nonlinear equations of the form $r(x)=0$. This section builds the minimal, reusable toolkit to do that reliably. + +**Example we use:** one implicit Backward Euler step for $\dot x = f(x)$: +$x_{n+1} = x_n + h\,f(x_{n+1}) \;\Rightarrow\; r(x) \equiv x_n + h\,f(x) - x = 0.$ + +We compare two solvers for $r(x)=0$: a **fixed-point** iteration and **Newton’s method**, and relate what you see to the theory (contraction vs. quadratic convergence). + +## From differential equations to algebraic equations + +Why not just use explicit Euler? For many control systems (pendulum near upright, contact, power systems) the dynamics can be **stiff**. Stability and larger steps favor **implicit** schemes, but those require solving $r(x)=0$ each step. + +For Backward Euler (BE), +$r(x) = x_n + h\,f(x) - x,$ +$\partial r(x) = h\,J_f(x) - I,$ +where $J_f$ is the Jacobian of $f$. Good solvers exploit this structure: the residual is “identity minus something small” for small $h$, and the Jacobian carries the physics through $J_f$. + +## Two algorithms we’ll compare + +**Fixed-point (Picard)** defines $g(x):=x_n + h\,f(x)$ and iterates +$x_{k+1}=g(x_k).$ +It converges if $g$ is a **contraction** near the root (intuitively, $h\,\|J_f\|$ small). The local linear rate is controlled by $\rho\!\left(J_g(x^\star)\right)=\rho\!\left(h\,J_f(x^\star)\right)$. + +**Newton’s method** linearizes $r$ and solves for a correction: +$[\partial r(x_k)]\,\Delta x_k = -\,r(x_k), \quad x_{k+1}=x_k+\Delta x_k,$ +so for BE, +$\underbrace{(h\,J_f(x_k)-I)}_{\text{BE Jacobian}}\Delta x_k = -\,(x_n+h\,f(x_k)-x_k).$ + +**Stopping & diagnostics.** We track the **residual norm** $\|r(x_k)\|$ and optionally the **step norm** $\|\Delta x_k\|$. Reporting both avoids “false convergence” when steps stagnate but residuals do not. + +## What the theorems (briefly) say + +- **Banach fixed-point theorem (Picard):** If $g$ is a contraction on a neighborhood of $x^\star$ (i.e., $\|J_g(x)\|\le q<1$), then $x_{k+1}=g(x_k)$ converges **linearly** to $x^\star$. For BE, $g'(x^\star)=h\,J_f(x^\star)$, so small $h$ or well-scaled $f$ helps. + +- **Newton’s method:** If $r\in C^2$, $\partial r(x^\star)$ is nonsingular, and $x_0$ is close enough to $x^\star$, then the iterates are **quadratically** convergent: +$\|x_{k+1}-x^\star\| \le C\,\|x_k-x^\star\|^2.$ +In practice we use **damping / line search** to reach the fast local regime from farther starts. + +**Takeaway:** Picard is simple but fragile; Newton has higher per-iteration cost but far fewer iterations and better robustness for implicit steps. +""" + +# ╔═╡ 49d5b2e6-eb29-478c-b817-8405d55170b1 +begin + """ + pendulum_dynamics(x) + + x = [θ; v], with θ̇ = v, v̇ = -(g/ℓ) sin(θ). + Returns ẋ. + """ + function pendulum_dynamics(x::AbstractVector) + ℓ = 1.0 + g = 9.81 + θ, v = x[1], x[2] + return @. [v, -(g/ℓ)*sin(θ)] + end +end + +# ╔═╡ 950c61b8-f076-4b9a-8970-e5c2841d75f2 +md""" +## Residual and Jacobian + +For the implicit step, define +$r(x) = x_n + h\,f(x) - x,$ +$\partial r(x) = \dfrac{\partial}{\partial x}\big(x_n + h\,f(x) - x\big) = h\,J_f(x) - I.$ +""" + + +# ╔═╡ 92841a2e-bc0d-40f8-8344-a5c398a67275 +begin + # Residual for BE step: r(x) = x_n + h f(x) - x + residual(fun, x_n, h, x) = x_n .+ h .* fun(x) .- x + + function jac_residual(fun, x_n, h, x) + if fun === pendulum_dynamics + ℓ = 1.0; g = 9.81; θ = x[1] + Jf = [ 0.0 1.0 + -(g/ℓ)*cos(θ) 0.0 ] + return Matrix(h * Jf - I) # dense, safe for \ + else + Jf = ForwardDiff.jacobian(fun, x) + return Matrix(h * Jf - I) + end + end +end + +# ╔═╡ 8813982c-8c9a-4706-91a8-ebadf9323a4f +md""" +## Root solvers + +**Linear-algebra view.** Each Newton update solves +$(h\,J_f(x_k)-I)\,\Delta x_k = -\,r(x_k).$ +For small $h$, the matrix is close to $-I$ (well-conditioned); as $h$ grows or the dynamics stiffen, conditioning deteriorates — that is when damping and scaling matter. + + + +**Fixed point (Picard)** updates: $x_{k+1} = g(x_k) = x_n + h\,f(x_k)$. +Converges locally if \(g\) is a contraction (roughly, small enough \(h\)). + +**Newton** uses the true Jacobian \(hJ_f - I\); near a root it is typically quadratic (fewer iterations). + +We’ll stop when $\|r(x_k)\|$ is small or a max iteration budget is reached. +""" + +# ╔═╡ 4307a2f3-0378-4282-815b-9ed1fa532e1c +begin + const SAFE = x -> max(x, eps()) # for log-scale plotting later + + """ + be_step_fixed_point(fun, x_n, h; tol=1e-8, maxiter=30) + + One implicit BE step via fixed-point. Returns (x_next, residual_norms). + """ + function be_step_fixed_point(fun, x_n, h; tol=1e-8, maxiter=30) + x = copy(x_n) # natural initial guess + errs = Float64[] + for _ in 1:maxiter + fx = fun(x) + r = x_n .+ h .* fx .- x + push!(errs, norm(r)) + if errs[end] ≤ tol; break; end + x = x_n .+ h .* fx # Picard update using same fx + end + return x, errs + end + + """ + be_step_newton(fun, x_n, h; tol=1e-10, maxiter=20) + + One implicit BE step via Newton on r(x)=0. Returns (x_next, residual_norms). + """ + function be_step_newton(fun, x_n, h; tol=1e-10, maxiter=20) + x = copy(x_n) + errs = Float64[] + for _ in 1:maxiter + r = residual(fun, x_n, h, x) + push!(errs, norm(r)) + if errs[end] ≤ tol; break; end + Dr = jac_residual(fun, x_n, h, x) + Δx = - Dr \ r + x .= x .+ Δx + end + return x, errs + end +end + +# ╔═╡ a45ed97f-f7c1-4ef5-9bc7-654e827f751b +md""" +### Controls + +Initial state $x_n = [\theta,\, v]$: +- $\theta$ $(@bind θ0 Slider(-3.0:0.05:3.0, default=0.10, show_value=true)) +- $v$ $(@bind v0 Slider(-6.0:0.1:6.0, default=0.00, show_value=true)) + +Step size $h$: $(@bind hstep Slider(0.1:0.1:0.2, default=0.1, show_value=true)) +Max iterations: $(@bind iters_max Select([10, 15, 20, 30]; default=20)) + +Compare: Fixed-point $(@bind show_fp CheckBox(default=true)) +/ Newton $(@bind show_nt CheckBox(default=true)) + +$(@bind run Button("Run")) +""" + + + +# ╔═╡ 5a17f83e-751b-4244-9c15-7165645bfe29 +begin + run + + x_n = [θ0, v0] + + x_fp, e_fp = show_fp ? be_step_fixed_point(pendulum_dynamics, x_n, hstep; maxiter=iters_max) : (x_n, Float64[]) + x_nt, e_nt = show_nt ? be_step_newton(pendulum_dynamics, x_n, hstep; maxiter=iters_max) : (x_n, Float64[]) + + efp = isempty(e_fp) ? Float64[] : max.(e_fp, eps()) # safe for log scale + ent = isempty(e_nt) ? Float64[] : max.(e_nt, eps()) + + p = plt.plot(title="Single-step convergence (‖r(x_k)‖)", + xlabel="iteration k", ylabel="‖r(x_k)‖", yscale=:log10) + + if !isempty(efp) + plt.plot!(1:length(efp), efp, lw=2, marker=:circle, label="Fixed-point") + end + if !isempty(ent) + plt.plot!(1:length(ent), ent, lw=2, marker=:utriangle, label="Newton") + end + p +end + + +# ╔═╡ 12c03202-58b2-482e-a65c-b83bc1f6eed1 +md""" +**What to notice** + +- For small enough \(h\), **fixed point** often converges, but slowly (roughly linear rate). +- **Newton** typically reaches the tolerance in a handful of steps (near-quadratic rate). +- Increase \(h\) to see fixed point struggle while Newton remains robust (until the Jacobian becomes ill-conditioned). +""" + +# ╔═╡ 662d58d7-4c9c-4699-a846-cb6070c507d9 +md""" +# Part II - Unconstrained Minimization — Newton, Regularization, and Line Search + +**Learning goals** +- Apply Newton’s method to a nonconvex scalar objective. +- See why plain Newton can **increase** the objective (negative curvature). +- Stabilize Newton with **Hessian regularization**. +- Add **Armijo backtracking** to get robust progress. + +We will use the toy function +$f(x) = x^4 + x^3 - x^2 - x$ +which has both minima and maxima, so it’s perfect to show where Newton can go wrong. +""" + +# ╔═╡ 27760607-b740-47dc-a810-c332baa2bd2d +begin + #### Problem definition (scalar) #### + f(x::Real) = x^4 + x^3 - x^2 - x + ∇f(x::Real) = 4x^3 + 3x^2 - 2x - 1 + ∇²f(x::Real)= 12x^2 + 6x - 2 + + grad_f(x) = ∇f(x) + hess_f(x) = ∇²f(x) + + #### Plot helpers #### + function plot_fx(; xrange=(-1.75, 1.25), npts=1000, title="Objective") + xs = range(xrange[1], xrange[2], length=npts) + plt.plot(xs, f.(xs), lw=2, label="f(x)", xlabel="x", ylabel="f(x)", title=title) + end + + function plot_trace(xhist; xrange=(-1.75, 1.25), npts=1000, title="") + xs = range(xrange[1], xrange[2], length=npts) + p = plt.plot(xs, f.(xs), lw=2, label="f(x)", xlabel="x", ylabel="f(x)", title=title) + plt.scatter!(p, xhist, f.(xhist), marker=:x, ms=7, label="iterates") + return p + end +end + + +# ╔═╡ 7765e032-c520-4f97-b877-0d479f383f28 +md""" +## 1) Inspect the objective + +Below is the graph of \(f(x)\). The multiple critical points (max/min) make it a nice testbed for Newton. +""" + +# ╔═╡ a098a49b-a368-4929-824c-385a06b88696 +plot_fx(title="f(x) = x^4 + x^3 - x^2 - x") + +# ╔═╡ a62f1b6a-87fe-4401-bc13-42166ca0e129 +md""" +## 2) Plain Newton (no safeguards) + +Newton update: + + +$x_{k+1} = x_k - \frac{\nabla f(x_k)}{\nabla^2 f(x_k)}$. + + +If $\nabla^2 f(x_k) < 0$, the step **ascends** along \(f\). + +Use the sliders to choose a start and number of iterations. +""" + +# ╔═╡ b2ddc048-4942-43bc-8faa-1921062d8c9c +begin + # One Newton step (no regularization, no line search) + newton_step_plain(x) = -grad_f(x) / hess_f(x) + + # Driver for plain Newton + function newton_optimize_plain(x0; tol=1e-8, maxiters=10) + x = float(x0) + xhist = Float64[x] + for _ in 1:maxiters + g = grad_f(x) + H = hess_f(x) + if abs(g) <= tol || !isfinite(H) + break + end + Δx = -g / H + if !isfinite(Δx) + break + end + x += Δx + push!(xhist, x) + if abs(grad_f(x)) <= tol + break + end + end + return xhist + end +end + + +# ╔═╡ 6fd7a753-6db7-4c37-9e22-dc63dd3072c8 +begin + @bind x0_plain PlutoUI.Slider(-1.75:0.01:1.1, default=-1.50, show_value=true) +end + + +# ╔═╡ 49e0f5c3-fe14-42e1-9b3a-83c4447148a8 +begin + @bind iters_plain PlutoUI.Slider(1:20, default=8, show_value=true) +end + +# ╔═╡ 096ebc95-f133-4ca3-b942-cf735faaa42b +begin + xhist_plain_1 = newton_optimize_plain(x0_plain; maxiters=iters_plain) + plot_trace(xhist_plain_1; title = "Plain Newton from x₀ = $(round(x0_plain,digits=3)) and iters = $(iters_plain)") +end + + +# ╔═╡ fdf41c76-4405-49e0-abfa-5c5193de99f4 +md""" +## 3) Globalization: Regularization + Armijo backtracking + +Two simple fixes: + +1. **Regularization:** If $\nabla^2 f(x_k)\le 0$, replace it by $\nabla^2 f(x_k) + \beta $ with $\beta>0$ (LM-style), so the step is a **descent** direction. +2. **Armijo line search:** Scale the step by \(\alpha \in (0,1]\) until +$f(x_k+\alpha\Delta x) \le f(x_k) + b\,\alpha\,\nabla f(x_k)\,\Delta x$, +with typical $b=10^{-4}$, $c=0.5$ for backtracking. + +We’ll expose both as toggles. +""" + + +# ╔═╡ d259c1b8-3716-4f80-b462-3b3daebb444d +md""" +### Controls + +**Start x₀:** $(@bind x0_cmp Slider(-1.5:0.01:0.75, default=0.00, show_value=true)) +**Iterations:** $(@bind iters_cmp Slider(1:40, default=10, show_value=true)) + +**Regularization β₀:** $(@bind beta0 Slider(0.0:0.1:5.0, default=1.0, show_value=true)) + +**Armijo b:** $(@bind armijo_b Select([1e-4, 1e-3, 1e-2]; default=1e-4)) +**Armijo c (backtracking factor):** $(@bind armijo_c Slider(0.1:0.05:0.9, default=0.5, show_value=true)) + +**Show:** +- Plain Newton $(@bind show_plain CheckBox(default=true)) +- Regularized $(@bind show_reg CheckBox(default=true)) +- Reg + Line Search $(@bind show_rls CheckBox(default=true)) + +**Layout:** $(@bind orientation Select(["row (1×N)", "column (N×1)"]; default="row (1×N)")) +""" + + +# ╔═╡ e012d7e0-0181-49d0-bb78-995378c4f87a +md""" +## 4) Takeaways + +- **Plain Newton** is fast **if** you’re in a nice region and the Hessian is positive. +- **Regularization** turns the Newton step into a descent direction when curvature is negative or near-singular. +- **Armijo backtracking** fixes overshooting and makes progress predictable without hand-tuning step sizes. +- These building blocks generalize to higher-dimensional problems and constrained KKT systems (next section of the tutorial). + +**Further experiments** +- Change the objective (keep the same drivers). +- Try different Armijo parameters $b, c$. +- Visualize **$|\nabla f(x_k)|$** vs iteration to see convergence. +""" + + +# ╔═╡ 0a3eb748-fab3-4f8b-993e-2246c32fb6aa + + +# ╔═╡ 3989b8e1-ac1f-430d-9d72-e298ba7ae0ca + + +# ╔═╡ e466ffb2-fcc8-4c3b-9059-7fd68a4265f2 + + +# ╔═╡ 09a301c7-c1f4-4890-afe9-fbc1d7c3b905 + + +# ╔═╡ 53020b8a-4948-467a-8629-bef9496d0374 + + +# ╔═╡ 26c887ce-d95b-4e38-9717-e119de0e80ca +md""" +# Part III - Constrained Optimization (KKT) + +#### Setup (equality constraints) +We solve +```math +\min_{x\in\mathbb{R}^n} f(x) \quad \text{s.t.}\ C(x)=0,\qquad C:\mathbb{R}^n\to\mathbb{R}^m. +``` + +**Geometric picture.** At an optimum on the manifold `C(x)=0`, the gradient is orthogonal to the tangent space: + +```math +\nabla f(x^\star)\ \perp\ \mathcal{T}_{x^\star}=\{p:\ J_C(x^\star)p=0\}. +``` + +Equivalently, the gradient is a combination of the constraint normals: + +```math +\nabla f(x^\star)+J_C(x^\star)^{\!T}\lambda^\star=0,\qquad C(x^\star)=0. +``` + +**Lagrangian.** Define $L(x,\lambda)=f(x)+\lambda^{\!T}C(x)$. +""" + +# ╔═╡ a1b7f614-710a-4ce8-815b-2f94754088c4 +md""" +#### From conditions to a solver (Newton/SQP on the KKT system) + +Linearizing feasibility and stationarity gives the **saddle-point** (KKT) system: + +```math +\begin{bmatrix} +H & J_C(x)^{\!T} \\ +J_C(x) & 0 +\end{bmatrix} +\begin{bmatrix}\Delta x\\ \Delta\lambda\end{bmatrix} +=- +\begin{bmatrix} +\nabla f(x)+J_C(x)^{\!T}\lambda\\ +C(x) +\end{bmatrix}, +\qquad +H \approx \nabla^2 f(x) + \sum_{i=1}^m \lambda_i\,\nabla^2 C_i(x). +``` + +Two common choices for `H`: + +* **Full Newton (Hessian of the Lagrangian):** as written above (fast near a solution). +* **Gauss–Newton/SQP:** drop the constraint-curvature term so $H\approx\nabla^2 f(x)$ (often more robust far from the solution). + +> Practical: this system is symmetric indefinite; block elimination (Schur complement) and sparse factorizations are standard. +""" + +# ╔═╡ 6fd82e73-6fef-4f7f-b291-f94cbac0d268 +md""" +#### Inequality constraints (KKT, first-order) + +For $c(x)\ge 0$, + +```math +\begin{aligned} +&\text{Stationarity:} && \nabla f(x)-J_c(x)^{\!T}\lambda=0,\\ +&\text{Primal feasibility:} && c(x)\ge 0,\\ +&\text{Dual feasibility:} && \lambda\ge 0,\\ +&\text{Complementarity:} && \lambda^{\!T}c(x)=0\quad(\lambda_i\,c_i(x)=0\ \forall i). +\end{aligned} +``` + +**Interpretation.** Active constraints ($c_i(x)=0$) may carry nonzero multipliers; inactive ones ($c_i(x)>0$) have $\lambda_i=0$. +""" + +# ╔═╡ 52718e0b-f958-445e-b9b6-9e5baf09e81a + + +# ╔═╡ edce5b27-9af8-4010-9d9f-60681b2f427c + + +# ╔═╡ c139ccc9-0355-4c41-8d82-0c97fef50900 + + +# ╔═╡ 43c5ccb9-d51e-4d73-b7be-f8b9dd599130 +md""" +# Part III Code - Equality Constrained Minimization (KKT) + +We solve + +```math +\min_x\; f(x) \quad \text{s.t.}\; c(x)=0 +``` + +with one equality constraint. The **KKT system** for a Newton/SQP step is + +```math +\begin{bmatrix} H & J^\top \\ J & 0 \end{bmatrix} +\begin{bmatrix} \Delta x \\ \Delta \lambda \end{bmatrix} += - \begin{bmatrix} \nabla f(x)+J^\top \lambda \\ c(x) \end{bmatrix}, +\quad J=\nabla c(x)^\top,\; H\approx\nabla^2\!L(x,\lambda). +``` + +We'll compare: + +* **Gauss–Newton/SQP**: $H=\nabla^2 f$ (drop constraint curvature) ⇒ robust PD top-left. +* **Full**: $H=\nabla^2 f + \lambda \nabla^2 c$. + +We’ll also use a simple merit function $\phi(x)=f(x)+\frac{\rho}{2}\,c(x)^2$ with Armijo backtracking. +""" + +# ╔═╡ e0a7f431-61dd-4df2-b53c-d81c7a302baf +begin + # === Objective & constraint (KKT section) === + Q_kkt = Diagonal([0.5, 1.0]) + xstar_kkt = [1.0, 0.0] + + # Scalar objective (use dot() to ensure a Float64, not 1×1) + f_kkt(x::AbstractVector) = 0.5 * dot(x .- xstar_kkt, Q_kkt * (x .- xstar_kkt)) + ∇f_kkt(x::AbstractVector) = Q_kkt * (x .- xstar_kkt) + ∇²f_kkt(::AbstractVector) = Q_kkt # constant SPD 2×2 + + # One equality constraint: c(x) = x1^2 + 2x1 - x2 + c_kkt(x::AbstractVector) = x[1]^2 + 2x[1] - x[2] + ∇c_kkt(x::AbstractVector) = [2x[1] + 2.0, -1.0] # length-2 + ∇²c_kkt(::AbstractVector) = [2.0 0.0; 0.0 0.0] # constant 2×2 +end + + + +# ╔═╡ 3e3d6e18-f922-4e91-8eaf-bc26b8f91757 +begin + function landscape_plot_kkt(; xlim=(-4,4), ylim=(-4,4), nsamp=120, show_colorbar=false) + xs = range(xlim[1], xlim[2], length=nsamp) + ys = range(ylim[1], ylim[2], length=nsamp) + Z = [ f_kkt([x,y]) for x in xs, y in ys ] + + p = plt.contour(xs, ys, Z; + levels=18, + colorbar=show_colorbar, + xlabel="x₁", ylabel="x₂", + title="Objective contours & constraint (c(x)=0)", + legend=:topleft, + aspect_ratio=:equal, + xlims=(xlim[1], xlim[2]), + ylims=(ylim[1], ylim[2])) + + xc = collect(xs) + yc = @. xc^2 + 2xc + plt.plot!(p, xc, yc; lw=2, label="c(x)=0") + plt.scatter!(p, [xstar_kkt[1]], [xstar_kkt[2]]; marker=:star5, ms=8, label="x⋆") + + plt.xlims!(p, (xlim[1], xlim[2])) + plt.ylims!(p, (ylim[1], ylim[2])) + return p + end + + plot_path_kkt!(p, X) = begin + plt.plot!(p, X[1,:], X[2,:]; marker=:x, ms=6, lw=1.5, label="iterates") + plt.xlims!(p, (-4,4)); plt.ylims!(p, (-4,4)) + p + end +end + + + +# ╔═╡ 31407c3f-8f3d-4dcc-bc49-cedd8ca14013 +begin + # === KKT step + simple merit line search (KKT section) === + + ϕ_kkt(x; ρ=1.0) = f_kkt(x) + 0.5*ρ*c_kkt(x)^2 + ∇ϕ_kkt(x; ρ=1.0) = ∇f_kkt(x) .+ ρ * c_kkt(x) .* ∇c_kkt(x) + + """ + kkt_step_kkt(x, λ; method=:gn, δ=1e-8) + + Solve the 3×3 KKT system: + [ H C'; C 0 ] [Δx; Δλ] = -[ ∇f + C'λ; c ] + with C = ∇c(x) as a 1×2 row, H = ∇²f (Gauss–Newton) or ∇²f + λ∇²c (Full). + A small ridge δ is added to H on the diagonal for numerical robustness. + """ + function kkt_step_kkt(x::AbstractVector, λ::Real; method::Symbol=:gn, δ::Float64=1e-8) + g = ∇f_kkt(x) # 2 + cv = ∇c_kkt(x) # length-2 + C = reshape(cv, 1, :) # 1×2 + H = Matrix(∇²f_kkt(x)) + if method === :full + H .+= λ .* ∇²c_kkt(x) + end + # add small ridge on diagonal (avoid UniformScaling arithmetic) + @inbounds for i in 1:size(H,1); H[i,i] += δ; end + + K = [H C'; C 0.0] # 3×3 + rhs = -vcat(g .+ λ .* cv, c_kkt(x)) + Δ = try + K \ rhs + catch + pinv(K) * rhs + end + Δx = Δ[1:2]; Δλ = Δ[3] + return Δx, Δλ + end + + """ + kkt_solve_kkt(x0, λ0; iters=8, method=:gn, linesearch=true, ρ=1.0, b=1e-4, cdec=0.5) + + A few KKT iterations with optional Armijo backtracking on ϕ_kkt. + Returns (X, Λ) with X ∈ ℝ^{2×(iters+1)}. + """ + function kkt_solve_kkt(x0, λ0; iters=8, method=:gn, linesearch=true, ρ=1.0, b=1e-4, cdec=0.5) + x = copy(x0); λ = float(λ0) + X = reshape(x, 2, 1); Λ = [λ] + for _ in 1:iters + Δx, Δλ = kkt_step_kkt(x, λ; method=method) + α = 1.0 + if linesearch + φx = ϕ_kkt(x; ρ=ρ) + gφ = ∇ϕ_kkt(x; ρ=ρ) + slope = dot(gφ, Δx) + for _ in 1:20 + if ϕ_kkt(x .+ α .* Δx; ρ=ρ) <= φx + b*α*slope + break + end + α *= cdec + end + end + x .+= α .* Δx + λ += α * Δλ + X = hcat(X, x); push!(Λ, λ) + end + return X, Λ + end +end + + +# ╔═╡ a61a63b7-716e-4500-9bc6-ab2caf9062e7 +md""" +### Controls (KKT) + +**x₁** $(@bind x1_kkt Slider(-3.5:0.1:1.5, default=-1.5, show_value=true)) +**x₂** $(@bind x2_kkt Slider(-3.0:0.1:3.0, default=-1.0, show_value=true)) + +**λ₀** $(@bind λ0_kkt Slider(-2.0:0.1:2.0, default=0.0, show_value=true)) +**Iterations** $(@bind iters_kkt Select([3, 5, 8, 10, 15, 20]; default=8)) + +**Method** $(@bind method_kkt Select([:gn, :full]; default=:gn)) +**Line search** $(@bind use_ls_kkt CheckBox(default=true)) + +**ρ (merit weight)** $(@bind rho_kkt Slider(0.1:0.1:5.0, default=1.0, show_value=true)) + +$(@bind run_kkt_btn Button("Run KKT")) +""" + + +# ╔═╡ 1fda2985-ab68-4460-98be-8621e5a5f1c8 +let + # === Run & plot (KKT section) === + run_kkt_btn # recompute only when the button is clicked + + x0 = [x1_kkt, x2_kkt] + X, Λ = kkt_solve_kkt(x0, λ0_kkt; iters=iters_kkt, method=method_kkt, + linesearch=use_ls_kkt, ρ=rho_kkt) + + fig = landscape_plot_kkt() + plot_path_kkt!(fig, X) + + feas = abs(c_kkt(X[:,end])) + stat = norm( ∇f_kkt(X[:,end]) .+ (Λ[end] .* ∇c_kkt(X[:,end])) ) + + plt.annotate!(fig, -1.8, 15.3, plt.text("feas = $(round(feas,digits=4))", 9)) + plt.annotate!(fig, -1.8, 12.3, plt.text("stat = $(round(stat,digits=4))", 9)) + + fig +end + +# ╔═╡ ea0f0b1e-65b1-4c66-b9fa-7f2c428e3459 + + +# ╔═╡ b4f6cce9-c39e-4325-a0e3-ab9c38179894 +md""" +# Part IV - Log-domain Interior-Point Method (IPM): Tiny QP + +We solve a 2D quadratic program with one inequality: + +- Objective: minimize $f(x) = \tfrac{1}{2}(x - [1,0])^\top Q (x - [1,0])$ with $Q=\mathrm{diag}(0.5, 1)$. +- Constraint: $c(x) = (-1, 1)\cdot x - 1 \ge 0$ (i.e., $x_2 \ge x_1 + 1$). + +**Log-domain substitution** for inequality handling: +$s = \sqrt{\rho}\,e^{\sigma}$, $\lambda = \sqrt{\rho}\,e^{-\sigma}$ so that $s\lambda = \rho$. + +We solve the relaxed KKT system $r(z;\rho)=0$ with unknowns $z=[x;\sigma]$ using damped Newton plus Armijo backtracking on the merit function $\phi(z)=\tfrac{1}{2}\lVert r(z)\rVert^2$. +""" + +# ╔═╡ 252554be-a839-4770-b222-fbb6a32df2ef +begin + # Quadratic objective + Q = Diagonal([0.5, 1.0]) + f(x) = 0.5 * dot(x .- [1.0, 0.0], Q * (x .- [1.0, 0.0])) + ∇f(x) = Q * (x .- [1.0, 0.0]) + + # Single inequality c(x) ≥ 0 with gradient A = (-1, 1) + A = [-1.0, 1.0] # length-2 vector + b = 1.0 + c(x) = dot(A, x) - b # scalar + J(x) = A' # 1×2 (shown for reference) +end + +# ╔═╡ 62d85874-9673-4893-bbff-fa75b4462e96 +begin + # Confidence check: our analytic ∇f, ∇²f vs automatic differentiation + fd_grad(x) = ForwardDiff.derivative(f, x) + fd_hess(x) = ForwardDiff.derivative(fd_grad, x) + + xsamp = [-1.0, -0.25, 0.0, 0.5, 1.0] + grad_err = maximum(abs.(fd_grad.(xsamp) .- ∇f.(xsamp))) + hess_err = maximum(abs.(fd_hess.(xsamp) .- ∇²f.(xsamp))) + + (grad_err < 1e-10 && hess_err < 1e-10) ? + tip(md"AD check passed: analytic derivatives match ForwardDiff on sample points.") : + danger(md"AD check mismatch. Largest errors — grad: $(grad_err), hess: $(hess_err)") +end + +# ╔═╡ 22bfe0a3-c61b-4dfe-8f20-a8bf807c2e14 +begin + # Regularized Newton direction (scalar LM-style) + function newton_direction_reg(x; β0::Float64=1.0, max_tries::Int=20) + g = grad_f(x) + H = hess_f(x) + if H > 0 + return -g / H, H + end + β = β0 + Hreg = H + tries = 0 + while Hreg <= 0 && tries < max_tries + Hreg = H + β + β *= 2 + tries += 1 + end + return -g / Hreg, Hreg + end + + # Armijo backtracking (scalar) + function armijo(f, x, Δx, g; b::Float64=1e-4, c::Float64=0.5, α0::Float64=1.0, max_backtracks::Int=50) + α = α0 + fx = f(x) + rhs = b * g * Δx + for _ in 1:max_backtracks + if f(x + α*Δx) <= fx + α*rhs + return α + end + α *= c + end + return α + end + + # Unified driver with full controls + function newton_optimize(x0; tol=1e-8, maxiters=20, regularize::Bool=false, linesearch::Bool=false, + β0::Float64=1.0, b::Float64=1e-4, c::Float64=0.5, α0::Float64=1.0) + x = float(x0) + xhist = Float64[x] + for _ in 1:maxiters + g = grad_f(x) + + # Direction + Δx, Hused = if regularize + newton_direction_reg(x; β0=β0) + else + (-g / hess_f(x), hess_f(x)) + end + + if !isfinite(Δx) || abs(g) <= tol + break + end + + # Step length + α = linesearch ? armijo(f, x, Δx, g; b=b, c=c, α0=α0) : 1.0 + + x = x + α*Δx + push!(xhist, x) + if abs(grad_f(x)) <= tol + break + end + end + return xhist + end +end + +# ╔═╡ 858d4ee1-4f15-4ced-b984-c0291237d359 +begin + # Build plots for whichever methods are selected + plots = Plots.Plot[] # vector of subplots + + if show_plain + xhist_plain = newton_optimize(x0_cmp; maxiters=iters_cmp, regularize=false, linesearch=false) + push!(plots, plot_trace(xhist_plain; title="Plain")) + end + if show_reg + xhist_reg = newton_optimize(x0_cmp; maxiters=iters_cmp, regularize=true, linesearch=false, β0=beta0) + push!(plots, plot_trace(xhist_reg; title="Regularized (β₀=$(round(beta0,digits=2)))")) + end + if show_rls + xhist_rls = newton_optimize(x0_cmp; maxiters=iters_cmp, regularize=true, linesearch=true, + β0=beta0, b=armijo_b, c=armijo_c) + push!(plots, plot_trace(xhist_rls; title="Reg + LineSearch (b=$(armijo_b), c=$(round(armijo_c,digits=2)))")) + end + + if isempty(plots) + md"Select at least one method to visualize." + else + n = length(plots) + layout_val = orientation == "row (1×N)" ? (1,n) : (n,1) + plt.plot(plots..., layout=layout_val, size=(1200, orientation=="row (1×N)" ? 360 : 900)) + end +end + + +# ╔═╡ 82e2eca9-4991-4538-8e1f-455cd46f849f +begin + # Generic version + function landscape_plot(; xlim=(-4,4), ylim=(-4,4), nsamp=120, show_colorbar=false) + xs = range(xlim[1], xlim[2], length=nsamp) + ys = range(ylim[1], ylim[2], length=nsamp) + Z = [ f([x,y]) for x in xs, y in ys ] # size(Z) = (length(xs), length(ys)) + + p = plt.contour(xs, ys, Z; + levels=18, + colorbar=show_colorbar, + xlabel="x₁", ylabel="x₂", + title="Objective contours & constraint", + legend=:topleft, + aspect_ratio=:equal, + xlims=(xlim[1], xlim[2]), # hard-lock axes + ylims=(ylim[1], ylim[2])) + + xc = collect(xs) + yc = @. xc^2 + 2xc + plt.plot!(p, xc, yc; lw=2, label="c(x)=0") + plt.scatter!(p, [xstar[1]], [xstar[2]]; marker=:star5, ms=8, label="x⋆") + + # re-enforce limits after adding series (paranoid, but safe) + plt.xlims!(p, (xlim[1], xlim[2])) + plt.ylims!(p, (ylim[1], ylim[2])) + return p + end + + plot_path!(p, X) = begin + plt.plot!(p, X[1,:], X[2,:]; marker=:x, ms=6, lw=1.5, label="iterates") + plt.xlims!(p, (-4,4)); plt.ylims!(p, (-4,4)) # keep locked if path goes outside + p + end +end + + +# ╔═╡ db230e89-e8ea-4c57-8517-0dbf68cab6b7 +md""" +## Visualizing the objective and the constraint + +We plot contour lines of $f(x)$ and the line $c(x)=0$ (the feasible region is on or above that line, i.e., $c(x)\ge 0$). +""" + +# ╔═╡ d9c51abd-2488-405f-9b4c-799f496c0dd1 +begin + function plot_landscape(; xlim=(-4.0, 4.0), ylim=(-4.0, 4.0)) + xs = range(xlim[1], xlim[2], length=200) + ys = range(ylim[1], ylim[2], length=200) + Z = [f([xi, yi]) for yi in ys, xi in xs] + + p = plt.contour( + xs, ys, Z; + fill=false, + xlabel="x₁", ylabel="x₂", + title="Objective & constraint", + aspect_ratio=1, # square figure; keep if you like + xlims=xlim, ylims=ylim # << fix the visible ranges + ) + + plt.plot!(p, xs, xs .+ 1; label="c(x)=0 (x₂ = x₁ + 1)") + return p + end + + fig_test = plot_landscape() # will be limited to [-4,4] × [-4,4] +end + + +# ╔═╡ af7cf190-fa37-40f7-ac9e-dbdb8e72fff8 +md""" +## Log-domain IP residuals + +With $z=[x;\sigma]$ and parameter $\rho>0$, define +$\lambda(z,\rho)=\sqrt{\rho}\,e^{-\sigma}$ and $s(z,\rho)=\sqrt{\rho}\,e^{\sigma}$. + +Residuals: +- Stationarity (2 numbers): $\nabla f(x) - \lambda A = 0$. +- Primal feasibility (1 number): $c(x) - s = 0$. + +So $r(z;\rho) = \begin{bmatrix} \nabla f(x) - \lambda A \\ c(x) - s \end{bmatrix} \in \mathbb{R}^3$. +""" + +# ╔═╡ 6c5b33c5-94fc-4b41-8128-696a58ca9b64 +begin + function ip_residual(z::AbstractVector, ρ::Real) + x = z[1:2] + σ = z[3] + λ = sqrt(ρ) * exp(-σ) # ≥ 0 + s = sqrt(ρ) * exp(σ) # ≥ 0 + r1 = ∇f(x) .- (λ .* A) # length-2 + r2 = c(x) - s # scalar + return vcat(r1, r2) # length-3 + end +end + +# ╔═╡ 64d20ba5-d195-41ef-88b8-d2968bf87ac2 +md""" +## Newton solve for fixed $\rho$ + +We solve $r(z; \rho)=0$ with damped Newton on the merit $\phi(z)=\tfrac{1}{2}\lVert r(z)\rVert^2$. + +Directional derivative of $\phi$ along $\Delta z$ is $r(z)^\top J(z)\,\Delta z$ where $J$ is the Jacobian of $r$. +""" + +# ╔═╡ 4f9c5ddd-05f5-4826-a775-bc1af4555153 +begin + function newton_solve(z0::AbstractVector, ρ; tol=1e-10, maxiter=50, α_min=1e-12) + z = copy(z0) + R = ip_residual(z, ρ) + Zhist = reshape(z, :, 1) + + for k in 1:maxiter + if norm(R) ≤ tol + return z, Zhist + end + Jz = ForwardDiff.jacobian(zz -> ip_residual(zz, ρ), z) # 3×3 + Δz = -Jz \ R + + ϕz = 0.5 * dot(R, R) + gΔ = dot(R, Jz * Δz) # directional derivative of ϕ at z along Δz + α = 1.0 + z_new = z .+ α .* Δz + R_new = ip_residual(z_new, ρ) + ϕnew = 0.5 * dot(R_new, R_new) + c_armijo, shrink = 1e-4, 0.5 + + if gΔ > 0 + # fallback: just ensure decrease in ϕ + while ϕnew > ϕz && α > α_min + α *= shrink + z_new = z .+ α .* Δz + R_new = ip_residual(z_new, ρ) + ϕnew = 0.5 * dot(R_new, R_new) + end + else + # standard Armijo + while ϕnew > ϕz + c_armijo*α*gΔ && α > α_min + α *= shrink + z_new = z .+ α .* Δz + R_new = ip_residual(z_new, ρ) + ϕnew = 0.5 * dot(R_new, R_new) + end + end + + z, R = z_new, R_new + Zhist = hcat(Zhist, z) + end + return z, Zhist + end +end + +# ╔═╡ 1fb54858-898e-4d0d-8435-746bf3c17e65 +md""" +#### KKT sanity checks to report: +* stationarity norm $\lVert\nabla f(x) - \lambda A\rVert$ +* primal feasibility $c(x)$ +* dual feasibility $\lambda$ +* and complementarity slackness $\lambda\,c(x)$ + +These should approach zero feasibility violation and complementarity as $\rho \to 0$. +""" + + +# ╔═╡ 2070905c-d96b-4e3d-8a51-f0bf26ba98b8 +begin + function kkt_checks(z, ρ) + x, σ = z[1:2], z[3] + λ = sqrt(ρ) * exp(-σ) + (; stationarity_norm = norm(∇f(x) .- (λ .* A)), + primal_feas = c(x), # should be ≥ 0 + dual_feas = λ, # should be ≥ 0 + complementarity = λ * c(x)) + end +end + + +# ╔═╡ 08270f19-7c1c-4707-baf8-fa985519baf3 +md""" +## Demo: follow the central path by decreasing $\rho$ + +We warm-start Newton at a smaller $\rho$ and plot the iterates on the $(x_1,x_2)$ plane. +""" + + +# ╔═╡ a4c50b10-0a6f-4967-9895-e5171f44fafa +begin + # Initial guess (must be feasible or strictly interior for the barrier) + x0_init = [-2.0, 2.0] + σ0_init = 0.0 + z0_init = vcat(x0_init, σ0_init) # [x; σ] ∈ ℝ^3 + + # Central-path parameters + ρ_path1 = 1.0 + ρ_path2 = 1e-8 +end + +# ╔═╡ 71530ac2-841c-4616-98e3-2dedd63e3834 +begin + # cold-start both runs from the same z0_init + sol_rho1_cold, traj_rho1_cold = newton_solve(z0_init, ρ_path1; tol=1e-10) + sol_rho2_cold, traj_rho2_cold = newton_solve(z0_init, ρ_path2; tol=1e-10) + + fig_ipm_compare = plot_landscape(xlim=(-4,4), ylim=(-4,4)) + plt.plot!(fig_ipm_compare; legend=:bottomright, grid=true) + + # starting point + plt.scatter!(fig_ipm_compare, [z0_init[1]], [z0_init[2]]; + label="start", markershape=:star5, markersize=9, markerstrokewidth=0, color=:black) + + # path & final for ρ = 1.0 + plt.plot!(fig_ipm_compare, vec(traj_rho1_cold[1, :]), vec(traj_rho1_cold[2, :]); + label="iterates ρ=1.0", lw=2, marker=:circle, markersize=3, color=:blue) + + # path & final for ρ = 1e-8 + plt.plot!(fig_ipm_compare, vec(traj_rho2_cold[1, :]), vec(traj_rho2_cold[2, :]); + label="iterates ρ=1e-8", lw=2, marker=:utriangle, markersize=3, color=:red) + + + fig_ipm_compare +end + + + +# ╔═╡ fe6ec86b-84c1-4e82-85e0-dce1a316214a +md""" +### Optional: Jacobian of the residual at the final point + +The Jacobian $J(z)$ of $r(z;\\rho)$ at the final iterate can be inspected via eigenvalues to gauge local conditioning. +""" + +# ╔═╡ 686b3f99-11df-4cc0-b739-3ff8476d217d + + +# ╔═╡ 8aa33896-2585-4a60-86dc-7386b96feb9f + + +# ╔═╡ 3132cda5-ad6e-4ae2-afba-ea18d09cf8df +md""" +# Part V - Sequential Quadratic Programming (SQP) + +**Idea.** Solve a nonlinear constrained problem by repeatedly solving a **quadratic program (QP)** built from local models. + +- Quadratic model of the Lagrangian/objective near the current iterate. +- Linearize the constraints around the current iterate. +- Each iteration: solve a QP to get a step $d$, then update $x \leftarrow x + \alpha d$. +- Strengths: Newton-like local convergence (often superlinear), warm-start friendly. +""" + + + +# ╔═╡ 74d5c04f-3f63-41ba-818d-ce217cd18022 +md""" +## Problem & KKT recap + +We consider +$\min_{x \in \mathbb{R}^n} \ f(x) \quad \text{s.t.}\quad g(x)=0,\ \ h(x)\le 0$. + +At a candidate optimum $x^\star$, the KKT conditions require multipliers +$\lambda \in \mathbb{R}^m$, $\mu \in \mathbb{R}^p_{\ge 0}$ such that +$\nabla f(x^\star) + \nabla g(x^\star)^\top \lambda + \nabla h(x^\star)^\top \mu = 0$, +$g(x^\star)=0,\ \ h(x^\star)\le 0,\ \ \mu \ge 0,\ \ \mu \odot h(x^\star)=0$. +""" + + +# ╔═╡ 4b560ea8-8104-40ce-bf44-b5c2267132eb +md""" +## Local models that define the QP + +At iterate $x_k$ with multipliers $(\lambda_k,\mu_k)$: + +**Quadratic model** +$m_k(d) \;=\; \nabla f(x_k)^\top d \;+\; \tfrac{1}{2}\, d^\top B_k\, d,$ +with $B_k \approx \nabla^2_{xx}\mathcal{L}(x_k,\lambda_k,\mu_k)$. Common choices: exact Hessian, (L-)BFGS, or Gauss–Newton. + +**Linearized constraints** +$ g(x_k) + \nabla g(x_k)\, d = 0, \qquad h(x_k) + \nabla h(x_k)\, d \le 0.$ +""" + +# ╔═╡ 96738f09-e894-499e-9e30-15692e2434dd +md""" +## The SQP subproblem (QP) + +$ +\begin{aligned} +\min_{d \in \mathbb{R}^n}\quad & +\nabla f(x_k)^\top d \;+\; \tfrac{1}{2}\, d^\top B_k\, d \\ +\text{s.t.}\quad & +\nabla g(x_k)\, d + g(x_k) = 0, \\ +& +\nabla h(x_k)\, d + h(x_k) \le 0. +\end{aligned} +$ + +Solving this QP yields a step $d_k$ and QP multipliers $(\lambda_{k+1},\mu_{k+1})$. +Update with $x_{k+1} = x_k + \alpha_k d_k$ (line search or trust region). +""" + + + +# ╔═╡ 704f8f36-fda1-445e-9551-942fd85bff42 +md""" +## SQP (line-search flavor): 6 steps + +1. Initialize $x_0$, multipliers $(\lambda_0,\mu_0)$, and $B_0 \succ 0$. +2. Build the QP at $x_k$ using $B_k$, $\nabla g(x_k)$, $\nabla h(x_k)$, $g(x_k)$, $h(x_k)$. +3. Solve the QP $\Rightarrow$ get $d_k$ and $(\lambda_{k+1},\mu_{k+1})$. +4. Choose $\alpha_k \in (0,1]$ via globalization (merit or filter). +5. Set $x_{k+1} = x_k + \alpha_k d_k$. +6. Update $B_{k+1}$ (e.g., damped BFGS). Stop when KKT residuals are small. +""" + + + +# ╔═╡ b6acef39-d40d-42e6-b037-10713dc1254e +md""" +### Globalization: make SQP robust + +**Merit (penalty) function** for line search, e.g. +$\phi(x) \;=\; f(x) \;+\; \tfrac{\rho}{2}\,\|g(x)\|^2 \;+\; \rho\,\|h(x)_+\|_1,$ +and choose $\alpha_k$ by Armijo/backtracking so $\phi$ decreases. + +**Filter methods** accept steps that improve *either* objective *or* feasibility. + +**Trust-region SQP**: restrict $\|d\|\le \Delta_k$, compare predicted vs actual reduction, adjust $\Delta_k$. + +### Inequalities & active sets (intuition) + +- The QP contains the **linearized inequalities** $h(x_k)+\nabla h(x_k)d \le 0$. +- Its KKT system enforces complementarity via multiplier signs and active constraints. +- A small **working set** (estimated active constraints) tends to stabilize across iterations, enabling warm starts and fast solves. + +""" + + + +# ╔═╡ 34737fc8-095c-4061-8661-e776c23c7eed + + +# ╔═╡ fc35e75c-b81d-44eb-bcee-74c590e38652 + + +# ╔═╡ 32587054-655f-4f06-9ff8-500ba44bec76 + + +# ╔═╡ 3dfc4b4e-85e3-4dee-ae33-5ddb2a4eec29 +question_box(md"hello") + +# ╔═╡ e8f62342-88ab-4754-af33-e2347be2daa0 +Foldable(md"Compressed vs. separated form", md" +We can either express the values of $x$ and $u$ at $t_{k + \frac{1}{2}}$ with the expressions above, or we can set them as decision variables, and enforce the expressions above as equality constraints. +Doing the former results in *compressed form* and the latter *separated form*. +The compressed form tends to perform better with a large number of segments and the separated form a small number of segments [^kelly2017]. +") + +# ╔═╡ Cell order: +# ╟─81ebc291-89f0-4c1e-ac34-d5715977dd86 +# ╟─9543f7bc-ab36-46ff-b471-9aa3db9739e4 +# ╠═8969e78a-29b0-46d3-b6ba-59980208fe5b +# ╟─d90e9be0-7b68-4139-b185-6cbaad0d307e +# ╠═7b896268-4336-47e2-a8b5-f985bfde51f5 +# ╟─342decc1-43fa-432a-9a9c-757a10ba6a5d +# ╟─fd1ad74b-cb74-49a7-80b8-1a282abfdff2 +# ╠═49d5b2e6-eb29-478c-b817-8405d55170b1 +# ╠═950c61b8-f076-4b9a-8970-e5c2841d75f2 +# ╠═92841a2e-bc0d-40f8-8344-a5c398a67275 +# ╠═8813982c-8c9a-4706-91a8-ebadf9323a4f +# ╠═4307a2f3-0378-4282-815b-9ed1fa532e1c +# ╠═a45ed97f-f7c1-4ef5-9bc7-654e827f751b +# ╠═5a17f83e-751b-4244-9c15-7165645bfe29 +# ╟─12c03202-58b2-482e-a65c-b83bc1f6eed1 +# ╠═662d58d7-4c9c-4699-a846-cb6070c507d9 +# ╠═27760607-b740-47dc-a810-c332baa2bd2d +# ╟─7765e032-c520-4f97-b877-0d479f383f28 +# ╠═a098a49b-a368-4929-824c-385a06b88696 +# ╠═62d85874-9673-4893-bbff-fa75b4462e96 +# ╠═a62f1b6a-87fe-4401-bc13-42166ca0e129 +# ╠═b2ddc048-4942-43bc-8faa-1921062d8c9c +# ╠═6fd7a753-6db7-4c37-9e22-dc63dd3072c8 +# ╠═49e0f5c3-fe14-42e1-9b3a-83c4447148a8 +# ╠═096ebc95-f133-4ca3-b942-cf735faaa42b +# ╠═fdf41c76-4405-49e0-abfa-5c5193de99f4 +# ╠═22bfe0a3-c61b-4dfe-8f20-a8bf807c2e14 +# ╠═d259c1b8-3716-4f80-b462-3b3daebb444d +# ╠═858d4ee1-4f15-4ced-b984-c0291237d359 +# ╠═e012d7e0-0181-49d0-bb78-995378c4f87a +# ╠═0a3eb748-fab3-4f8b-993e-2246c32fb6aa +# ╠═3989b8e1-ac1f-430d-9d72-e298ba7ae0ca +# ╠═e466ffb2-fcc8-4c3b-9059-7fd68a4265f2 +# ╠═09a301c7-c1f4-4890-afe9-fbc1d7c3b905 +# ╠═53020b8a-4948-467a-8629-bef9496d0374 +# ╠═26c887ce-d95b-4e38-9717-e119de0e80ca +# ╟─a1b7f614-710a-4ce8-815b-2f94754088c4 +# ╟─6fd82e73-6fef-4f7f-b291-f94cbac0d268 +# ╠═52718e0b-f958-445e-b9b6-9e5baf09e81a +# ╠═edce5b27-9af8-4010-9d9f-60681b2f427c +# ╠═c139ccc9-0355-4c41-8d82-0c97fef50900 +# ╟─43c5ccb9-d51e-4d73-b7be-f8b9dd599130 +# ╠═e0a7f431-61dd-4df2-b53c-d81c7a302baf +# ╠═82e2eca9-4991-4538-8e1f-455cd46f849f +# ╠═3e3d6e18-f922-4e91-8eaf-bc26b8f91757 +# ╠═31407c3f-8f3d-4dcc-bc49-cedd8ca14013 +# ╠═a61a63b7-716e-4500-9bc6-ab2caf9062e7 +# ╠═1fda2985-ab68-4460-98be-8621e5a5f1c8 +# ╠═ea0f0b1e-65b1-4c66-b9fa-7f2c428e3459 +# ╠═b4f6cce9-c39e-4325-a0e3-ab9c38179894 +# ╠═252554be-a839-4770-b222-fbb6a32df2ef +# ╠═db230e89-e8ea-4c57-8517-0dbf68cab6b7 +# ╠═d9c51abd-2488-405f-9b4c-799f496c0dd1 +# ╠═af7cf190-fa37-40f7-ac9e-dbdb8e72fff8 +# ╠═6c5b33c5-94fc-4b41-8128-696a58ca9b64 +# ╠═64d20ba5-d195-41ef-88b8-d2968bf87ac2 +# ╠═4f9c5ddd-05f5-4826-a775-bc1af4555153 +# ╠═1fb54858-898e-4d0d-8435-746bf3c17e65 +# ╠═2070905c-d96b-4e3d-8a51-f0bf26ba98b8 +# ╠═08270f19-7c1c-4707-baf8-fa985519baf3 +# ╠═a4c50b10-0a6f-4967-9895-e5171f44fafa +# ╠═71530ac2-841c-4616-98e3-2dedd63e3834 +# ╠═fe6ec86b-84c1-4e82-85e0-dce1a316214a +# ╠═686b3f99-11df-4cc0-b739-3ff8476d217d +# ╠═8aa33896-2585-4a60-86dc-7386b96feb9f +# ╠═3132cda5-ad6e-4ae2-afba-ea18d09cf8df +# ╟─74d5c04f-3f63-41ba-818d-ce217cd18022 +# ╟─4b560ea8-8104-40ce-bf44-b5c2267132eb +# ╟─96738f09-e894-499e-9e30-15692e2434dd +# ╟─704f8f36-fda1-445e-9551-942fd85bff42 +# ╟─b6acef39-d40d-42e6-b037-10713dc1254e +# ╠═34737fc8-095c-4061-8661-e776c23c7eed +# ╠═fc35e75c-b81d-44eb-bcee-74c590e38652 +# ╠═32587054-655f-4f06-9ff8-500ba44bec76 +# ╠═3dfc4b4e-85e3-4dee-ae33-5ddb2a4eec29 +# ╠═e8f62342-88ab-4754-af33-e2347be2daa0 From 6fcb1dc2a1a5ac3921ed8d951f15492edd20a911 Mon Sep 17 00:00:00 2001 From: arnauddeza Date: Sat, 15 Nov 2025 19:38:12 -0500 Subject: [PATCH 2/3] Nov 15 final checkpoint: update class02/class02.jl --- class02/class02.jl | 1338 ++++++++++++++++++++++---------------------- 1 file changed, 684 insertions(+), 654 deletions(-) diff --git a/class02/class02.jl b/class02/class02.jl index 63200b9..2c4e1b1 100644 --- a/class02/class02.jl +++ b/class02/class02.jl @@ -74,7 +74,7 @@ md""" # ╔═╡ 342decc1-43fa-432a-9a9c-757a10ba6a5d md""" -# Part 0: Overview of Lecture 2 +# Overview of Chapter 2 Lecture 2 is the course’s optimization backbone: it makes explicit the idea that most control problems are optimization problems in disguise. We set the common language (gradients/Hessians, KKT systems, globalization) and the “solver toolbox” (penalty, augmented Lagrangian, primal–dual interior-point, SQP) that shows up everywhere else: @@ -84,18 +84,16 @@ We set the common language (gradients/Hessians, KKT systems, globalization) and and modern differentiable controllers backprop through these solvers. -The point isn’t to prove every theorem—it’s to give you a reliable recipe for turning a control task into a well-posed QP/NLP and picking/configuring a solver that actually converges. +The point of this chapter is to give you a reliable recipe for turning a control task into a well-posed QP/NLP and picking/configuring a solver that actually converges. Positionally, this lecture is the hinge between “dynamics & modeling” (Class 1) and everything that follows: PMP/LQR reframed via KKT and Riccati; nonlinear trajectory optimization and collocation (Class 5); distributed MPC/ADMM (Class 8); GPU-accelerated solves (Class 9); and the learning side—adjoints for Neural DEs (Class 10) and optimization-as-a-layer for PINNs/neural operators (Classes 11–13). -Concretely, you leave with (i) a map from control formulations to QP/NLP templates, (ii) rules of thumb for choosing between ALM, IPM, and SQP, and (iii) practical globalization (regularization + line search) so demos and projects are reproducible and fast. - -### General structure of lecture +##### General structure of chapter (please refer to lecture slides as needed!) - **Root finding:** how implicit time-stepping (Backward Euler) turns into solving $r(x)=0$ each step. -- **Unconstrained minimization:** Newton’s method and why we need *globalization* (regularization + line search). +- **Unconstrained minimization:** Newton’s method and why we need *globalization strategies* (regularization + line search). - **Constrained minimization:** KKT conditions in action for equality constraints. -- **Interior Point Method:** IPM, augmented lagragnian method. +- **Interior Point Method:** IPM (check lecture slides for augmented lagrangian method and other penalty methods). - **SQP (Sequential Quadratic Programming):** how to reduce a nonlinear program to a sequence of easy QPs. """ @@ -103,50 +101,50 @@ Concretely, you leave with (i) a map from control formulations to QP/NLP templat md""" # Part I — Unconstrained Minimization as Root Finding -In control, *implicit* simulators and optimizers show up everywhere: +Many steps in control are solutions of **nonlinear equations** $r(x)=0$: +- **Implicit simulation (Backward Euler)** each step solves $r(x)=0$. +- **Newton steps** inside optimizers solve a linearized $r=0$. +- **Adjoints** are solutions of linear(ized) equations. -- **Implicit time stepping** (e.g., Backward Euler) for stability on stiff dynamics. -- **KKT systems** and **Newton steps** inside constrained optimization. -- **Adjoints** in learning-to-control are solutions of linearized/adjoint equations. +We’ll keep one reusable pattern: define a residual $r(x)$, compute its Jacobian ($\partial r(x)$), and choose an iteration that drives $\|r(x_k)\|$ down quickly and reliably. -All three reduce to solving nonlinear equations of the form $r(x)=0$. This section builds the minimal, reusable toolkit to do that reliably. +**Going from implicit step to residual** -**Example we use:** one implicit Backward Euler step for $\dot x = f(x)$: -$x_{n+1} = x_n + h\,f(x_{n+1}) \;\Rightarrow\; r(x) \equiv x_n + h\,f(x) - x = 0.$ +For $\dot x=f(x)$, one Backward Euler step with step size $h$ satisfies -We compare two solvers for $r(x)=0$: a **fixed-point** iteration and **Newton’s method**, and relate what you see to the theory (contraction vs. quadratic convergence). +$x_{n+1} = x_n + h\,f(x_{n+1})$ -## From differential equations to algebraic equations +Move everything to the left to get the **residual** -Why not just use explicit Euler? For many control systems (pendulum near upright, contact, power systems) the dynamics can be **stiff**. Stability and larger steps favor **implicit** schemes, but those require solving $r(x)=0$ each step. +$r(x) \;\equiv\; x_n + h\,f(x) - x, +\qquad +\partial r(x) \;=\; h\,J_f(x) - I$ -For Backward Euler (BE), -$r(x) = x_n + h\,f(x) - x,$ -$\partial r(x) = h\,J_f(x) - I,$ -where $J_f$ is the Jacobian of $f$. Good solvers exploit this structure: the residual is “identity minus something small” for small $h$, and the Jacobian carries the physics through $J_f$. +Two important facts: +- For small $h$, $\partial r \approx -I$ ⇒ Newton systems are well-conditioned. +- Note $J_f$ carries physics; using it (rather than a crude approximation) is what makes Newton fast. -## Two algorithms we’ll compare +We compare two solvers for $r(x)=0$: a **fixed-point** iteration and **Newton’s method**. -**Fixed-point (Picard)** defines $g(x):=x_n + h\,f(x)$ and iterates -$x_{k+1}=g(x_k).$ -It converges if $g$ is a **contraction** near the root (intuitively, $h\,\|J_f\|$ small). The local linear rate is controlled by $\rho\!\left(J_g(x^\star)\right)=\rho\!\left(h\,J_f(x^\star)\right)$. +**Fixed-point (Picard).** Define $g(x)=x_n+h\,f(x)$ and iterate -**Newton’s method** linearizes $r$ and solves for a correction: -$[\partial r(x_k)]\,\Delta x_k = -\,r(x_k), \quad x_{k+1}=x_k+\Delta x_k,$ -so for BE, -$\underbrace{(h\,J_f(x_k)-I)}_{\text{BE Jacobian}}\Delta x_k = -\,(x_n+h\,f(x_k)-x_k).$ +$x_{k+1}=g(x_k)$ -**Stopping & diagnostics.** We track the **residual norm** $\|r(x_k)\|$ and optionally the **step norm** $\|\Delta x_k\|$. Reporting both avoids “false convergence” when steps stagnate but residuals do not. +It converges if $g$ is a contraction near the root (intuitively $h\|J_f\|$ small). Rate: **linear**. -## What the theorems (briefly) say +**Newton’s method.** Linearize $r$ and solve for a correction: -- **Banach fixed-point theorem (Picard):** If $g$ is a contraction on a neighborhood of $x^\star$ (i.e., $\|J_g(x)\|\le q<1$), then $x_{k+1}=g(x_k)$ converges **linearly** to $x^\star$. For BE, $g'(x^\star)=h\,J_f(x^\star)$, so small $h$ or well-scaled $f$ helps. +$(\partial r(x_k))\,\Delta x_k = -\,r(x_k), \qquad x_{k+1}=x_k+\Delta x_k$ + +Near a solution, the rate is **quadratic**. +""" -- **Newton’s method:** If $r\in C^2$, $\partial r(x^\star)$ is nonsingular, and $x_0$ is close enough to $x^\star$, then the iterates are **quadratically** convergent: -$\|x_{k+1}-x^\star\| \le C\,\|x_k-x^\star\|^2.$ -In practice we use **damping / line search** to reach the fast local regime from farther starts. +# ╔═╡ 5e6c4ea7-b283-423c-b9c0-421d53cebc2d +md""" +We now will move onto a small example to use root finding on. -**Takeaway:** Picard is simple but fragile; Newton has higher per-iteration cost but far fewer iterations and better robustness for implicit steps. +**Reminder of the pendulum dynamics:** we use the simple, undamped pendulum with state $x=[\theta,\;v]$ so $\dot{\theta}=v$ and $\dot{v}=-(g/\ell)\sin\theta$, where $g=9.81\,\mathrm{m/s^2}$ and $\ell$ is the length. +In vector form, $f(x)=\begin{bmatrix}v\\ -(g/\ell)\sin\theta\end{bmatrix}$, which is what `pendulum_dynamics` returns. """ # ╔═╡ 49d5b2e6-eb29-478c-b817-8405d55170b1 @@ -167,14 +165,18 @@ end # ╔═╡ 950c61b8-f076-4b9a-8970-e5c2841d75f2 md""" -## Residual and Jacobian - -For the implicit step, define +For the implicit step, define the residual and jacobian as $r(x) = x_n + h\,f(x) - x,$ $\partial r(x) = \dfrac{\partial}{\partial x}\big(x_n + h\,f(x) - x\big) = h\,J_f(x) - I.$ """ +# ╔═╡ 80035b9c-eba6-469c-b138-c6c792979493 +md""" +Backward Euler converts the implicit update into a root-finding task: the residual $r(x)=x_n+h\,f(x)-x$ measures how far a candidate $x$ misses the equation $x=x_n+h\,f(x)$, and the step is the root $r(x)=0$. +Its Jacobian is $\partial r(x)=h\,J_f(x)-I$, which is close to $-I$ when $h$ is small—so Newton steps are well-conditioned while $J_f$ carries the system’s physics. +""" + # ╔═╡ 92841a2e-bc0d-40f8-8344-a5c398a67275 begin # Residual for BE step: r(x) = x_n + h f(x) - x @@ -195,25 +197,19 @@ end # ╔═╡ 8813982c-8c9a-4706-91a8-ebadf9323a4f md""" -## Root solvers +Now we can move onto to actually implementing a root finding solver -**Linear-algebra view.** Each Newton update solves +Each Newton update solves $(h\,J_f(x_k)-I)\,\Delta x_k = -\,r(x_k).$ For small $h$, the matrix is close to $-I$ (well-conditioned); as $h$ grows or the dynamics stiffen, conditioning deteriorates — that is when damping and scaling matter. - - - + **Fixed point (Picard)** updates: $x_{k+1} = g(x_k) = x_n + h\,f(x_k)$. Converges locally if \(g\) is a contraction (roughly, small enough \(h\)). - -**Newton** uses the true Jacobian \(hJ_f - I\); near a root it is typically quadratic (fewer iterations). - -We’ll stop when $\|r(x_k)\|$ is small or a max iteration budget is reached. + """ # ╔═╡ 4307a2f3-0378-4282-815b-9ed1fa532e1c -begin - const SAFE = x -> max(x, eps()) # for log-scale plotting later +begin """ be_step_fixed_point(fun, x_n, h; tol=1e-8, maxiter=30) @@ -255,7 +251,15 @@ end # ╔═╡ a45ed97f-f7c1-4ef5-9bc7-654e827f751b md""" -### Controls +##### How to use the root finding demo + +1. Pick an initial state $x_n=[\theta, v]$ and a step size $h$. +2. Click **Run** to see $\|r(x_k)\|$ vs iteration (log scale). +3. Try increasing $h$: + - Fixed-point slows down or fails (not a contraction). + - Newton stays fast as long as $\partial r=hJ_f-I$ is nonsingular. + +**Good starting point to try:** $\theta\in[0.1,0.3]$, $v=0$, $h=0.1$. Initial state $x_n = [\theta,\, v]$: - $\theta$ $(@bind θ0 Slider(-3.0:0.05:3.0, default=0.10, show_value=true)) @@ -297,78 +301,166 @@ begin end -# ╔═╡ 12c03202-58b2-482e-a65c-b83bc1f6eed1 +# ╔═╡ 56c965c9-5acc-40a5-b1dd-c3a59f0462a9 md""" -**What to notice** +**What you should see:** + +(For $\theta=0.15, v=0.0, h = 0.1$) Newton drives the residual from ~$10^{-3}$ to ~$10^{-11}$ in about 3 iterations, while fixed-point takes $~15$ iterations to reach only ~$10^{-8}$. The steep, almost vertical drop of the orange curve (log scale) is the signature of **near-quadratic** convergence; the blue curve decays roughly linearly and can stagnate. Because Newton uses the true Jacobian $h J_f - I$, this advantage usually grows as $h$ (or stiffness) increases. +""" + +# ╔═╡ af82d16d-c649-461b-856a-42355517d9f4 +md""" +##### From roots to minima (part II of chapter) + +Minimization often reduces to **root finding** on the gradient: + +$\min_x f(x)\quad \Longleftrightarrow \quad \nabla f(x)=0$ -- For small enough \(h\), **fixed point** often converges, but slowly (roughly linear rate). -- **Newton** typically reaches the tolerance in a handful of steps (near-quadratic rate). -- Increase \(h\) to see fixed point struggle while Newton remains robust (until the Jacobian becomes ill-conditioned). +The next section of this chapter takes the same Newton machinery you just used on $r=0$ and applies it to $\nabla f=0$, then adds **globalization** (regularization + line search) to make it behave well away from the optimum. """ + # ╔═╡ 662d58d7-4c9c-4699-a846-cb6070c507d9 md""" # Part II - Unconstrained Minimization — Newton, Regularization, and Line Search -**Learning goals** -- Apply Newton’s method to a nonconvex scalar objective. -- See why plain Newton can **increase** the objective (negative curvature). -- Stabilize Newton with **Hessian regularization**. -- Add **Armijo backtracking** to get robust progress. +In this next part we will apply Newton's method to a nonconvex scalar objective. We will see that that plain Newton (i.e without additional safeguards) can **increase** the objective (negative curvature). To handle this we will go over a technique to stabilize newton's method using **Hessian regularization**. Finally to assure robust progress throughout the solution process and avoiding oscillations, we will go also show how to add **Armijo backtracking**. + +We will work with a toy scalar function which has multiple critical points (minima/maxima) which makes it a nice testbed to show where Newton can go wrong. The function is -We will use the toy function $f(x) = x^4 + x^3 - x^2 - x$ -which has both minima and maxima, so it’s perfect to show where Newton can go wrong. + +Before doing anything related to newton's method, let's just plot the actual function we have and visualize it! """ # ╔═╡ 27760607-b740-47dc-a810-c332baa2bd2d begin - #### Problem definition (scalar) #### + f(x::Real) = x^4 + x^3 - x^2 - x ∇f(x::Real) = 4x^3 + 3x^2 - 2x - 1 ∇²f(x::Real)= 12x^2 + 6x - 2 grad_f(x) = ∇f(x) hess_f(x) = ∇²f(x) - - #### Plot helpers #### + function plot_fx(; xrange=(-1.75, 1.25), npts=1000, title="Objective") xs = range(xrange[1], xrange[2], length=npts) - plt.plot(xs, f.(xs), lw=2, label="f(x)", xlabel="x", ylabel="f(x)", title=title) + ys = f.(xs) + ylo, yhi = extrema(ys) + pad = 0.05*(yhi - ylo + eps()) # small padding + plt.plot(xs, ys; + lw=2, label="f(x)", xlabel="x", ylabel="f(x)", title=title, + xlims=xrange, ylims=(ylo - pad, yhi + pad) + ) end - function plot_trace(xhist; xrange=(-1.75, 1.25), npts=1000, title="") + function plot_trace(xhist; xrange=(-1.75, 1.25), npts=1000, title="", + show_first_iter::Bool=true, show_first_model::Bool=true) + xs = range(xrange[1], xrange[2], length=npts) - p = plt.plot(xs, f.(xs), lw=2, label="f(x)", xlabel="x", ylabel="f(x)", title=title) - plt.scatter!(p, xhist, f.(xhist), marker=:x, ms=7, label="iterates") + ys = f.(xs) + ylo, yhi = extrema(ys) + pad = 0.05*(yhi - ylo + eps()) + + p = plt.plot(xs, ys; + lw=2, label="f(x)", xlabel="x", ylabel="f(x)", title=title, + xlims=xrange, ylims=(ylo - pad, yhi + pad) + ) + + x_in = [x for x in xhist if isfinite(x) && xrange[1] ≤ x ≤ xrange[2]] + if !isempty(x_in) + plt.scatter!(p, x_in, f.(x_in); marker=:x, ms=7, label="iterates") + end + + if show_first_iter && length(xhist) ≥ 2 + x0, x1 = xhist[1], xhist[2] + if isfinite(x0) && isfinite(x1) && + (xrange[1] ≤ x0 ≤ xrange[2]) && (xrange[1] ≤ x1 ≤ xrange[2]) && + isfinite(f(x0)) && isfinite(f(x1)) + plt.plot!(p, [x0, x1], [f(x0), f(x1)]; + lw=2, ls=:dash, label="first step x₀ → x₁" + ) + plt.scatter!(p, [x1], [f(x1)]; marker=:diamond, ms=8, label="x₁") + end + end + + if show_first_model && !isempty(xhist) && isfinite(xhist[1]) + x0 = xhist[1] + g0, H0 = grad_f(x0), hess_f(x0) + if isfinite(g0) && isfinite(H0) + m0(x) = f(x0) + g0*(x - x0) + 0.5*H0*(x - x0)^2 + width = 0.75 + xs_loc = range(max(xrange[1], x0 - width), min(xrange[2], x0 + width), length=250) + if length(xs_loc) > 1 + plt.plot!(p, xs_loc, m0.(xs_loc); ls=:dot, lw=2, label="quadratic model @ x₀") + end + end + end + return p end end +# ╔═╡ a098a49b-a368-4929-824c-385a06b88696 +plot_fx(title="f(x) = x^4 + x^3 - x^2 - x") -# ╔═╡ 7765e032-c520-4f97-b877-0d479f383f28 +# ╔═╡ 17ac372e-87e6-4649-ba9d-1df1cdb7b55b md""" -## 1) Inspect the objective - -Below is the graph of \(f(x)\). The multiple critical points (max/min) make it a nice testbed for Newton. +We can see that this function has a global minima on the right side of the plot close to $x_1\approx0.6$ whilst it has a local minima around $x_1\approx-1$. This is a perfect example as we can analyze what happens when we start in the basin of attraction of the local minima on the left. Now let's go over how the newton method should be implemented """ -# ╔═╡ a098a49b-a368-4929-824c-385a06b88696 -plot_fx(title="f(x) = x^4 + x^3 - x^2 - x") - # ╔═╡ a62f1b6a-87fe-4401-bc13-42166ca0e129 md""" -## 2) Plain Newton (no safeguards) +#### Plain Newton update -Newton update: +We want a **stationary point** of $f$, i.e. solve + +```math +\nabla f(x) = 0 +``` +Newton’s method does this by **linearizing the gradient** at the current point ($x_k$): -$x_{k+1} = x_k - \frac{\nabla f(x_k)}{\nabla^2 f(x_k)}$. +```math +\nabla f(x) \approx \nabla f(x_k) + \nabla^2 f(x_k)\,(x - x_k) +``` +Setting this approximation to zero and solving for (x) gives the update -If $\nabla^2 f(x_k) < 0$, the step **ascends** along \(f\). +```math +x_{k+1} = x_k - \big[\nabla^2 f(x_k)\big]^{-1}\,\nabla f(x_k) +``` -Use the sliders to choose a start and number of iterations. +In **one dimension**, the Hessian is just the scalar ($f''(x_k)$), so + +```math +\boxed{\,x_{k+1} = x_k - \dfrac{f'(x_k)}{f''(x_k)}\,} +``` + +**Quadratic-model view (same update).** Around (x_k), approximate (f) by the quadratic + +```math +m_k(s)= f(x_k)+ \nabla f(x_k)\, s + \tfrac12\, \nabla^2 f(x_k)\, s^2 . +``` + +Minimizing ($m_k$) w.r.t. ($s$) gives ($s_k = -\nabla f(x_k)/\nabla^2 f(x_k)$) (1D), hence the same update ($x_{k+1}=x_k+s_k$). +This reveals a key fact: + +* If ($f''(x_k) > 0$) (positive curvature), then ($s_k$) is a **descent direction** and the step tends to **decrease** (f). +* If ($f''(x_k) < 0$) (negative curvature), the local quadratic is **concave** and the same formula points **uphill**—plain Newton can **increase** (f). + +In higher dimensions the descent test is + +```math +\nabla f(x_k)^\top \big[\nabla^2 f(x_k)\big]^{-1}\nabla f(x_k) > 0, +``` + +which holds when the Hessian is positive definite; if the Hessian is indefinite/negative, Newton’s direction need not be descent. +""" + +# ╔═╡ be41026f-cb28-4647-8db6-1d243739f444 +md""" +The following code implements an iterative optimization algorithm using Newton for our 1D nonconvex scalar function (no safeguards yet) """ # ╔═╡ b2ddc048-4942-43bc-8faa-1921062d8c9c @@ -401,9 +493,16 @@ begin end +# ╔═╡ 1ffa2941-619b-400a-ba0f-56baa6ee7f59 +md""" +The first slider corresponds to the initial starting pointer iterate for the newton's method. The second slider corresponds to the number of iterations we should run the algorithm for. + +First test out the algorithm with an initial start position at $x=-1.5$ and $5$ iterations. The algorithm will converge to a local minima at $-1$. +""" + # ╔═╡ 6fd7a753-6db7-4c37-9e22-dc63dd3072c8 begin - @bind x0_plain PlutoUI.Slider(-1.75:0.01:1.1, default=-1.50, show_value=true) + @bind x0_plain PlutoUI.Slider(-1.75:0.01:0.5, default=-1.50, show_value=true) end @@ -415,19 +514,23 @@ end # ╔═╡ 096ebc95-f133-4ca3-b942-cf735faaa42b begin xhist_plain_1 = newton_optimize_plain(x0_plain; maxiters=iters_plain) - plot_trace(xhist_plain_1; title = "Plain Newton from x₀ = $(round(x0_plain,digits=3)) and iters = $(iters_plain)") + plot_trace( + xhist_plain_1; + title = "Plain Newton from x₀ = $(round(x0_plain,digits=3)) and iters = $(iters_plain))", + show_first_iter = true, + show_first_model = true, # set false if you only want the step segment + ) end - # ╔═╡ fdf41c76-4405-49e0-abfa-5c5193de99f4 md""" -## 3) Globalization: Regularization + Armijo backtracking +To avoid issues with newton, we add globalization strategies which include regularization and Armijo backtracking Two simple fixes: -1. **Regularization:** If $\nabla^2 f(x_k)\le 0$, replace it by $\nabla^2 f(x_k) + \beta $ with $\beta>0$ (LM-style), so the step is a **descent** direction. -2. **Armijo line search:** Scale the step by \(\alpha \in (0,1]\) until -$f(x_k+\alpha\Delta x) \le f(x_k) + b\,\alpha\,\nabla f(x_k)\,\Delta x$, +1. **Regularization:** If $\nabla^2 f(x_k)\le 0$, replace it by $\nabla^2 f(x_k) + \beta $ with $\beta>0$, so the step is a **descent** direction. +2. **Armijo line search:** Scale the step by $\alpha \in (0,1]$ until +$f(x_k+\alpha\Delta x) \le f(x_k) + b\,\alpha\,\nabla f(x_k)\,\Delta x$ with typical $b=10^{-4}$, $c=0.5$ for backtracking. We’ll expose both as toggles. @@ -436,7 +539,8 @@ We’ll expose both as toggles. # ╔═╡ d259c1b8-3716-4f80-b462-3b3daebb444d md""" -### Controls +#### Controls +Use the controls to find settings where it's clear that the left/last plot is the best. I.e the use of regularization and backtracking ensures we move towards the true optimal minima with no over-shooting due to backtracking. A good example is a starting point of roughly -0.36 and $\beta_0=1$ and $c=0.5$. In that setting we can see that both only the plain newton cannot solve to optimality. The pure regularized version has oscillation as it overshoots the minima. On the other hand the regularized and line search implementation is able to find the global minima efficiently. **Start x₀:** $(@bind x0_cmp Slider(-1.5:0.01:0.75, default=0.00, show_value=true)) **Iterations:** $(@bind iters_cmp Slider(1:40, default=10, show_value=true)) @@ -455,356 +559,506 @@ md""" """ +# ╔═╡ 76c51e32-bd0e-4e72-8c47-64352da13d3e +md""" +If you are interested in seeing how the plotting works, open the code for the cell below. +""" + # ╔═╡ e012d7e0-0181-49d0-bb78-995378c4f87a md""" -## 4) Takeaways +Here are the main takeaways for part II. - **Plain Newton** is fast **if** you’re in a nice region and the Hessian is positive. - **Regularization** turns the Newton step into a descent direction when curvature is negative or near-singular. - **Armijo backtracking** fixes overshooting and makes progress predictable without hand-tuning step sizes. - These building blocks generalize to higher-dimensional problems and constrained KKT systems (next section of the tutorial). - -**Further experiments** -- Change the objective (keep the same drivers). -- Try different Armijo parameters $b, c$. -- Visualize **$|\nabla f(x_k)|$** vs iteration to see convergence. """ +# ╔═╡ 26c887ce-d95b-4e38-9717-e119de0e80ca +md""" +## Part III — Constrained Optimization (KKT) -# ╔═╡ 0a3eb748-fab3-4f8b-993e-2246c32fb6aa - +We now move from unconstrained to **equality-constrained** minimization, then note what changes for **inequalities**. -# ╔═╡ 3989b8e1-ac1f-430d-9d72-e298ba7ae0ca +##### 1) Problem and picture +We solve -# ╔═╡ e466ffb2-fcc8-4c3b-9059-7fd68a4265f2 +```math +\min_{x\in\mathbb{R}^n} f(x)\quad\text{s.t.}\quad C(x)=0,\qquad C:\mathbb{R}^n\to\mathbb{R}^m. +``` +**Geometry.** At an optimal $x^\star$ on the manifold $C(x)=0$, the gradient must be orthogonal to all feasible directions (the **tangent space**). Equivalently, $\nabla f(x^\star)$ lies in the span of the constraint normals: -# ╔═╡ 09a301c7-c1f4-4890-afe9-fbc1d7c3b905 +```math +\nabla f(x^\star) + J_C(x^\star)^{\!T}\lambda^\star = 0, \qquad C(x^\star)=0. +``` +This is the KKT stationarity + feasibility for equalities. (Under a mild **constraint qualification** like LICQ, these conditions are necessary at a local minimizer.) -# ╔═╡ 53020b8a-4948-467a-8629-bef9496d0374 +**Lagrangian.** $L(x,\lambda)=f(x)+\lambda^{T}C(x)$. +KKT (equalities only): $\nabla_x L(x,\lambda)=0,\ C(x)=0$. +""" + -# ╔═╡ 26c887ce-d95b-4e38-9717-e119de0e80ca -md""" -# Part III - Constrained Optimization (KKT) +# ╔═╡ 52718e0b-f958-445e-b9b6-9e5baf09e81a +md""" +##### 2) Where the KKT linear system comes from -#### Setup (equality constraints) -We solve -```math -\min_{x\in\mathbb{R}^n} f(x) \quad \text{s.t.}\ C(x)=0,\qquad C:\mathbb{R}^n\to\mathbb{R}^m. -``` +At iterate $(x,\lambda)$, take a Newton step $(\Delta x,\Delta\lambda)$ by **linearizing** the two KKT equations: -**Geometric picture.** At an optimum on the manifold `C(x)=0`, the gradient is orthogonal to the tangent space: +* **Feasibility** $C(x+\Delta x)\approx C(x)+J_C(x)\,\Delta x=0 \;\Rightarrow\; J_C(x)\,\Delta x=-C(x)$. +* **Stationarity** $\nabla_x L(x+\Delta x,\lambda+\Delta\lambda)\approx \nabla_x L(x,\lambda)+H\,\Delta x+J_C(x)^{T}\Delta\lambda=0$, -```math -\nabla f(x^\star)\ \perp\ \mathcal{T}_{x^\star}=\{p:\ J_C(x^\star)p=0\}. -``` +where $H=\nabla^2_{xx}L(x,\lambda)$. -Equivalently, the gradient is a combination of the constraint normals: +with the (approximate) **Lagrangian Hessian** ```math -\nabla f(x^\star)+J_C(x^\star)^{\!T}\lambda^\star=0,\qquad C(x^\star)=0. +H \;\approx\; \nabla^2 f(x)\;+\;\sum_{i=1}^m \lambda_i\,\nabla^2 C_i(x). ``` -**Lagrangian.** Define $L(x,\lambda)=f(x)+\lambda^{\!T}C(x)$. -""" - -# ╔═╡ a1b7f614-710a-4ce8-815b-2f94754088c4 -md""" -#### From conditions to a solver (Newton/SQP on the KKT system) - -Linearizing feasibility and stationarity gives the **saddle-point** (KKT) system: +Stacking these two linearized equations gives the **saddle-point (KKT) system**: ```math \begin{bmatrix} -H & J_C(x)^{\!T} \\ -J_C(x) & 0 +H & J_C^{\!T}\\ +J_C & 0 \end{bmatrix} \begin{bmatrix}\Delta x\\ \Delta\lambda\end{bmatrix} =- \begin{bmatrix} -\nabla f(x)+J_C(x)^{\!T}\lambda\\ +\nabla f(x)+J_C^{\!T}\lambda\\ C(x) -\end{bmatrix}, -\qquad -H \approx \nabla^2 f(x) + \sum_{i=1}^m \lambda_i\,\nabla^2 C_i(x). +\end{bmatrix}. ``` -Two common choices for `H`: +This is exactly the equality-constrained Newton step (feasible or infeasible start variants differ only in the right-hand side). -* **Full Newton (Hessian of the Lagrangian):** as written above (fast near a solution). -* **Gauss–Newton/SQP:** drop the constraint-curvature term so $H\approx\nabla^2 f(x)$ (often more robust far from the solution). +**Two common (H) choices.** -> Practical: this system is symmetric indefinite; block elimination (Schur complement) and sparse factorizations are standard. -""" +* **Full Newton (Lagrangian Hessian):** as above — best local rate near a solution. +* **Gauss–Newton/SQP:** drop the constraint-curvature term, taking ($H\approx\nabla^2 f(x)$) which is often more robust far from the solution. -# ╔═╡ 6fd82e73-6fef-4f7f-b291-f94cbac0d268 -md""" -#### Inequality constraints (KKT, first-order) +**How we actually solve it.** The matrix is symmetric indefinite. Standard tactics: -For $c(x)\ge 0$, +* **Block elimination (Schur complement):** if ($H$) is nonsingular, ```math -\begin{aligned} -&\text{Stationarity:} && \nabla f(x)-J_c(x)^{\!T}\lambda=0,\\ -&\text{Primal feasibility:} && c(x)\ge 0,\\ -&\text{Dual feasibility:} && \lambda\ge 0,\\ -&\text{Complementarity:} && \lambda^{\!T}c(x)=0\quad(\lambda_i\,c_i(x)=0\ \forall i). -\end{aligned} +(J_C H^{-1}J_C^{\!T})\,\Delta\lambda \;=\; J_C H^{-1}(\nabla f+J_C^{\!T}\lambda)+C, +\quad\text{then back-solve for }\Delta x. ``` -**Interpretation.** Active constraints ($c_i(x)=0$) may carry nonzero multipliers; inactive ones ($c_i(x)>0$) have $\lambda_i=0$. +* **LDLᵀ (symmetric-indefinite) factorization** for sparse problems. """ -# ╔═╡ 52718e0b-f958-445e-b9b6-9e5baf09e81a - - # ╔═╡ edce5b27-9af8-4010-9d9f-60681b2f427c +md""" +##### 3) Inequalities: what changes (just the essentials) +For $c(x)\ge 0$ (component-wise), first-order KKT add **sign** and **complementarity**: -# ╔═╡ c139ccc9-0355-4c41-8d82-0c97fef50900 +```math +\begin{aligned} +&\text{Stationarity:}& &\nabla f(x)-J_c(x)^{\!T}\lambda=0,\\ +&\text{Primal feasibility:}& &c(x)\ge 0,\\ +&\text{Dual feasibility:}& &\lambda\ge 0,\\ +&\text{Complementarity:}& &\lambda^{\!T}c(x)=0\quad(\lambda_i\,c_i(x)=0). +\end{aligned} +``` + +**Interpretation.** If constraint $i$ is **active** $(c_i(x)=0)$, its multiplier may be positive; if **inactive** $(c_i(x)>0)$, then $\lambda_i=0$. +""" -# ╔═╡ 43c5ccb9-d51e-4d73-b7be-f8b9dd599130 +# ╔═╡ 4a3bdacd-bc17-4b10-bbed-6d34f0531d60 md""" -# Part III Code - Equality Constrained Minimization (KKT) +##### Small example -We solve +We will now do a tiny code example of equality-constrained problem and solve it with a Newton/SQP-style **KKT step** plus a simple **merit-function line search**. The goal is to see the geometry and the update. + +We solve a **single equality constraint** problem in $\mathbb{R}^2$: +```math +\min_x\; f(x)\qquad \text{s.t. } c(x)=0. +``` + +We’ll use a convex quadratic objective and a curved constraint: ```math -\min_x\; f(x) \quad \text{s.t.}\; c(x)=0 +f(x)=\tfrac12(x-x_\star)^\top Q(x-x_\star),\qquad +c(x)=x_1^2+2x_1-x_2. ``` -with one equality constraint. The **KKT system** for a Newton/SQP step is +A Newton/SQP step ($\Delta x,\Delta\lambda$) solves the **KKT system** ```math \begin{bmatrix} H & J^\top \\ J & 0 \end{bmatrix} \begin{bmatrix} \Delta x \\ \Delta \lambda \end{bmatrix} -= - \begin{bmatrix} \nabla f(x)+J^\top \lambda \\ c(x) \end{bmatrix}, -\quad J=\nabla c(x)^\top,\; H\approx\nabla^2\!L(x,\lambda). +=- +\begin{bmatrix} \nabla f(x)+J^\top\lambda \\ c(x) \end{bmatrix}, +\quad J=\nabla c(x)^\top,\ \ H\approx\nabla^2\!L(x,\lambda). ``` -We'll compare: +We’ll compare two (H) choices: -* **Gauss–Newton/SQP**: $H=\nabla^2 f$ (drop constraint curvature) ⇒ robust PD top-left. -* **Full**: $H=\nabla^2 f + \lambda \nabla^2 c$. +* **Gauss–Newton/SQP:** ($H=\nabla^2 f$) (drop constraint curvature). +* **Full:** ($H=\nabla^2 f + \lambda,\nabla^2 c$). -We’ll also use a simple merit function $\phi(x)=f(x)+\frac{\rho}{2}\,c(x)^2$ with Armijo backtracking. +To globalize we use the **merit** ($\phi(x)=f(x)+\tfrac{\rho}{2}c(x)^2$ with Armijo backtracking. """ -# ╔═╡ e0a7f431-61dd-4df2-b53c-d81c7a302baf +# ╔═╡ 20fa9f12-ef19-482a-be83-feaed95109c3 begin - # === Objective & constraint (KKT section) === - Q_kkt = Diagonal([0.5, 1.0]) - xstar_kkt = [1.0, 0.0] - - # Scalar objective (use dot() to ensure a Float64, not 1×1) - f_kkt(x::AbstractVector) = 0.5 * dot(x .- xstar_kkt, Q_kkt * (x .- xstar_kkt)) - ∇f_kkt(x::AbstractVector) = Q_kkt * (x .- xstar_kkt) - ∇²f_kkt(::AbstractVector) = Q_kkt # constant SPD 2×2 - - # One equality constraint: c(x) = x1^2 + 2x1 - x2 - c_kkt(x::AbstractVector) = x[1]^2 + 2x[1] - x[2] - ∇c_kkt(x::AbstractVector) = [2x[1] + 2.0, -1.0] # length-2 - ∇²c_kkt(::AbstractVector) = [2.0 0.0; 0.0 0.0] # constant 2×2 + # objective: bowl centered at x⋆ + Q_kkt = Diagonal([0.5, 1.0]) + xstar_kkt = [1.0, 0.0] + + f_kkt(x::AbstractVector) = 0.5 * dot(x .- xstar_kkt, Q_kkt * (x .- xstar_kkt)) + ∇f_kkt(x::AbstractVector) = Q_kkt * (x .- xstar_kkt) + ∇²f_kkt(::AbstractVector) = Q_kkt # constant SPD + + # one equality constraint: x₂ = x₁² + 2x₁ + c_kkt(x::AbstractVector) = x[1]^2 + 2x[1] - x[2] + ∇c_kkt(x::AbstractVector) = [2x[1] + 2.0, -1.0] + ∇²c_kkt(::AbstractVector) = [2.0 0.0; 0.0 0.0] end - - -# ╔═╡ 3e3d6e18-f922-4e91-8eaf-bc26b8f91757 +# ╔═╡ 6c560afb-921d-4f34-b77b-a31e37b7d571 +begin + ϕ_kkt(x; ρ=1.0) = f_kkt(x) + 0.5*ρ*c_kkt(x)^2 + ∇ϕ_kkt(x; ρ=1.0) = ∇f_kkt(x) .+ ρ*c_kkt(x).*∇c_kkt(x) + + """ + kkt_step(x, λ; method=:gn, δ=1e-8) + + Build and solve the 3×3 KKT system: + [ H J'; J 0 ] [Δx; Δλ] = -[ ∇f + J'λ; c ] + with H = ∇²f (method=:gn) or H = ∇²f + λ∇²c (method=:full). + δ adds a tiny ridge to H for numerical robustness. + """ + function kkt_step(x::AbstractVector, λ::Real; method::Symbol=:gn, δ::Float64=1e-8) + H = Matrix(∇²f_kkt(x)) + if method === :full + H .+= λ .* ∇²c_kkt(x) + end + @inbounds for i in 1:2; H[i,i] += δ; end + J = reshape(∇c_kkt(x), 1, :) # 1×2 + rhs = -vcat(∇f_kkt(x) .+ λ .* ∇c_kkt(x), c_kkt(x)) + K = [H J'; J 0.0] + Δ = try + K \ rhs + catch + pinv(K) * rhs + end + return Δ[1:2], Δ[3] + end + + """ + kkt_solve(x0, λ0; iters=8, method=:gn, linesearch=true, ρ=1.0, b=1e-4, cdec=0.5) + + A few KKT iterations with Armijo on ϕ. Returns (X, Λ) where + X ∈ ℝ^{2×(iters+1)} collects iterates. + """ + function kkt_solve(x0, λ0; iters=8, method=:gn, linesearch=true, ρ=1.0, b=1e-4, cdec=0.5) + x = copy(x0); λ = float(λ0) + X = reshape(x, 2, 1); Λ = [λ] + for _ in 1:iters + Δx, Δλ = kkt_step(x, λ; method=method) + + # Armijo w/ descent fallback on φ + α = 1.0 + if linesearch + φx = ϕ_kkt(x; ρ=ρ) + gφ = ∇ϕ_kkt(x; ρ=ρ) + slope = dot(gφ, Δx) + if !(isfinite(slope)) || slope ≥ 0 + Δx .= -gφ + Δλ = 0.0 + slope = -dot(gφ, gφ) + end + for _ in 1:20 + if ϕ_kkt(x .+ α .* Δx; ρ=ρ) ≤ φx + b*α*slope + break + end + α *= cdec + end + end + + x .+= α .* Δx + λ += α * Δλ + X = hcat(X, x); push!(Λ, λ) + end + return X, Λ + end +end + +# ╔═╡ 4d8533ef-be1b-45c9-acaf-ce278c3e5db7 begin - function landscape_plot_kkt(; xlim=(-4,4), ylim=(-4,4), nsamp=120, show_colorbar=false) + # Find constrained minimizer of f along the curve x₂ = x₁² + 2x₁ (within the window) + function constrained_star_kkt(; xlim=(-4,4), ylim=(-4,4)) + xs = range(xlim[1], xlim[2], length=1201) + ys = @. xs^2 + 2xs + mask = (ys .>= ylim[1]) .& (ys .<= ylim[2]) + xs_in = collect(xs)[mask]; ys_in = ys[mask] + if isempty(xs_in) # fallback if curve is off-screen + xs_in = collect(xs); ys_in = ys + end + vals = [ f_kkt([x,y]) for (x,y) in zip(xs_in, ys_in) ] + i = argmin(vals) + return xs_in[i], ys_in[i] + end + + function landscape_plot_kkt(; xlim=(-4,4), ylim=(-4,4), nsamp=121) xs = range(xlim[1], xlim[2], length=nsamp) ys = range(ylim[1], ylim[2], length=nsamp) - Z = [ f_kkt([x,y]) for x in xs, y in ys ] + Z = [ f_kkt([x,y]) for y in ys, x in xs ] p = plt.contour(xs, ys, Z; - levels=18, - colorbar=show_colorbar, - xlabel="x₁", ylabel="x₂", - title="Objective contours & constraint (c(x)=0)", - legend=:topleft, - aspect_ratio=:equal, - xlims=(xlim[1], xlim[2]), - ylims=(ylim[1], ylim[2])) - - xc = collect(xs) - yc = @. xc^2 + 2xc + levels=18, xlabel="x₁", ylabel="x₂", + title="KKT path on objective contours (c(x)=0)", + legend=:bottomright, aspect_ratio=:equal, + xlims=(xlim[1], xlim[2]), ylims=(ylim[1], ylim[2])) + + # constraint curve + xc = collect(xs); yc = @. xc^2 + 2xc plt.plot!(p, xc, yc; lw=2, label="c(x)=0") - plt.scatter!(p, [xstar_kkt[1]], [xstar_kkt[2]]; marker=:star5, ms=8, label="x⋆") + + + xcs, ycs = constrained_star_kkt(xlim=xlim, ylim=ylim) + plt.scatter!(p, [xcs], [ycs]; + marker=:star5, ms=9, label="constrained x̂") - plt.xlims!(p, (xlim[1], xlim[2])) - plt.ylims!(p, (ylim[1], ylim[2])) return p end - plot_path_kkt!(p, X) = begin - plt.plot!(p, X[1,:], X[2,:]; marker=:x, ms=6, lw=1.5, label="iterates") - plt.xlims!(p, (-4,4)); plt.ylims!(p, (-4,4)) - p - end + # Path overlay: X's for iterates, star for the current x0 from sliders + plot_path_kkt!(p, X; x0::AbstractVector = X[:,1]) = begin + # iterates + plt.plot!(p, X[1,:], X[2,:]; marker=:x, ms=6, lw=1.5, label="iterates") + plt.scatter!(p, [x0[1]], [x0[2]]; marker=:star5, ms=9, label="start x₀") + plt.xlims!(p, (-4,4)); plt.ylims!(p, (-4,4)) + p + end + end +# ╔═╡ 79604603-df5e-47de-aeb8-08e7658fe190 +md""" +### Controls -# ╔═╡ 31407c3f-8f3d-4dcc-bc49-cedd8ca14013 -begin - # === KKT step + simple merit line search (KKT section) === +**Start** +x₁ $(@bind x1_kkt Slider(-3.5:0.1:3.5, default=-1.5, show_value=true)) +x₂ $(@bind x2_kkt Slider(-3.0:0.1:3.0, default=-1.0, show_value=true)) - ϕ_kkt(x; ρ=1.0) = f_kkt(x) + 0.5*ρ*c_kkt(x)^2 - ∇ϕ_kkt(x; ρ=1.0) = ∇f_kkt(x) .+ ρ * c_kkt(x) .* ∇c_kkt(x) +**Dual init** +λ₀ $(@bind λ0_kkt Slider(-2.0:0.1:2.0, default=0.0, show_value=true)) - """ - kkt_step_kkt(x, λ; method=:gn, δ=1e-8) +**Solver** +Iters $(@bind iters_kkt Select([3,5,8,10,15,20]; default=8)) +Method $(@bind method_kkt Select([:gn, :full]; default=:gn)) +Line search $(@bind use_ls_kkt CheckBox(default=true)) +ρ (merit weight) $(@bind rho_kkt Slider(0.1:0.1:5.0, default=1.0, show_value=true)) - Solve the 3×3 KKT system: - [ H C'; C 0 ] [Δx; Δλ] = -[ ∇f + C'λ; c ] - with C = ∇c(x) as a 1×2 row, H = ∇²f (Gauss–Newton) or ∇²f + λ∇²c (Full). - A small ridge δ is added to H on the diagonal for numerical robustness. - """ - function kkt_step_kkt(x::AbstractVector, λ::Real; method::Symbol=:gn, δ::Float64=1e-8) - g = ∇f_kkt(x) # 2 - cv = ∇c_kkt(x) # length-2 - C = reshape(cv, 1, :) # 1×2 - H = Matrix(∇²f_kkt(x)) - if method === :full - H .+= λ .* ∇²c_kkt(x) - end - # add small ridge on diagonal (avoid UniformScaling arithmetic) - @inbounds for i in 1:size(H,1); H[i,i] += δ; end - - K = [H C'; C 0.0] # 3×3 - rhs = -vcat(g .+ λ .* cv, c_kkt(x)) - Δ = try - K \ rhs - catch - pinv(K) * rhs - end - Δx = Δ[1:2]; Δλ = Δ[3] - return Δx, Δλ - end +$(@bind run_kkt Button("Run KKT")) +""" - """ - kkt_solve_kkt(x0, λ0; iters=8, method=:gn, linesearch=true, ρ=1.0, b=1e-4, cdec=0.5) +# ╔═╡ c249309e-a47c-48d9-9b06-d448e0a57a28 +let + run_kkt + + x1_kkt; x2_kkt; λ0_kkt; iters_kkt; method_kkt; use_ls_kkt; rho_kkt - A few KKT iterations with optional Armijo backtracking on ϕ_kkt. - Returns (X, Λ) with X ∈ ℝ^{2×(iters+1)}. - """ - function kkt_solve_kkt(x0, λ0; iters=8, method=:gn, linesearch=true, ρ=1.0, b=1e-4, cdec=0.5) - x = copy(x0); λ = float(λ0) - X = reshape(x, 2, 1); Λ = [λ] - for _ in 1:iters - Δx, Δλ = kkt_step_kkt(x, λ; method=method) - α = 1.0 - if linesearch - φx = ϕ_kkt(x; ρ=ρ) - gφ = ∇ϕ_kkt(x; ρ=ρ) - slope = dot(gφ, Δx) - for _ in 1:20 - if ϕ_kkt(x .+ α .* Δx; ρ=ρ) <= φx + b*α*slope - break - end - α *= cdec - end - end - x .+= α .* Δx - λ += α * Δλ - X = hcat(X, x); push!(Λ, λ) - end - return X, Λ - end + x0 = [x1_kkt, x2_kkt] + X, Λ = kkt_solve(x0, λ0_kkt; iters=iters_kkt, method=method_kkt, + linesearch=use_ls_kkt, ρ=rho_kkt) + + fig = landscape_plot_kkt() + plot_path_kkt!(fig, X; x0=x0) + + feas = abs(c_kkt(X[:,end])) + stat = norm( ∇f_kkt(X[:,end]) .+ (Λ[end] .* ∇c_kkt(X[:,end])) ) + fig end +# ╔═╡ 917ed8f6-d451-4725-a794-37b6bc7e46f5 +md""" +**What to notice** + +- The iterate stays close to the constraint curve (feasibility via the KKT step). +- **Gauss–Newton** ($H=\nabla^2 f$) is usually robust from farther starts. +- **Full** ($H=\nabla^2 f + \lambda\nabla^2 c$) accelerates near a solution. +- Armijo on $\phi(x)=f(x)+\tfrac{\rho}{2}c(x)^2$ avoids oscillation and keeps the path stable across slider settings. +""" -# ╔═╡ a61a63b7-716e-4500-9bc6-ab2caf9062e7 +# ╔═╡ c0a75eef-e3e1-4e5f-8184-292827870cb2 md""" -### Controls (KKT) +# Part IV — Interior-Point Method -**x₁** $(@bind x1_kkt Slider(-3.5:0.1:1.5, default=-1.5, show_value=true)) -**x₂** $(@bind x2_kkt Slider(-3.0:0.1:3.0, default=-1.0, show_value=true)) +##### Why Interior-Point? -**λ₀** $(@bind λ0_kkt Slider(-2.0:0.1:2.0, default=0.0, show_value=true)) -**Iterations** $(@bind iters_kkt Select([3, 5, 8, 10, 15, 20]; default=8)) +We want to solve an inequality-constrained problem -**Method** $(@bind method_kkt Select([:gn, :full]; default=:gn)) -**Line search** $(@bind use_ls_kkt CheckBox(default=true)) +$\min_x\ f(x)\quad\text{s.t.}\quad c(x)\ge 0$ -**ρ (merit weight)** $(@bind rho_kkt Slider(0.1:0.1:5.0, default=1.0, show_value=true)) +Interior-point methods keep the iterate **strictly inside** the feasible set and add a +**barrier** that explodes as we approach the boundary: -$(@bind run_kkt_btn Button("Run KKT")) -""" +$F_\mu(x)\;=\;f(x)\;-\;\mu\,\log c(x),\qquad \mu>0$ +For a fixed $\mu$ we take a few Newton steps on $F_\mu$ (with a feasibility-aware line +search). Then we **decrease** $\mu$ and repeat. As $\mu\downarrow 0$, the minimizer of $F_\mu$ +slides along the **central path** toward the true constrained optimum. -# ╔═╡ 1fda2985-ab68-4460-98be-8621e5a5f1c8 -let - # === Run & plot (KKT section) === - run_kkt_btn # recompute only when the button is clicked - x0 = [x1_kkt, x2_kkt] - X, Λ = kkt_solve_kkt(x0, λ0_kkt; iters=iters_kkt, method=method_kkt, - linesearch=use_ls_kkt, ρ=rho_kkt) +More concretely, we solve the inequality problem by following minimizers of the **barrier objective** +$F_\mu(x) \;=\; f(x)\;-\;\mu\,\log c(x), \qquad \mu>0$, +staying strictly inside $c(x)>0$ and shrinking $\mu$. - fig = landscape_plot_kkt() - plot_path_kkt!(fig, X) +**Inputs.** +- feasible start $x_0$ with $c(x_0)>0$ (or “interiorize” a guess), +- barrier $\mu_0>0$, shrink factor $\tau\in(0,1)$ (e.g. $\tau=0.3$), +- inner Newton tolerance / max steps. - feas = abs(c_kkt(X[:,end])) - stat = norm( ∇f_kkt(X[:,end]) .+ (Λ[end] .* ∇c_kkt(X[:,end])) ) +**Derivatives used by Newton.**: The gradient and Hessian of the barrier objective are - plt.annotate!(fig, -1.8, 15.3, plt.text("feas = $(round(feas,digits=4))", 9)) - plt.annotate!(fig, -1.8, 12.3, plt.text("stat = $(round(stat,digits=4))", 9)) +$\nabla F_\mu(x)=\nabla f(x)\;-\;\frac{\mu}{c(x)}\,\nabla c(x)$ +$\nabla^2 F_\mu(x)=\nabla^2 f(x) +\;+\;\frac{\mu}{c(x)^2}\,\nabla c(x)\nabla c(x)^\top +\;-\;\frac{\mu}{c(x)}\,\nabla^2 c(x)$ - fig -end +**Inner loop (Newton with feasibility-aware backtracking, fixed $\mu$).** -# ╔═╡ ea0f0b1e-65b1-4c66-b9fa-7f2c428e3459 - +1. Compute the Newton direction by solving + +$\nabla^2 F_\mu(x_k)\,\Delta x_k \;=\; -\,\nabla F_\mu(x_k)$ -# ╔═╡ b4f6cce9-c39e-4325-a0e3-ab9c38179894 +2. Choose step length $\alpha \in \{1,\; \beta,\; \beta^2,\ldots\}$ (e.g. $beta=0.5$) until both hold: + - **interior:** $c(x_k+\alpha\Delta x_k) > 0$ + - **Armijo decrease:** + + $F_\mu(x_k+\alpha\Delta x_k) \;\le\; + F_\mu(x_k) + b\,\alpha\,\nabla F_\mu(x_k)^\top \Delta x_k$ with $b\in(0,1)$ (e.g. $b=10^{-4}$). +3. Update $x_{k+1} \leftarrow x_k+\alpha\Delta x_k$. +4. Stop the inner loop if $\|\nabla F_\mu(x_{k+1})\|$ is small or the step count is reached. + +**Outer loop (reduce the barrier, warm-start).** + +1. Set $\mu \leftarrow \tau\,\mu$. +2. Re-run the inner loop starting at the last $x$. +3. Stop when $\mu \le \mu_{\min}$ (or changes in $x$ become tiny). + +This produces a sequence that tracks the **central path** and approaches the constrained minimizer as $\mu\downarrow 0$. +""" + + +# ╔═╡ 60cc80f1-818b-45d4-8248-bf2e0bfb6936 md""" -# Part IV - Log-domain Interior-Point Method (IPM): Tiny QP +##### Our tiny example + +Objective (a shifted quadratic bowl): + +$f(x)=\tfrac12\,(x-x_\star)^\top Q\,(x-x_\star), +\qquad +Q=\mathrm{diag}(0.5,\,1),\quad x_\star=\begin{bmatrix}1 \\ 0\end{bmatrix}$ -We solve a 2D quadratic program with one inequality: -- Objective: minimize $f(x) = \tfrac{1}{2}(x - [1,0])^\top Q (x - [1,0])$ with $Q=\mathrm{diag}(0.5, 1)$. -- Constraint: $c(x) = (-1, 1)\cdot x - 1 \ge 0$ (i.e., $x_2 \ge x_1 + 1$). +Curved inequality (feasible region **above** the curve): -**Log-domain substitution** for inequality handling: -$s = \sqrt{\rho}\,e^{\sigma}$, $\lambda = \sqrt{\rho}\,e^{-\sigma}$ so that $s\lambda = \rho$. +$c(x)=x_2-\bigl(x_1^2+2x_1\bigr)\ \ge 0$ -We solve the relaxed KKT system $r(z;\rho)=0$ with unknowns $z=[x;\sigma]$ using damped Newton plus Armijo backtracking on the merit function $\phi(z)=\tfrac{1}{2}\lVert r(z)\rVert^2$. +The unconstrained minimizer $x_\star=(1,0)$ is **infeasible** for this constraint, so the true solution lies **on the boundary** $c(x)=0$. +In the plot you’ll see: +- the boundary $x_2=x_1^2+2x_1$ (orange), +- a gold star at $x_\star$ (for reference), +- red **×** markers for the **barrier Newton iterates**. + +Two practical details: + +1. We keep iterates strictly feasible with a tiny **interiorize** step if $c(x)\le 0$. +2. The line search accepts steps only if $c(x+\alpha\Delta x)>0$ **and** + $F_\mu$ decreases (Armijo condition). """ -# ╔═╡ 252554be-a839-4770-b222-fbb6a32df2ef -begin - # Quadratic objective - Q = Diagonal([0.5, 1.0]) - f(x) = 0.5 * dot(x .- [1.0, 0.0], Q * (x .- [1.0, 0.0])) - ∇f(x) = Q * (x .- [1.0, 0.0]) - - # Single inequality c(x) ≥ 0 with gradient A = (-1, 1) - A = [-1.0, 1.0] # length-2 vector - b = 1.0 - c(x) = dot(A, x) - b # scalar - J(x) = A' # 1×2 (shown for reference) -end +# ╔═╡ 8a5029b2-2355-4b9b-84d5-50970c6a4b44 +begin + + # Quadratic bowl (unconstrained minimizer at x⋆ = (1, 0)) + Q = Diagonal([0.5, 1.0]) + xstar = [1.0, 0.0] + f(x) = 0.5 * dot(x .- xstar, Q * (x .- xstar)) + ∇f(x) = Q * (x .- xstar) + ∇²f(::AbstractVector) = Q + + # Curved inequality: c(x) = x1^2 + 2 x1 - x2 ≥ 0 + c(x) = x[2] - (x[1]^2 + 2x[1]) + ∇c(x) = [-2x[1] - 2.0, 1.0] + ∇²c(::AbstractVector) = [-2.0 0.0; 0.0 0.0] # constant + + # Barrier objective pieces + Fμ(x, μ) = f(x) - μ * log(c(x)) + ∇Fμ(x, μ) = ∇f(x) .- (μ / c(x)) .* ∇c(x) + ∇²Fμ(x, μ) = ∇²f(x) .+ (μ / (c(x)^2)) .* (∇c(x) * ∇c(x)') .- (μ / c(x)) .* ∇²c(x) + + # Keep the iterate strictly feasible: nudge along +∇c until c(x) > eps + function interiorize(x; eps=1e-3) + cx = c(x) + cx > eps && return x + g = ∇c(x) + t = (eps - cx) / max(norm(g)^2, 1e-12) # first-order step + return x .+ t .* g + end -# ╔═╡ 62d85874-9673-4893-bbff-fa75b4462e96 -begin - # Confidence check: our analytic ∇f, ∇²f vs automatic differentiation - fd_grad(x) = ForwardDiff.derivative(f, x) - fd_hess(x) = ForwardDiff.derivative(fd_grad, x) + # A few Newton steps on Fμ with Armijo + feasibility guard + function barrier_newton(x0, μ; steps=8, b=1e-4, shrink=0.5) + x = interiorize(x0) + X = reshape(x, 2, 1) + for _ in 1:steps + g = ∇Fμ(x, μ) + H = ∇²Fμ(x, μ) # SPD near the interior + Δ = - H \ g + # Backtrack until c(x+αΔ)>0 and Fμ decreases + α = 1.0 + Fx = Fμ(x, μ); slope = dot(g, Δ) + while true + x_try = x .+ α .* Δ + if c(x_try) > 0 && Fμ(x_try, μ) <= Fx + b*α*slope + x = x_try + break + end + α *= shrink + if α < 1e-12; break; end + end + X = hcat(X, x) + if norm(g) < 1e-10; break; end + end + return X + end - xsamp = [-1.0, -0.25, 0.0, 0.5, 1.0] - grad_err = maximum(abs.(fd_grad.(xsamp) .- ∇f.(xsamp))) - hess_err = maximum(abs.(fd_hess.(xsamp) .- ∇²f.(xsamp))) - - (grad_err < 1e-10 && hess_err < 1e-10) ? - tip(md"AD check passed: analytic derivatives match ForwardDiff on sample points.") : - danger(md"AD check mismatch. Largest errors — grad: $(grad_err), hess: $(hess_err)") + # Plot: contours, constraint, stars, iterates + function ipm_plot(X; legend=:bottomright) + xs = range(-4, 4, length=181) + ys = range(-4, 4, length=181) + Z = [ f([x,y]) for y in ys, x in xs ] + p = plt.contour(xs, ys, Z; levels=18, aspect_ratio=:equal, + xlims=(-4,4), ylims=(-4,4), + xlabel="x₁", ylabel="x₂", + title="Interior-Point (log-barrier)", + legend=legend) + # constraint curve: x2 = x1^2 + 2 x1 + xc = collect(xs); yc = @. xc^2 + 2xc + plt.plot!(p, xc, yc; lw=2, label="c(x)=0 (boundary)") + # stars + plt.scatter!(p, [xstar[1]], [xstar[2]]; marker=:star5, ms=9, label="unconstrained x⋆") + # start + iterates + plt.scatter!(p, [X[1,1]], [X[2,1]]; marker=:star5, ms=9, label="start x₀") + plt.plot!(p, vec(X[1,:]), vec(X[2,:]); marker=:x, ms=6, lw=2, label="barrier iterates") + return p + end end + # ╔═╡ 22bfe0a3-c61b-4dfe-8f20-a8bf807c2e14 begin # Regularized Newton direction (scalar LM-style) @@ -900,242 +1154,67 @@ begin end -# ╔═╡ 82e2eca9-4991-4538-8e1f-455cd46f849f -begin - # Generic version - function landscape_plot(; xlim=(-4,4), ylim=(-4,4), nsamp=120, show_colorbar=false) - xs = range(xlim[1], xlim[2], length=nsamp) - ys = range(ylim[1], ylim[2], length=nsamp) - Z = [ f([x,y]) for x in xs, y in ys ] # size(Z) = (length(xs), length(ys)) - - p = plt.contour(xs, ys, Z; - levels=18, - colorbar=show_colorbar, - xlabel="x₁", ylabel="x₂", - title="Objective contours & constraint", - legend=:topleft, - aspect_ratio=:equal, - xlims=(xlim[1], xlim[2]), # hard-lock axes - ylims=(ylim[1], ylim[2])) - - xc = collect(xs) - yc = @. xc^2 + 2xc - plt.plot!(p, xc, yc; lw=2, label="c(x)=0") - plt.scatter!(p, [xstar[1]], [xstar[2]]; marker=:star5, ms=8, label="x⋆") - - # re-enforce limits after adding series (paranoid, but safe) - plt.xlims!(p, (xlim[1], xlim[2])) - plt.ylims!(p, (ylim[1], ylim[2])) - return p - end - - plot_path!(p, X) = begin - plt.plot!(p, X[1,:], X[2,:]; marker=:x, ms=6, lw=1.5, label="iterates") - plt.xlims!(p, (-4,4)); plt.ylims!(p, (-4,4)) # keep locked if path goes outside - p - end -end - - -# ╔═╡ db230e89-e8ea-4c57-8517-0dbf68cab6b7 -md""" -## Visualizing the objective and the constraint - -We plot contour lines of $f(x)$ and the line $c(x)=0$ (the feasible region is on or above that line, i.e., $c(x)\ge 0$). -""" - -# ╔═╡ d9c51abd-2488-405f-9b4c-799f496c0dd1 -begin - function plot_landscape(; xlim=(-4.0, 4.0), ylim=(-4.0, 4.0)) - xs = range(xlim[1], xlim[2], length=200) - ys = range(ylim[1], ylim[2], length=200) - Z = [f([xi, yi]) for yi in ys, xi in xs] - - p = plt.contour( - xs, ys, Z; - fill=false, - xlabel="x₁", ylabel="x₂", - title="Objective & constraint", - aspect_ratio=1, # square figure; keep if you like - xlims=xlim, ylims=ylim # << fix the visible ranges - ) - - plt.plot!(p, xs, xs .+ 1; label="c(x)=0 (x₂ = x₁ + 1)") - return p - end - - fig_test = plot_landscape() # will be limited to [-4,4] × [-4,4] -end - - -# ╔═╡ af7cf190-fa37-40f7-ac9e-dbdb8e72fff8 -md""" -## Log-domain IP residuals - -With $z=[x;\sigma]$ and parameter $\rho>0$, define -$\lambda(z,\rho)=\sqrt{\rho}\,e^{-\sigma}$ and $s(z,\rho)=\sqrt{\rho}\,e^{\sigma}$. - -Residuals: -- Stationarity (2 numbers): $\nabla f(x) - \lambda A = 0$. -- Primal feasibility (1 number): $c(x) - s = 0$. - -So $r(z;\rho) = \begin{bmatrix} \nabla f(x) - \lambda A \\ c(x) - s \end{bmatrix} \in \mathbb{R}^3$. -""" - -# ╔═╡ 6c5b33c5-94fc-4b41-8128-696a58ca9b64 -begin - function ip_residual(z::AbstractVector, ρ::Real) - x = z[1:2] - σ = z[3] - λ = sqrt(ρ) * exp(-σ) # ≥ 0 - s = sqrt(ρ) * exp(σ) # ≥ 0 - r1 = ∇f(x) .- (λ .* A) # length-2 - r2 = c(x) - s # scalar - return vcat(r1, r2) # length-3 - end -end - -# ╔═╡ 64d20ba5-d195-41ef-88b8-d2968bf87ac2 +# ╔═╡ 8e66acef-684d-4536-ae9b-49ce6e8dc24b md""" -## Newton solve for fixed $\rho$ +##### How to read the figure -We solve $r(z; \rho)=0$ with damped Newton on the merit $\phi(z)=\tfrac{1}{2}\lVert r(z)\rVert^2$. +- Pick a start \(x_1,x_2\) and a barrier strength $\mu=10^k$ with the sliders. +- Click **Run IPM**: the code takes a handful of Newton steps on $F_\mu$. +- Large $\mu$: iterates stay well inside; Small $\mu$: iterates **hug the boundary** + and move toward the constrained minimum. -Directional derivative of $\phi$ along $\Delta z$ is $r(z)^\top J(z)\,\Delta z$ where $J$ is the Jacobian of $r$. +**Sanity checks as you play:** +- Check $c(x)$ stays **positive** along the path (interior). +- Check $F_\mu$ is **monotone decreasing** across accepted steps. +- As you reduce $\mu$, the final point gets closer to the boundary optimum. """ -# ╔═╡ 4f9c5ddd-05f5-4826-a775-bc1af4555153 -begin - function newton_solve(z0::AbstractVector, ρ; tol=1e-10, maxiter=50, α_min=1e-12) - z = copy(z0) - R = ip_residual(z, ρ) - Zhist = reshape(z, :, 1) - - for k in 1:maxiter - if norm(R) ≤ tol - return z, Zhist - end - Jz = ForwardDiff.jacobian(zz -> ip_residual(zz, ρ), z) # 3×3 - Δz = -Jz \ R - - ϕz = 0.5 * dot(R, R) - gΔ = dot(R, Jz * Δz) # directional derivative of ϕ at z along Δz - α = 1.0 - z_new = z .+ α .* Δz - R_new = ip_residual(z_new, ρ) - ϕnew = 0.5 * dot(R_new, R_new) - c_armijo, shrink = 1e-4, 0.5 - - if gΔ > 0 - # fallback: just ensure decrease in ϕ - while ϕnew > ϕz && α > α_min - α *= shrink - z_new = z .+ α .* Δz - R_new = ip_residual(z_new, ρ) - ϕnew = 0.5 * dot(R_new, R_new) - end - else - # standard Armijo - while ϕnew > ϕz + c_armijo*α*gΔ && α > α_min - α *= shrink - z_new = z .+ α .* Δz - R_new = ip_residual(z_new, ρ) - ϕnew = 0.5 * dot(R_new, R_new) - end - end - - z, R = z_new, R_new - Zhist = hcat(Zhist, z) - end - return z, Zhist - end -end - -# ╔═╡ 1fb54858-898e-4d0d-8435-746bf3c17e65 +# ╔═╡ 2ff89961-af3e-4792-8f71-3f2a2ca53056 md""" -#### KKT sanity checks to report: -* stationarity norm $\lVert\nabla f(x) - \lambda A\rVert$ -* primal feasibility $c(x)$ -* dual feasibility $\lambda$ -* and complementarity slackness $\lambda\,c(x)$ - -These should approach zero feasibility violation and complementarity as $\rho \to 0$. -""" +### IPM controls +Start x₁ $(@bind x1 PlutoUI.Slider(-3.0:0.1:2.0, default=-2.0, show_value=true)) +Start x₂ $(@bind x2 PlutoUI.Slider(-2.0:0.1:4.0, default= 2.0, show_value=true)) -# ╔═╡ 2070905c-d96b-4e3d-8a51-f0bf26ba98b8 -begin - function kkt_checks(z, ρ) - x, σ = z[1:2], z[3] - λ = sqrt(ρ) * exp(-σ) - (; stationarity_norm = norm(∇f(x) .- (λ .* A)), - primal_feas = c(x), # should be ≥ 0 - dual_feas = λ, # should be ≥ 0 - complementarity = λ * c(x)) - end -end +Barrier exponent \(k\) (we use \(\mu=10^k\)) +k = $(@bind k PlutoUI.Slider(-6:0.05:0, default=-3, show_value=true)) +Steps $(@bind steps PlutoUI.Select([4,6,8,10,15,20,25,50,100]; default=8)) -# ╔═╡ 08270f19-7c1c-4707-baf8-fa985519baf3 -md""" -## Demo: follow the central path by decreasing $\rho$ - -We warm-start Newton at a smaller $\rho$ and plot the iterates on the $(x_1,x_2)$ plane. +$(@bind run_p4 PlutoUI.Button("Run IPM")) """ -# ╔═╡ a4c50b10-0a6f-4967-9895-e5171f44fafa -begin - # Initial guess (must be feasible or strictly interior for the barrier) - x0_init = [-2.0, 2.0] - σ0_init = 0.0 - z0_init = vcat(x0_init, σ0_init) # [x; σ] ∈ ℝ^3 - - # Central-path parameters - ρ_path1 = 1.0 - ρ_path2 = 1e-8 +# ╔═╡ eb4d1779-2283-44ca-addb-47713e04948d +let + run_p4 + μ = 10.0^k + X = barrier_newton([x1, x2], μ; steps=steps) + ipm_plot(X) end -# ╔═╡ 71530ac2-841c-4616-98e3-2dedd63e3834 -begin - # cold-start both runs from the same z0_init - sol_rho1_cold, traj_rho1_cold = newton_solve(z0_init, ρ_path1; tol=1e-10) - sol_rho2_cold, traj_rho2_cold = newton_solve(z0_init, ρ_path2; tol=1e-10) - - fig_ipm_compare = plot_landscape(xlim=(-4,4), ylim=(-4,4)) - plt.plot!(fig_ipm_compare; legend=:bottomright, grid=true) - - # starting point - plt.scatter!(fig_ipm_compare, [z0_init[1]], [z0_init[2]]; - label="start", markershape=:star5, markersize=9, markerstrokewidth=0, color=:black) - # path & final for ρ = 1.0 - plt.plot!(fig_ipm_compare, vec(traj_rho1_cold[1, :]), vec(traj_rho1_cold[2, :]); - label="iterates ρ=1.0", lw=2, marker=:circle, markersize=3, color=:blue) +# ╔═╡ e06d3761-77a6-4e84-a3ea-d13b6d0c57dd +md""" - # path & final for ρ = 1e-8 - plt.plot!(fig_ipm_compare, vec(traj_rho2_cold[1, :]), vec(traj_rho2_cold[2, :]); - label="iterates ρ=1e-8", lw=2, marker=:utriangle, markersize=3, color=:red) - +The takeaway from IPM in a nutshell is that IPM consists of repeating the following steps in iterative fashion: - fig_ipm_compare -end +$\textbf{repeat:}\quad +\begin{cases} +\text{(inner) solve } \min_x F_\mu(x)\ \text{by Newton + backtracking},\\[2pt] +\text{(outer) } \mu \leftarrow \tau\,\mu\quad(0<\tau<1)\ \text{and warm-start}. +\end{cases}$ +At a constrained solution $x^\star$, the KKT conditions read -# ╔═╡ fe6ec86b-84c1-4e82-85e0-dce1a316214a -md""" -### Optional: Jacobian of the residual at the final point - -The Jacobian $J(z)$ of $r(z;\\rho)$ at the final iterate can be inspected via eigenvalues to gauge local conditioning. +$\nabla f(x^\star) - J_c(x^\star)^\top \lambda^\star = 0,\quad +c(x^\star)\ge 0,\quad \lambda^\star\ge 0,\quad +\lambda^{\star\top}c(x^\star)=0$ +Barrier methods implicitly track $\lambda \approx \mu/c(x)$ and drive +$\lambda\circ c(x)\to 0$ as $\mu\downarrow 0$ (complementarity). """ -# ╔═╡ 686b3f99-11df-4cc0-b739-3ff8476d217d - - -# ╔═╡ 8aa33896-2585-4a60-86dc-7386b96feb9f - - # ╔═╡ 3132cda5-ad6e-4ae2-afba-ea18d09cf8df md""" # Part V - Sequential Quadratic Programming (SQP) @@ -1146,62 +1225,63 @@ md""" - Linearize the constraints around the current iterate. - Each iteration: solve a QP to get a step $d$, then update $x \leftarrow x + \alpha d$. - Strengths: Newton-like local convergence (often superlinear), warm-start friendly. -""" +##### Problem & KKT recap +We consider -# ╔═╡ 74d5c04f-3f63-41ba-818d-ce217cd18022 -md""" -## Problem & KKT recap +```math +\min_{x \in \mathbb{R}^n} f(x) \quad \text{s.t.}\quad g(x)=0,\ \ h(x)\le 0. +``` -We consider -$\min_{x \in \mathbb{R}^n} \ f(x) \quad \text{s.t.}\quad g(x)=0,\ \ h(x)\le 0$. +At a candidate optimum $x^\star$, the KKT conditions require multipliers $\lambda \in \mathbb{R}^m$, $\mu \in \mathbb{R}^p_{\ge 0}$ such that -At a candidate optimum $x^\star$, the KKT conditions require multipliers -$\lambda \in \mathbb{R}^m$, $\mu \in \mathbb{R}^p_{\ge 0}$ such that -$\nabla f(x^\star) + \nabla g(x^\star)^\top \lambda + \nabla h(x^\star)^\top \mu = 0$, -$g(x^\star)=0,\ \ h(x^\star)\le 0,\ \ \mu \ge 0,\ \ \mu \odot h(x^\star)=0$. -""" +```math +\nabla f(x^\star) + \nabla g(x^\star)^\top \lambda + \nabla h(x^\star)^\top \mu = 0 +``` +```math +g(x^\star)=0,\ \ h(x^\star)\le 0,\ \ \mu \ge 0,\ \ \mu \odot h(x^\star)=0 +``` -# ╔═╡ 4b560ea8-8104-40ce-bf44-b5c2267132eb -md""" -## Local models that define the QP +##### Local models that define the QP At iterate $x_k$ with multipliers $(\lambda_k,\mu_k)$: **Quadratic model** -$m_k(d) \;=\; \nabla f(x_k)^\top d \;+\; \tfrac{1}{2}\, d^\top B_k\, d,$ + +```math +m_k(d) = \nabla f(x_k)^\top d + \tfrac{1}{2}\, d^\top B_k\, d +``` + with $B_k \approx \nabla^2_{xx}\mathcal{L}(x_k,\lambda_k,\mu_k)$. Common choices: exact Hessian, (L-)BFGS, or Gauss–Newton. **Linearized constraints** -$ g(x_k) + \nabla g(x_k)\, d = 0, \qquad h(x_k) + \nabla h(x_k)\, d \le 0.$ -""" -# ╔═╡ 96738f09-e894-499e-9e30-15692e2434dd -md""" -## The SQP subproblem (QP) +```math +\begin{aligned} +g(x_k) + \nabla g(x_k)\, d &= 0,\\ +h(x_k) + \nabla h(x_k)\, d &\le 0. +\end{aligned} +``` + +##### The SQP subproblem (QP) -$ +```math \begin{aligned} \min_{d \in \mathbb{R}^n}\quad & -\nabla f(x_k)^\top d \;+\; \tfrac{1}{2}\, d^\top B_k\, d \\ +\nabla f(x_k)^\top d + \tfrac{1}{2}\, d^\top B_k\, d \\ \text{s.t.}\quad & \nabla g(x_k)\, d + g(x_k) = 0, \\ & \nabla h(x_k)\, d + h(x_k) \le 0. \end{aligned} -$ - -Solving this QP yields a step $d_k$ and QP multipliers $(\lambda_{k+1},\mu_{k+1})$. -Update with $x_{k+1} = x_k + \alpha_k d_k$ (line search or trust region). -""" +``` +Solving this QP yields a step $(d_k)$ and QP multipliers $(\lambda_{k+1},\mu_{k+1})$. +Update with $(x_{k+1} = x_k + \alpha_k d_k)$ (line search or trust region). - -# ╔═╡ 704f8f36-fda1-445e-9551-942fd85bff42 -md""" -## SQP (line-search flavor): 6 steps +##### SQP (line-search flavor): 6 steps 1. Initialize $x_0$, multipliers $(\lambda_0,\mu_0)$, and $B_0 \succ 0$. 2. Build the QP at $x_k$ using $B_k$, $\nabla g(x_k)$, $\nabla h(x_k)$, $g(x_k)$, $h(x_k)$. @@ -1209,13 +1289,8 @@ md""" 4. Choose $\alpha_k \in (0,1]$ via globalization (merit or filter). 5. Set $x_{k+1} = x_k + \alpha_k d_k$. 6. Update $B_{k+1}$ (e.g., damped BFGS). Stop when KKT residuals are small. -""" - - -# ╔═╡ b6acef39-d40d-42e6-b037-10713dc1254e -md""" -### Globalization: make SQP robust +##### Globalization: make SQP robust **Merit (penalty) function** for line search, e.g. $\phi(x) \;=\; f(x) \;+\; \tfrac{\rho}{2}\,\|g(x)\|^2 \;+\; \rho\,\|h(x)_+\|_1,$ @@ -1225,109 +1300,64 @@ and choose $\alpha_k$ by Armijo/backtracking so $\phi$ decreases. **Trust-region SQP**: restrict $\|d\|\le \Delta_k$, compare predicted vs actual reduction, adjust $\Delta_k$. -### Inequalities & active sets (intuition) +##### Inequalities & active sets (intuition) - The QP contains the **linearized inequalities** $h(x_k)+\nabla h(x_k)d \le 0$. - Its KKT system enforces complementarity via multiplier signs and active constraints. - A small **working set** (estimated active constraints) tends to stabilize across iterations, enabling warm starts and fast solves. - """ - - -# ╔═╡ 34737fc8-095c-4061-8661-e776c23c7eed - - -# ╔═╡ fc35e75c-b81d-44eb-bcee-74c590e38652 - - -# ╔═╡ 32587054-655f-4f06-9ff8-500ba44bec76 - - -# ╔═╡ 3dfc4b4e-85e3-4dee-ae33-5ddb2a4eec29 -question_box(md"hello") - -# ╔═╡ e8f62342-88ab-4754-af33-e2347be2daa0 -Foldable(md"Compressed vs. separated form", md" -We can either express the values of $x$ and $u$ at $t_{k + \frac{1}{2}}$ with the expressions above, or we can set them as decision variables, and enforce the expressions above as equality constraints. -Doing the former results in *compressed form* and the latter *separated form*. -The compressed form tends to perform better with a large number of segments and the separated form a small number of segments [^kelly2017]. -") - # ╔═╡ Cell order: # ╟─81ebc291-89f0-4c1e-ac34-d5715977dd86 # ╟─9543f7bc-ab36-46ff-b471-9aa3db9739e4 -# ╠═8969e78a-29b0-46d3-b6ba-59980208fe5b +# ╟─8969e78a-29b0-46d3-b6ba-59980208fe5b # ╟─d90e9be0-7b68-4139-b185-6cbaad0d307e -# ╠═7b896268-4336-47e2-a8b5-f985bfde51f5 +# ╟─7b896268-4336-47e2-a8b5-f985bfde51f5 # ╟─342decc1-43fa-432a-9a9c-757a10ba6a5d # ╟─fd1ad74b-cb74-49a7-80b8-1a282abfdff2 +# ╟─5e6c4ea7-b283-423c-b9c0-421d53cebc2d # ╠═49d5b2e6-eb29-478c-b817-8405d55170b1 -# ╠═950c61b8-f076-4b9a-8970-e5c2841d75f2 +# ╟─950c61b8-f076-4b9a-8970-e5c2841d75f2 +# ╟─80035b9c-eba6-469c-b138-c6c792979493 # ╠═92841a2e-bc0d-40f8-8344-a5c398a67275 -# ╠═8813982c-8c9a-4706-91a8-ebadf9323a4f +# ╟─8813982c-8c9a-4706-91a8-ebadf9323a4f # ╠═4307a2f3-0378-4282-815b-9ed1fa532e1c -# ╠═a45ed97f-f7c1-4ef5-9bc7-654e827f751b +# ╟─a45ed97f-f7c1-4ef5-9bc7-654e827f751b # ╠═5a17f83e-751b-4244-9c15-7165645bfe29 -# ╟─12c03202-58b2-482e-a65c-b83bc1f6eed1 -# ╠═662d58d7-4c9c-4699-a846-cb6070c507d9 +# ╟─56c965c9-5acc-40a5-b1dd-c3a59f0462a9 +# ╟─af82d16d-c649-461b-856a-42355517d9f4 +# ╟─662d58d7-4c9c-4699-a846-cb6070c507d9 # ╠═27760607-b740-47dc-a810-c332baa2bd2d -# ╟─7765e032-c520-4f97-b877-0d479f383f28 # ╠═a098a49b-a368-4929-824c-385a06b88696 -# ╠═62d85874-9673-4893-bbff-fa75b4462e96 -# ╠═a62f1b6a-87fe-4401-bc13-42166ca0e129 +# ╟─17ac372e-87e6-4649-ba9d-1df1cdb7b55b +# ╟─a62f1b6a-87fe-4401-bc13-42166ca0e129 +# ╟─be41026f-cb28-4647-8db6-1d243739f444 # ╠═b2ddc048-4942-43bc-8faa-1921062d8c9c +# ╟─1ffa2941-619b-400a-ba0f-56baa6ee7f59 # ╠═6fd7a753-6db7-4c37-9e22-dc63dd3072c8 # ╠═49e0f5c3-fe14-42e1-9b3a-83c4447148a8 # ╠═096ebc95-f133-4ca3-b942-cf735faaa42b -# ╠═fdf41c76-4405-49e0-abfa-5c5193de99f4 +# ╟─fdf41c76-4405-49e0-abfa-5c5193de99f4 # ╠═22bfe0a3-c61b-4dfe-8f20-a8bf807c2e14 -# ╠═d259c1b8-3716-4f80-b462-3b3daebb444d -# ╠═858d4ee1-4f15-4ced-b984-c0291237d359 -# ╠═e012d7e0-0181-49d0-bb78-995378c4f87a -# ╠═0a3eb748-fab3-4f8b-993e-2246c32fb6aa -# ╠═3989b8e1-ac1f-430d-9d72-e298ba7ae0ca -# ╠═e466ffb2-fcc8-4c3b-9059-7fd68a4265f2 -# ╠═09a301c7-c1f4-4890-afe9-fbc1d7c3b905 -# ╠═53020b8a-4948-467a-8629-bef9496d0374 -# ╠═26c887ce-d95b-4e38-9717-e119de0e80ca -# ╟─a1b7f614-710a-4ce8-815b-2f94754088c4 -# ╟─6fd82e73-6fef-4f7f-b291-f94cbac0d268 -# ╠═52718e0b-f958-445e-b9b6-9e5baf09e81a -# ╠═edce5b27-9af8-4010-9d9f-60681b2f427c -# ╠═c139ccc9-0355-4c41-8d82-0c97fef50900 -# ╟─43c5ccb9-d51e-4d73-b7be-f8b9dd599130 -# ╠═e0a7f431-61dd-4df2-b53c-d81c7a302baf -# ╠═82e2eca9-4991-4538-8e1f-455cd46f849f -# ╠═3e3d6e18-f922-4e91-8eaf-bc26b8f91757 -# ╠═31407c3f-8f3d-4dcc-bc49-cedd8ca14013 -# ╠═a61a63b7-716e-4500-9bc6-ab2caf9062e7 -# ╠═1fda2985-ab68-4460-98be-8621e5a5f1c8 -# ╠═ea0f0b1e-65b1-4c66-b9fa-7f2c428e3459 -# ╠═b4f6cce9-c39e-4325-a0e3-ab9c38179894 -# ╠═252554be-a839-4770-b222-fbb6a32df2ef -# ╠═db230e89-e8ea-4c57-8517-0dbf68cab6b7 -# ╠═d9c51abd-2488-405f-9b4c-799f496c0dd1 -# ╠═af7cf190-fa37-40f7-ac9e-dbdb8e72fff8 -# ╠═6c5b33c5-94fc-4b41-8128-696a58ca9b64 -# ╠═64d20ba5-d195-41ef-88b8-d2968bf87ac2 -# ╠═4f9c5ddd-05f5-4826-a775-bc1af4555153 -# ╠═1fb54858-898e-4d0d-8435-746bf3c17e65 -# ╠═2070905c-d96b-4e3d-8a51-f0bf26ba98b8 -# ╠═08270f19-7c1c-4707-baf8-fa985519baf3 -# ╠═a4c50b10-0a6f-4967-9895-e5171f44fafa -# ╠═71530ac2-841c-4616-98e3-2dedd63e3834 -# ╠═fe6ec86b-84c1-4e82-85e0-dce1a316214a -# ╠═686b3f99-11df-4cc0-b739-3ff8476d217d -# ╠═8aa33896-2585-4a60-86dc-7386b96feb9f -# ╠═3132cda5-ad6e-4ae2-afba-ea18d09cf8df -# ╟─74d5c04f-3f63-41ba-818d-ce217cd18022 -# ╟─4b560ea8-8104-40ce-bf44-b5c2267132eb -# ╟─96738f09-e894-499e-9e30-15692e2434dd -# ╟─704f8f36-fda1-445e-9551-942fd85bff42 -# ╟─b6acef39-d40d-42e6-b037-10713dc1254e -# ╠═34737fc8-095c-4061-8661-e776c23c7eed -# ╠═fc35e75c-b81d-44eb-bcee-74c590e38652 -# ╠═32587054-655f-4f06-9ff8-500ba44bec76 -# ╠═3dfc4b4e-85e3-4dee-ae33-5ddb2a4eec29 -# ╠═e8f62342-88ab-4754-af33-e2347be2daa0 +# ╟─d259c1b8-3716-4f80-b462-3b3daebb444d +# ╟─76c51e32-bd0e-4e72-8c47-64352da13d3e +# ╟─858d4ee1-4f15-4ced-b984-c0291237d359 +# ╟─e012d7e0-0181-49d0-bb78-995378c4f87a +# ╟─26c887ce-d95b-4e38-9717-e119de0e80ca +# ╟─52718e0b-f958-445e-b9b6-9e5baf09e81a +# ╟─edce5b27-9af8-4010-9d9f-60681b2f427c +# ╟─4a3bdacd-bc17-4b10-bbed-6d34f0531d60 +# ╠═20fa9f12-ef19-482a-be83-feaed95109c3 +# ╠═6c560afb-921d-4f34-b77b-a31e37b7d571 +# ╠═4d8533ef-be1b-45c9-acaf-ce278c3e5db7 +# ╟─79604603-df5e-47de-aeb8-08e7658fe190 +# ╠═c249309e-a47c-48d9-9b06-d448e0a57a28 +# ╟─917ed8f6-d451-4725-a794-37b6bc7e46f5 +# ╟─c0a75eef-e3e1-4e5f-8184-292827870cb2 +# ╟─60cc80f1-818b-45d4-8248-bf2e0bfb6936 +# ╠═8a5029b2-2355-4b9b-84d5-50970c6a4b44 +# ╟─8e66acef-684d-4536-ae9b-49ce6e8dc24b +# ╟─2ff89961-af3e-4792-8f71-3f2a2ca53056 +# ╠═eb4d1779-2283-44ca-addb-47713e04948d +# ╟─e06d3761-77a6-4e84-a3ea-d13b6d0c57dd +# ╟─3132cda5-ad6e-4ae2-afba-ea18d09cf8df From cb88e1e55919b911bf0c97c5571eccf04da3cff6 Mon Sep 17 00:00:00 2001 From: arnauddeza Date: Sun, 16 Nov 2025 14:16:14 -0500 Subject: [PATCH 3/3] Sync class02 folder: add class02.html and remove obsolete files --- class02/SQP.tex | 123 -------- class02/background_materials/README.md | 1 - class02/class02.html | 19 ++ class02/class02.md | 55 ---- class02/eq_constraints.tex | 205 ------------ class02/figures/log_barrier.png | Bin 32399 -> 0 bytes class02/figures/quadratic_penalty.png | Bin 61491 -> 0 bytes class02/figures/tri_paper.png | Bin 74894 -> 0 bytes class02/ineq_constraints.tex | 419 ------------------------- class02/intro.tex | 63 ---- class02/log_barrier.png | Bin 32399 -> 0 bytes class02/main.tex | 82 ----- class02/notation.tex | 117 ------- class02/overview.md | 22 +- class02/penalty_barrier_demo.py | 38 --- class02/quadratic_penalty.png | Bin 61491 -> 0 bytes class02/root_finding.tex | 251 --------------- 17 files changed, 22 insertions(+), 1373 deletions(-) delete mode 100644 class02/SQP.tex delete mode 100644 class02/background_materials/README.md create mode 100644 class02/class02.html delete mode 100644 class02/class02.md delete mode 100644 class02/eq_constraints.tex delete mode 100644 class02/figures/log_barrier.png delete mode 100644 class02/figures/quadratic_penalty.png delete mode 100644 class02/figures/tri_paper.png delete mode 100644 class02/ineq_constraints.tex delete mode 100644 class02/intro.tex delete mode 100644 class02/log_barrier.png delete mode 100644 class02/main.tex delete mode 100644 class02/notation.tex delete mode 100644 class02/penalty_barrier_demo.py delete mode 100644 class02/quadratic_penalty.png delete mode 100644 class02/root_finding.tex diff --git a/class02/SQP.tex b/class02/SQP.tex deleted file mode 100644 index 5e65cdb..0000000 --- a/class02/SQP.tex +++ /dev/null @@ -1,123 +0,0 @@ -\section{Sequential Quadratic Programming (SQP)} - -% ------------------------------------------------ -\begin{frame}{What is SQP?} -\textbf{Idea:} Solve a nonlinear, constrained problem by repeatedly solving a \emph{quadratic program (QP)} built from local models.\\[4pt] -\begin{itemize} - \item Linearize constraints; quadratic model of the Lagrangian/objective. - \item Each iteration: solve a QP to get a step \(d\), update \(x \leftarrow x + \alpha d\). - \item Strength: strong local convergence (often superlinear) with good Hessian info. -\end{itemize} -\end{frame} - -% ------------------------------------------------ -\begin{frame}{Target Problem (NLP)} -\[ -\min_{x \in \R^n} \ f(x) -\quad -\text{s.t.}\quad -g(x)=0,\quad h(x)\le 0 -\] -\begin{itemize} - \item \(f:\R^n\!\to\!\R\), \(g:\R^n\!\to\!\R^{m}\) (equalities), \(h:\R^n\!\to\!\R^{p}\) (inequalities). - \item KKT recap (at candidate optimum \(x^\star\)): -\[ -\exists \ \lambda \in \R^{m},\ \mu \in \R^{p}_{\ge 0}: -\ \grad f(x^\star) + \nabla g(x^\star)^T\lambda + \nabla h(x^\star)^T \mu = 0, -\] -\[ -g(x^\star)=0,\quad h(x^\star)\le 0,\quad \mu \ge 0,\quad \mu \odot h(x^\star) = 0. -\] -\end{itemize} -\end{frame} - -% ------------------------------------------------ -\begin{frame}{From NLP to a QP (Local Model)} -At iterate \(x_k\) with multipliers \((\lambda_k,\mu_k)\):\\[4pt] -\textbf{Quadratic model of the Lagrangian} -\[ -m_k(d) = \ip{\grad f(x_k)}{d} + \tfrac{1}{2} d^T B_k d -\] -with \(B_k \approx \nabla^2_{xx}\Lag(x_k,\lambda_k,\mu_k)\).\\[6pt] -\textbf{Linearized constraints} -\[ -g(x_k) + \nabla g(x_k)\, d = 0,\qquad -h(x_k) + \nabla h(x_k)\, d \le 0. -\] -\end{frame} - -% ------------------------------------------------ -\begin{frame}{The SQP Subproblem (QP)} -\[ -\begin{aligned} -\min_{d \in \R^n}\quad & \grad f(x_k)^T d + \tfrac{1}{2} d^T B_k d \\ -\text{s.t.}\quad & \nabla g(x_k)\, d + g(x_k) = 0, \\ -& \nabla h(x_k)\, d + h(x_k) \le 0. -\end{aligned} -\] -\begin{itemize} - \item Solve QP \(\Rightarrow\) step \(d_k\) and updated multipliers \((\lambda_{k+1},\mu_{k+1})\). - \item Update \(x_{k+1} = x_k + \alpha_k d_k\) (line search or trust-region). -\end{itemize} -\end{frame} - -% ------------------------------------------------ -\begin{frame}{Algorithm Sketch (SQP)} -\begin{enumerate} - \item Start with \(x_0\), multipliers \((\lambda_0,\mu_0)\), and \(B_0 \succ 0\). - \item Build QP at \(x_k\) with \(B_k\), linearized constraints. - \item Solve QP \(\Rightarrow\) get \(d_k\), \((\lambda_{k+1},\mu_{k+1})\). - \item Globalize: line search on merit or use filter/TR to choose \(\alpha_k\). - \item Update \(x_{k+1} = x_k + \alpha_k d_k\), update \(B_{k+1}\) (e.g., BFGS). -\end{enumerate} -\end{frame} - -% ------------------------------------------------ -\begin{frame}{Toy Example (Local Models)} -\textbf{Problem:} -\[ -\min_{x\in\R^2} \ \tfrac{1}{2}\norm{x}^2 -\quad \text{s.t.} \quad g(x)=x_1^2 + x_2 - 1 = 0,\ \ h(x)=x_2 - 0.2 \le 0. -\] -At \(x_k\), build QP with -\[ -\grad f(x_k)=x_k,\quad B_k=I,\quad -\nabla g(x_k) = \begin{bmatrix} 2x_{k,1} & 1 \end{bmatrix},\ -\nabla h(x_k) = \begin{bmatrix} 0 & 1 \end{bmatrix}. -\] -Solve for \(d_k\), then \(x_{k+1}=x_k+\alpha_k d_k\). -\end{frame} - - -% ------------------------------------------------ -\begin{frame}{Globalization: Making SQP Robust} -SQP is an important method, and there are many issues to be considered to obtain an \textbf{efficient} and \textbf{reliable} implementation: -\begin{itemize} - \item Efficient solution of the linear systems at each Newton Iteration (Matrix block structure can be exploited. - \item Quasi-Newton approximations to the Hessian. - \item Trust region, line search, etc. to improve robustnes (i.e TR: restrict \(\norm{d}\) to maintain model validity.) - \item Treatment of constraints (equality and inequality) during the iterative process. - \item Selection of good starting guess for $\lambda$. -\end{itemize} -\end{frame} - - - - - - -% ------------------------------------------------ -\begin{frame}{Final Takeaways on SQP} -\textbf{When SQP vs.\ Interior-Point?} -\begin{itemize} - \item \textbf{SQP}: strong local convergence; warm-start friendly; natural for NMPC. - \item \textbf{IPM}: very robust for large, strictly feasible problems; good for dense inequality sets. - \item In practice: both are valuable—choose to match problem structure and runtime needs. -\end{itemize} -\textbf{Takeaways of SQP} -\begin{itemize} - \item SQP = Newton-like method using a sequence of structured QPs. - \item Globalization (merit/filter/TR) makes it reliable from poor starts. - \item Excellent fit for control (NMPC/trajectory optimization) due to sparsity and warm starts. -\end{itemize} -\end{frame} diff --git a/class02/background_materials/README.md b/class02/background_materials/README.md deleted file mode 100644 index 1bc7820..0000000 --- a/class02/background_materials/README.md +++ /dev/null @@ -1 +0,0 @@ -# Class 2 Background Material - 08/29/2025 diff --git a/class02/class02.html b/class02/class02.html new file mode 100644 index 0000000..df21819 --- /dev/null +++ b/class02/class02.html @@ -0,0 +1,19 @@ + + + + + + + +
\ No newline at end of file diff --git a/class02/class02.md b/class02/class02.md deleted file mode 100644 index 68bc9ec..0000000 --- a/class02/class02.md +++ /dev/null @@ -1,55 +0,0 @@ -# Class 2 — 08/29/2025 - -**Presenter:** Arnaud Deza - -**Topic:** Numerical optimization for control (gradient/SQP/QP); ALM vs. interior-point vs. penalty methods - ---- - -## Overview - -This class covers the fundamental numerical optimization techniques essential for optimal control problems. We explore gradient-based methods, Sequential Quadratic Programming (SQP), and various approaches to handling constraints including Augmented Lagrangian Methods (ALM), interior-point methods, and penalty methods. - -## Interactive Materials - -The class is structured around 1 slide deck and four interactive Jupyter notebooks: - -1. **[Part 1a: Root Finding & Backward Euler](https://learningtooptimize.github.io/LearningToControlClass/dev/class02/part1_root_finding.html)** - - Root-finding algorithms for implicit integration - - Fixed-point iteration vs. Newton's method - - Application to pendulum dynamics - - -2. **[Part 1b: Minimization via Newton's Method](https://learningtooptimize.github.io/LearningToControlClass/dev/class02/part1_minimization.html)** - - Unconstrained optimization fundamentals - - Newton's method implementation - - Globalization strategies: Hessian matrix and regularization - -3. **[Part 2: Equality Constraints](https://learningtooptimize.github.io/LearningToControlClass/dev/class02/part2_eq_constraints.html)** - - Lagrange multiplier theory - - KKT conditions for equality constraints - - Quadratic programming implementation - -4. **[Part 3: Interior-Point Methods](https://learningtooptimize.github.io/LearningToControlClass/dev/class02/part3_ipm.html)** - - Inequality constraint handling - - Barrier methods and log-barrier functions - - Comparison with penalty methods - -## Additional Resources - -- **[Lecture Slides (PDF)](https://learningtooptimize.github.io/LearningToControlClass/dev/class02/ISYE_8803___Lecture_2___Slides.pdf)** - Complete slide deck -- **[LaTeX Source](https://learningtooptimize.github.io/LearningToControlClass/dev/class02/main.tex)** - Source code for lecture slides - -## Key Learning Outcomes - -- Understand gradient-based optimization methods -- Implement Newton's method for minimization -- Apply root-finding techniques for implicit integration -- Solve equality-constrained optimization problems -- Compare different constraint handling methods -- Implement Sequential Quadratic Programming (SQP) - -## Next Steps - -This class provides the foundation for advanced topics in subsequent classes, including Pontryagin's Maximum Principle, nonlinear trajectory optimization, and stochastic optimal control. - diff --git a/class02/eq_constraints.tex b/class02/eq_constraints.tex deleted file mode 100644 index a09a8bc..0000000 --- a/class02/eq_constraints.tex +++ /dev/null @@ -1,205 +0,0 @@ - -%\section{Part II -- Equality constraints: KKT, Newton vs. Gauss–Newton} -\section{Constrained Optimization} - -% ==== Equality constraints: KKT, Newton vs. Gauss–Newton ==== - -\begin{frame}{Equality-constrained minimization: geometry and conditions} -\textbf{Problem}; $\min_{x\in\mathbb{R}^n} f(x)\quad \text{s.t.}\quad C(x)=0, C:\mathbb{R}^n\to\mathbb{R}^m$. - -\medskip -\textbf{Geometric picture.} At an optimum on the manifold $C(x)=0$, the negative gradient must lie in the tangent space: - -$$ -\grad f(x^\star)\ \perp\ \mathcal{T}_{x^\star}=\{p:\; J_C(x^\star)p=0\}. -$$ - -Equivalently, the gradient is a linear combination of constraint normals: - -$$ -\grad f(x^\star)+J_C(x^\star)^{\!T}\lambda^\star=0,\qquad C(x^\star)=0\quad(\lambda^\star\in\mathbb{R}^m). -$$ - -\medskip -\textbf{Lagrangian.}; $L(x,\lambda)=f(x)+\lambda^{\!T}C(x)$. -\end{frame} - -\begin{frame}{A nicer visual explanation/derivation of KKT conditions} -\begin{center} - Quick little whiteboard derivation -\end{center} - -\end{frame} - - - -\section{Constrained Optimization} - -% ==== Slide 1: Picture-first intuition ==== -\begin{frame}[t]{Equality constraints: picture first} -\setbeamercovered{invisible} - -\textbf{Goal.} Minimize $f(x)$ while staying on the surface $C(x)=0$. - -\uncover<2->{\textbf{Feasible set as a surface.} Think of $C(x)=0$ as a smooth surface embedded in $\mathbb{R}^n$ (a manifold).} - -\uncover<3->{\textbf{Move without breaking the constraint.} Tangent directions are the “along-the-surface” moves that keep $C(x)$ unchanged to first order. Intuitively: tiny steps that slide on the surface.} - -\uncover<4->{\textbf{What must be true at the best point.} At $x^\star$, there is no downhill direction that stays on the surface. Equivalently, the usual gradient of $f$ has \emph{no component along the surface}.} - -\uncover<5->{\textbf{Normals enter the story.} If the gradient can’t point along the surface, it must point \emph{through} it—i.e., it aligns with a combination of the surface’s normal directions (one normal per constraint).} -\end{frame} - -% ==== Slide 2: From picture to KKT ==== -\begin{frame}[t]{From the picture to KKT (equality case)} -\setbeamercovered{invisible} - -\textbf{KKT conditions at a regular local minimum (equality only):} - -\uncover<1->{\textbf{1) Feasibility:} $C(x^\star)=0$. \emph{(We’re on the surface.)}} - -\uncover<2->{\textbf{2) Stationarity:} $\nabla f(x^\star) + J_C(x^\star)^{\!T}\lambda^\star = 0$. \emph{(The gradient is a linear combination of the constraint normals.)}} - -\uncover<3->{\textbf{Lagrangian viewpoint.} Define $L(x,\lambda)=f(x)+\lambda^{\!T}C(x)$. At a solution, $x^\star$ is a stationary point of $L$ w.r.t.\ $x$ (that’s the stationarity equation), while $C(x^\star)=0$ enforces feasibility.} - -\uncover<4->{\textbf{What the multipliers mean.} The vector $\lambda^\star$ tells how strongly each constraint “pushes back” at the optimum; it also measures sensitivity of the optimal value to small changes in the constraints.} - -\end{frame} - - -\begin{frame}{KKT system for equalities (first-order necessary conditions)} -\textbf{KKT (FOC).} - -$$ -\grad_x L(x,\lambda)=\grad f(x)+J_C(x)^{\!T}\lambda=0,\qquad \grad_\lambda L(x,\lambda)=C(x)=0. -$$ - -\textbf{Solve by Newton on KKT:} linearize both optimality and feasibility: - -$$ -\begin{bmatrix} -\hess f(x) + \sum_{i=1}^m \lambda_i\,\hess C_i(x) & J_C(x)^{\!T}\\[2pt] -J_C(x) & 0 -\end{bmatrix} -\begin{bmatrix}\Delta x\\ \Delta\lambda\end{bmatrix} -=- -\begin{bmatrix} -\grad f(x)+J_C(x)^{\!T}\lambda\\ C(x) -\end{bmatrix}. -$$ - -\textit{Notes.} This is a symmetric \emph{saddle-point} system; typical solves use block elimination (Schur complement) or sparse factorizations. -\end{frame} - - - - - - -\begin{frame}{Move to Julia Code} -\begin{center} - \textbf{Quick Demo of Julia Notebook: part2\_eq\_constraints.ipynb} -\end{center} -\end{frame} - -\begin{frame}{Numerical practice: Newton on KKT} - \setbeamercovered{invisible} - - - \textbf{When it works best.} - \begin{itemize} - \item Near a regular solution with $J_{C}(x^\star)$ full row rank and positive-definite reduced Hessian. - \item With a globalization (line search on a merit function) and mild regularization for robustness. - \end{itemize} - - % --- Part 2: appears on the 2nd click only --- - \uncover<2->{% - \textbf{Common safeguards.} - \begin{itemize} - \item \emph{Regularize} the $(1,1)$ block to ensure a good search direction (e.g., add $\beta I$). - \item \emph{Merit/penalty} line search to balance feasibility vs.\ optimality during updates. - \item \emph{Scaling} constraints to improve conditioning of the KKT system. - \end{itemize} - } -\end{frame} - - -\begin{frame}{Gauss--Newton vs. full Newton on KKT} - -\uncover<1->{ -\textbf{Full Newton Hessian of the Lagrangian:}\quad -$\nabla_{xx}^2 L(x,\lambda) = \nabla^2 f(x) + \sum_{i=1}^m \lambda_i\, \nabla^2 C_i(x)$ -} - -\vspace{0.6em} - -\uncover<2->{ -\textbf{Gauss--Newton approximation:} drop the \emph{constraint-curvature} term -$\sum_{i=1}^m \lambda_i\, \nabla^2 C_i(x)$: -\begin{align*} -H_{\text{GN}}(x) &\approx \nabla^2 f(x). -\end{align*} -} - -\uncover<3->{ -\textbf{Trade-offs (high level).} -\begin{itemize} - \item \emph{Full Newton:} fewer iterations near the solution, but each step is costlier and can be less robust far from it. - \item \emph{Gauss--Newton:} cheaper per step and often more stable; may need more iterations but wins in wall-clock on many problems. -\end{itemize} -} - -\end{frame} - - -% ==== Inequalities & KKT: complementarity ==== - -\begin{frame}{Inequality-constrained minimization and KKT} -\textbf{Problem.} $\quad \quad \min f(x)\quad\text{s.t.}\quad c(x)\ge 0, \quad \quad c:\mathbb{R}^n\to\mathbb{R}^p$. - -\textbf{KKT conditions (first-order).} - -$$ -\begin{aligned} -&\text{Stationarity:} && \grad f(x)-J_c(x)^{\!T}\lambda=0,\\ -&\text{Primal feasibility:} && c(x)\ge 0,\\ -&\text{Dual feasibility:} && \lambda\ge 0,\\ -&\text{Complementarity:} && \lambda^{\!T}c(x)=0\quad(\text{i.e., }\lambda_i c_i(x)=0\ \forall i). -\end{aligned} -$$ - -\textbf{Interpretation.} -\begin{itemize} -\item \emph{Active} constraints: $c_i(x)=0 \Rightarrow \lambda_i\ge 0$ can be nonzero (acts like an equality). -\item \emph{Inactive} constraints: $c_i(x)>0 \Rightarrow \lambda_i=0$ (no influence on optimality). -\end{itemize} -\end{frame} - - - - -\begin{frame}{Complementarity in plain English (and why Newton is tricky)} -\footnotesize - -\textbf{What $\lambda_i c_i(x)=0$ means.} -\begin{itemize} -\item Tight constraint ($c_i=0$) $\Rightarrow$ can press back ($\lambda_i\ge0$). -\item Loose constraint ($c_i>0$) $\Rightarrow$ no force ($\lambda_i=0$). -\end{itemize} - -\textbf{Why naive Newton fails.} -\begin{itemize} -\item Complementarity = nonsmooth + inequalities ($\lambda\ge0$, $c(x)\ge0$). -\item Equality-style Newton can violate nonnegativity or bounce across boundary. -\end{itemize} - -\textbf{Two main strategies (preview).} -\begin{itemize} -\item \emph{Active-set:} guess actives $\Rightarrow$ solve equality-constrained subproblem, update set. -\item \emph{Barrier/PDIP/ALM:} smooth or relax complementarity, damped Newton, drive relaxation $\to 0$. -\end{itemize} -\end{frame} - - - - \ No newline at end of file diff --git a/class02/figures/log_barrier.png b/class02/figures/log_barrier.png deleted file mode 100644 index 6ca0171f6f4c3acb9adea9d0f77ecf282d47ae32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32399 zcmbrm1yogQ7d5;ODUAXW5-JuTrF07_ic$gs(jp+;or;2pfPtd41t<+lr+|n^DBTUx zorn0>#`pRn-uD~-_`W*^_g>|ky`TNWT64}d*A7rpkR>B#AVv^`?Ba#9R}cip2SG5s zdkElf_`i1!!heJv&S^NPSQ|Sy8`v2k@&*nz7S;|Hrq>TT8QIyJT3emw66E4PdGMx# zgN?liH@D^Q4{%xAnQ+fWM{B}Gh-@xs+9L>s0s4bUkc>A)5YP3CXV0j*L{4@&dr%u~ z63*1sT4js|Jwy(D@VOHgOMMRK_8zA~(Kw{k&xlE*2wr{kHP8U#79lQMV z_SMz%_AwQ{6cT$PYW!ZkdSzJTRpNum8GE0Ymv)zW&-!G}tKoFyja7rvMAEg7Ut8o4 zo!MMW^*+SSO(EFrVwkVzUd@@UKiU3qW!ihI1D|u;i)r=by^M?u9@8I3?%cimQax2S zXLUHj_GkS|r{a)eCiO-!czOwkp{MGv^70rqu*=JiDjvh5@iCv@hUwn_`SYh!aTfZe z-rFQI-%i&#oT<*!la(MLwlHr_Dus^)zkkmzhMmaF84fp}`Fy~-H@kSlGBA~%M_(#) zeB;-6kBl9TGkp~o7uPnecJ(R_GT|gDEiJ8|*(PEXK{jnp)7I9ek?+LGRkU(FwP&C| zko)tGx0ljAu}caTn#Tct-bUF6~wYF-U)GeTE(Q#&C_FPadSom@2xu7-ksZ*!g zH@r6Ow3VwhkjFxBF`@CKOiCJ>yZ7$xiS$|<)hP0CeKL})l})Y^clpfCndS+MU87C17>Dy7DgKgDJ0i=F<8$SPwK-s2u$RSiVek0|beQ;t0N459T>m2Pl|I5wn=-Ns7cNNk6nSDkc#!vI z_jm<<_`r9^-=Fo=seL1n_T&Nz_`v1e+$rbAT}N?X$nyF!YwQ=*r{`G|OVdEqRS)W4LmUG8$u zUfU$YF4u}qyCiw9&sG{_wmpoF?j9SDz@B8_U}ygXkNY4Y;RHGZ-kb9=TI;y4(Mwjp5WwkOpQ5hLe5C; z@2-)amPVkI(G5qHHVc(^hHXI9CJZ(~Y7Jj=l)#d(8z{ziJ&dviS zWTk7`%O9S2d97z_a~Yi}lNit8^pw7N?vp1SxVI*)z2{x(vY6Fx7~D^YD8NqT6Z>4t zwKEE=tE#F}?(6G&?>yZ-_DGglSy5v34TF$Pter@d+3MWT)W~F8rffw;#XDwkw*-G? zw+rI)-%hjh^Q%SKd?F(DX-_wlO|*Q$Z~ly!h)7w+k5pM**Y&O2=-aTy+>z)+Bmr(! zH;@ZwLQSyK#&`P{nROD0pF>zuk%ZLZ&GrFTq%qB)Otmgryh(;a{Ee>Hs?pP^sLAf> zzB2zrHkG&pT0IYC@zud6%rO@SmS8!zR0SgBqhOnl!8)tj4}Vj}B_LITg=4OZfPuis z*^eGQR};>Esc@;?d@N>bJ^qT^J3?j`DU4E&_m*d3jEp&w0Dmso$l=I%A>o-PGFwg+ zVc`zX`_(EtQOmlpsc`DYC2lo1K4#U=IOU@yU1szbBYM{3nl@HPB)8rn2{as6U6Oio z?c;G`qrKKlm%RuG2@}%|E8lys53r1VY|&NmUai(^9ISex<>~!4R0MW|nuEiK_Coh; z7>wh1^Sj<&{l!Yj&DrOvmZOd@fCNXNqBW-T~;FI@U9SWNoiSpiD9eeXc!A`Qaw)QF# z8fVHZIRa-lm_gJj2|HUJK!uJW4`O>0Jmgzu?{!;#$1&B#G78BLd8rU(l;OuT!>c(- z^S$@h=2WGq(}c}5)(3;z7%6Puh%OHtA9ShI3=9ksJ2Z3c-pXCPsO;SB8t=BT*wO?S zN_zbGu`1lJA3=CaX2N3hgn(0H*t$b<03+9j8!1Y zPB{`%Fss_y+FEiUn1l$cysE-lK6%Gl;gJMb635;W%<>u}hme-$rlxm~SY_XvHpICm zS+7vom-&&^OfA2;_@r~|BI8D(-_li8)r9BI=^Q6Nwz!F39y)O4GIgk*8uye=2XB>#b?{*KglcW4t$WVcWoB8OMkkekSF&XidAi9G{eQSvX3; z5j&afp5#6DE*>3kzSHCtn3hiaIyQt^64z2qNhubVppo7r@}-%%dDC-VL)Buh4OLCe zHeS7sA12=)4y}&FOs8-99k7ClZ~tiCH6F}`SmzGy$Cu*N%qTBRdG$(~j8RC5jE+Z& z-tT4Tl`+VMSDo^8o`!{SybX2hyGyIOwYkx>GCR=3f_4SgRoEY)Q z(oJg&9RtJrrHQry-K_>iNf?>;LB9C7xX*C8f5Y3>yY;_9f^?P9N|W8i@HHZ#>Vf{(s&#pYrSu8Z@(JTVdHVb*jQk-+u9qe z)s(27YOLp+gp;%*XWV))49zA-3gG1)pH3qNE)sbV< zf)JxgStJVDEOp!`KSByf&Kk*|E|M`dW#h`9I7LlElUoe=|Jdc{KGVfpMOEf0#hYu# z#+f~4&YXA~&SX4)UfsxkruR(w=g$K9HH0i4l!Nt&6ER|WOIhO(jXCBoCn!eW9q z{-P<}CSZ>`F~3#GjRloRkGZO!EvYQX!DGh=q&DiqOv96lM!vf(j@=E@^P=zZTxR<5 z<42lt4FkjnQRXr`PSeRu#HTIO#9a*C*hp;C%b|q@hX*W@%y2O5QUf6Y#B44cpXkh` zIv{9;hrD_Fwv>nThLI5ld3I9sHY9fV8-uFHM-iXMNXF1-&kl2Pl1XjY?qy_rP!UM` z`puic=h&9Lw7M>b;5c`NREDsG5K$dtwAbhwb&zft+ln|%iVMfcfnrop!prdb^)!<@ z<|OUhj4sOvUPFS->8(u)6pwgqjO(!;If4g)evMae#4cA!MWvy?CC7GP2Da${cDlF{ zVgp`SbyL4PeAD_q>2*yQ1?4K8Hu9>6Lx`&A9jJ7tT+tNe; zWN;cIBP024``(gUoK~qHr}ywD7KLBa( z1f)-I{%l357US2!A|n}RY;1VCT}PNETGMf9XlSPFC$71LWNBZpuR0ob5YQ}S3tVJy zc=-C_SX0j6<4a=E_HdH>!_8C9T)%#pt8n3p2S6uFE&_=1S0e3eg*DQz<4%g(E}8e6=H_OBRb6@cph}kXAg1wo z^Kn?r!5K%~dq=b~ZV=Tg_AYY?LO_g;WgO=hyAL1$Z%Z@MeL?zIsoP@ zjk95)p$8=-HlKGuD4s_FU!4S&l?kUwl2LwOy)d(0lB-Qobj9O)}S5l%x!@#$1mn}N7$nM^~ zd*RY08CYFyE?QW`<0myU@ZP<9hi)mXV|J$<3E)&QkHseS=aSwMQ~A>pVt_U@KHS{< z`2l@{Sw{qi(*eEC26{9ZcRKfYu9UHC`p7c7;z5S0=Ocp)1j?R~v}onAXv@IY^WNY` zLmW(Yj|*@go2`xIftUWwBEwD@nVDVQk;!_+%walCkPPexf&}{^Z&)r0!#R5o^l@^Nj?QYtS_|uUiyy!uJXbksyTeU1;-AyXr@Gvrx z6hV<}mSsotpleOCd4OqnYmsMuehUTSwK_zL$PQPoEf;SEJ$y*{Ek?4(&}9f{kLA2m zJL}P-_{hVD4||Jh+JC>d6k%PQC_|xm`OCeYdFtwd}gMMVqh#W^>aAC zxlIj!i~h#iVomL%Y{N?ONI;o2(>GWFq6z_onnj}qz@7>~0rx+A_;3uHY|5Y@x#55w z9~U@TvnvB31JUkNrOQ3u-ruy1>!O41-zP?AJZC&5e`bBY&RyG}<8iYD2_jG2YuS-i z%2%_0#13~X^CnxS-?ShsWf?%7H2qQ>U&7I{LPM!Ly|9xx z8%rJOj^kG!FpD#w%Q4&M&yv-e(%RZ;98YWd^O1#BPsma=ckze3X$0k8`eTjnTp+iL zH-|lYCctL#sjSQvf=2J-OI&AkbePacIgnn-T&CmPbr+^=f3~uwW5pN>uniE8kxpHSXbCH zE0C?*Kh}ilx>Xe>g?_rkU3dr1>2g|Bmz95w@%+yht-yAG>Lo5s>mT?QDNLoTtn31p zix3|fDY=Df=@yfvfNTHb;&Fquh0&?hXSI40?=2LBhfprWN1tbxtBnc-_3?@<0GYQ+ z+@}lufhY7eYgt>)F+-&OQd3g_53beKLJ+2HM+9IlFuU80#JnYCk#5uS=J*rQMXj6m!lOvS{1DY#?{h9t7ps*F5Tg zgB;+vnq))aio1wk4XpSU2A9BPFzVSezxMWPVq5F;=%nQP^Fm%^@tDED&BrCSD9}6< zep%+p`12Sy?v^P)m!^X#=+L!BC0mWHLJrF0S=LZMfZqumJ`DOjev3l9LQkcjqN(j=whK>+QG44c@T z+*+;kPBU-eI;oKsTQQJv#=(IvPBA7JGAqagG*gS{$jF&Q8pm$bbDB=up4&f|n5cMS zhEIDIMdOJOMc!1YySnDOzkl@R&5^}c!;p8v_QMHhdO>S8~3?0iLKrTb<6 zQ*H}4@uem^b2FQ=_@W7h;XD_*0jcO}DCzWE?wT6m)Xq`DD4Cj?jy0+4sZoe8Je0d| zL8+3(yFg@Z1``V>t*hTl(0S3`zM;fp;k&F{10O%^#%;*gqUWuHG8Qvn!VsQLss?Y4TVzA67eFXqOJ`iA(NSZHh&~j-fz+l}#y6YN0b^7$vIMcCI?+x?BRLj8Iw{Htb zIs;7k3V@y0@N)v-BQ=sES>2FI!DVIk>qM+1@rn8c71x z>D}SOhgCH+o`;J#zW@65tJ$0vP%Cy$PI;g(P1)9Zi{(^`2>=()V1$O(^~ow!tgKSP zc@6yllBfWO9lJ%yd~=RvvBSDJ8PLe9NQHPgd3iOM$l`Z;29@~fU}3(Al@U#ty< z>0R@zgXL@iuANcE!eT#C3)B-5LvUNlYe|x~!8dYk2hKZwKS%rS%a;o@2M#1!70iA< zW?#;vgRs9dy_Hy>k>S5te-LH0=t#qbRxY zVGY}}l>!%e+AyjLCzH9M=fj)0xbt0*o=`!DosUm7%5ChO8ANsYxRX?WZ-POav+x>uQiXU@t;3Mt>cb0;BM z-2FY!j{Nu~Q*P^Ej*R^gSCX}z&fnlgaSqDx8ATj^K0iLLvTVi{K_Rh%WSZ1{p6bf) zQc3#&lEEPYa>je0FyX@$3|i9+TC=TLy1lTL6J7bVugqj zow*?%xl)iB?xTMg1IdA%+9LV*9b#rrb_1V@vPQ|tG3)nu$ zBFlxc!i%CEUzsX$)(W2@K8cDg%~%Ew z&&gNh;ipLvO6q;hMjeNyU&I9z6o?%;e%!r@UqL5u^~-0E8-@}Yj?NO@(Nmve7`byE zu$~@i9F3#UlIG-gTYXOH=p-3p*OzLrPd zh%?N3VEaSHPd!?3f8PCkH0Iq6^_5#744??u&tN5wt&w!chcpnkm$vaSRoH}kZ$hOn zULi8kYIA**Y3jkMK8!fes`_m7J9BJ{tO!2({4-$9qby741!|5L zexgwIGti$OuIbP3#iiLX(341EeNJ7-VvIJ(%SBc=A8-Hu_8}H>jC*NH8v|#i-?J}P z*@oYq^X|U>Rc_rqPOS&TuhYh!^)onl{J5+Ktw-#2i40zrHW=sCDu4V~S}`uH{)Q|p z+5WOa`l$)V&o1oC?+OM*=-=iz?@AjHL?47wny^oyUbGDE}g`+gM~gve>gI6sHfaX z885OT=J);m&_`MJ`{<_e=aD~`DLK_ z3HaN~0V`X>zM4i@PZDGt7N0MTHsl0V<=ZjpT-Z23N7&sZrI0V-C#~*iDRf5K$pWP9%abwJB$2{BYX(c(iI@rKR2J~+CuH4@B z%Bj>Nn9wgnc6|}nMSFYjSrT^LgR=5bu!?^a%geLm{8@ICmc646Wm;0}Y$h zgm=zmv{~G{e}8WGVq@R?UhXD7dnBy-Zm#{v{ps5jsyoJ*hAc=2RoifnixDavfha(K z+W6jIADK9azu)J|MadN$rr#?pEdj?;Ka;ftT(*0!z}y@;zMtzjN1q?GUJO@mHe~Jc^omp z&7u>S1)LB;^M{))>ah^u{&Qx(pl24{qI?_gaht4v<^?}^KzfPs&aHbpb{K>P0~6(= z3hu_dNbiCHk&F9&f1iMR_Y$zMGK6AQn|Mt85CSe- zyjaQ_B6c{7L(ME7C(1PZ1Q(ZIRRF`e9brgT8zW>=%UA#;%LQsGXbI@si_?UtwxjDY zdrnWVCV4iwSKIIV*V9$%q-7;vpK=)R{BbPz3gUS{UF|S0FPhA<+`Fv3<`3NQ^Xmtx zUTkHnmi%4t8J;|h`Y?vl{<1B1dKNjmxaL4>c+ zG>IA-c*fLc%ORw>c$&#L{20h)+`CQ_YYKv@m9{YK!$6}!aiBYGOlk!}w6<*pO^rlx zR@VNZp&@2Hq*Eu=&wwvKX*{pePDY87GhaL9{J754%T>6R5%F5;!1Jc4E!otFck9>rHK|`E8;sb>o1JKV9AHe*mCWxc~?;9K% z!XQw*R#H+zkT2i9omE!ei|BuUbq>mk_{hmB?fz~r_$xCrGuUw2Dk{F9-6B%w&f!Bz z7(w2?eGBT)Bdp_#7cUT>+qZGHWm^CZ=%tYoP)#7ckRhmc*_^D^VuTF^IUGa|M9R%A zZ)h1ZYHVB_0i;E*OvTO61|W+*0PjosY*x0X051Prz>>iWYQDv7$GyM4>toI&!}e{@ zRU8oHLEb$pdry>*M_8C13OvUFqfY8BgC>A(opMm2BE--&{P^+XR3$9r z1qFrkwzj<24Gp`Ra=WaSHJTln1R`n*lS~=-b4bKAoQ)x)<;eV0W8z85II5I@2r?juZR|Fx!b1MZvhE4i1%|!7CXjIj1!;j6O<8Js#%sK3sjW$CSD?=62& z4a!^O?MX}>ID~>0V=jQ>1B7-aiHpzj2!4lpIf?#l?23Z#){o}2P}+YK5wYz~0CA(p zDqL$IT(Oxxw6TOwesE+YP!jnAzV-Y0B*)H*`2>YsB>#7uS-yx|JL4ENrRzkW!Lcr7 zL`MMf<8aaSz#9g?BmOo@V{l$0ia6&_t|IsH&6_)`dn*3P6-kdN{Cx`baZ_=g)&3t) zCdI*N{F6@%=ujDFg==l4lU$PF9}v$HFAE+u6D{q3h70FFzo1}e`3uxzdXKy!ycFpq|D`WI^W@`!c! zVoiLVlOA{+qK>JC>%mG_{C&Nbvy(j5i1=A z_}bX0swe1s?RQ3nS#n1T4MeXj&`;n{UueXj|O|jHA=3-+b3jfSv8?fV+ zeUvtY>9|VA1uW;pNV$|3Xu(6O}i?bL`focjOW%d$?cW*CAx40Y9zox$e% zDx|%?AwE-ipwx|wj!8N}E5-?@{L~2PnjM$$mnmVoVYf%)haR=O7)d|p#iGc4+k zrdqt=%Eo+Gh&_wsfu%C>+edt_5a*?o_1Tmd8w&jRhi+v}eT{#;NbZh^Vp8#*U0l=C zyA}K!KOO>>73G5;D+U3Zm9=aip~1l&lLXVRE9ACEXyoOKdp2Js`w)61PJf@vkXyx#|Xgrk?9?Z((BRZw#jQE)kd%k_HmwU%q0!@>gxr>ZQt z?7iU6hxA$YkT~g&)#EK2HWZuPEY=S{c0VxCVD2jC;XirjrJ(qY0b!pxI=@>_In+P# zkm?EE4KMvOOgc0aXUrCVbjKiI6=sG^%-IvECA)s^= zdl&zGin*W5_30Z$VblBcq7uAHA?gnf-rE)RQF#2F>WsJZ_zlvF4<;U4g1E7lDM+RS zCyh{vxqeE! zI;51D$xnphzqaRZ-&=66#x;Nx*uT3yq=4)9i8rz3z;+{wR=Ng5U1 z1Va5TFR#5tKKANAJ6M2Yu|)2MP>XzmGth9WAe^;bxVrTui9$z}G(_o>PWOeh;4 zU*lf`)#n!WotPRDSwij`W_LUa4Eo^tm6p{5aUh%rveg)Mgxc+r%$WwnlITKnCCGuAw3&Y&LCV8qQ*h9 z#uM9q*%~wDC@%M`X(>RCSFT*y4JpdWFi`LAg$iLs<2#kv$?Tq2CUwy$Lx5TE?E0`j zmy{fHsbRNbk~8N}8z~IH{WCaS0@1=hI3v*VAl$;Ei!ZzWpy;zM+i)yz{H;f_EsiIz zeYmiD$l90{a2(PaRt8s~cAX+_{a?t z6PTv8j$D*wxw^U{K1oR@si~e5)>rk<`8G7g5ld=kP1T z!N0@98Q#Rl<09|lYyAV4Q8UY!d>5U67T&BR0CY6m$uNRWqNVg6uHL=dNm)?2_# zkrH5v=~i8Nz;Qqn0cjdRel#>FtEd=`dO{ucf}*1A#ft$jAH_p_P>0LW<|=MK?p^f` zjIXdJ%ZVsiV(==a*}s2no3Kc3uCM|FBn0EmTdV`vo*eGT^M}StFHHBam_*tVbi0jn zF6W_+n-Gb$6La(P|9`n+c>XnOtKVEPRPE}(9ohZ4*}_K-;m~I*_@Ro>SZd;TM<&n{ zl;m>57*+HiM|&UbWT%~9xS}|y1<2?($5Hss*!Ocz`YA9ardDhU8R$r9rsJ=()B)Q zsr(ND!G89;KFZXPcS8#}z**Vxn}w`adsd+CT`@OQjXD?%A-Sx;e`lfOrw=^0$m&|4 znRX_SrK9uj{>0dxUjfBBL780M_FuTO0K@4R;sRwJ!hM%{an;JWmhf<`EsZ?c&37q2f5MXOmO{x zJh^<)+fZqFc``6Gx1Q#p{D)AEr+BPS9j4^iLzZFKBpz?rULQT>s8&FEHq!6X=*6An z3#CuSnTJHmgJmkPo#XGvH4wQUF4FRk{eQ!>qi6--BHLZ}%}jEZ)K*-Wnz)N}FQ~hW zI$kfCJ{QU~cqRY%?mr)QRF{CXf9qOTiKo5&%zVm~iFeNm*&nRXXA1eOP6K4zpY z5%I;D9)_CAI_3}M7v(@>4penFEY2iXmtd=%Fvt(T@&R8-g`bZnQ7E_#vFhcitZ!I3 z(|RLH%4N(_7s2f3Zh%^C>7Jb=L&&2GNem<=bK+KS%&BMnG+NfIg!}JuMk{XXnZ8n4329xTll~w! zWLZY6XwBm794&0@L*)jtYx42^ZKwMwnHUCj>u{9F-%`uT$@QAF2(9kiE-o}SgyQ_x zr*$OuE$7+6J};@`XFE~;6a5tJ`TOYmx4ZrdxHg2R=ZGOzaJ z=Lx`thMynI?!tvOdvbhVNe7c+Xw`v^5<@}k$rIMU;j3@|r{D$TnQF#1)*KuF!oPTL zk1aRL4a^BY5EK4I4AJ?4tD&XUL1WEv<<9&US&b_dI7;aC|1P*Su;Fhvv1n!*8$eUZ z9%5o?4GlU7*F50#6$8HnRBWwHEY82t_a81|;ep*vL`JsgNR>eIyA(pa>X3k}*LiRH zf;W=f8=QHNQEV!ioO>bKmxH5u7GkymczZR{^f3fv40pg{Movi1_%q2V&>*{G?*UzJ zW_@0Fqyk)5&;oO}%Sv+T$-9mnXSCs^Jrd(K#)dYFpw6UNQ+e|$s6o>qU?K{E#)rKu zUMq*J3KyG$Ew13jmj!dDiqdOkS=?eGw(Toz)zQA2VMgmvcBib?+}uOH#Sz6AfWK^n zT;3=0{eBL1cn%|QrkXd*L)~s>@QI?+4V71|{09^+1fL0wM*MYTI{TQV-unD7qwQyn zl(0JVtAhB+ICFJZWmhCQ@jWy|RiNfUhh}5cPlj4>Hz;jhztg|(${f2VX#SAv4vste z0cQr#Q8`%q!jra8m*7B%WKpI7t8|oAf75h!e~LGkw3<9qKWhlJ4!S)*K`@BV(Vsjw4M}N4#QP8H?X@2gUClQaag>WZFedlBd6)^An;kOIL;C_ zIxikO%ar@mSKQ|mAh^G4p6FAd4OL&+;(Nzai$9~g3fh#&z%3@Rxs+WD$kq|&`8!{Y z;hyJk5f}j&5O=}o7zOh#pbWe=y;5k621Q_KrpKD zB0GqfvEw59Ij$Xj-fvcea{E@ICwhZj+ek5m>)Ehy?uRgYS%${D@ z?#=FS&ScgF(5GX)6B;j|8R8{t1XbB++JIh5^aF1l_fyiozDS8UsrJArWnI?%F$%ox zD(#tF`DIjT9Nh}26A?NrgxNVbB0OusEs1uFp-xn2C7Aj$T%Kkw@L-bm?wnHh&GV2EG}C$s^2?gI8*)KScBLxpokI?ZEIs zH$Dv>ygPauW9r}uEJ;v)Et1$JVUpd!Yt<>VoUFU=HSBDBBYwKabfbxJd&7riGXB9_ z;0rKRiE{YV4i309XknOW|40Ibn|mQ4D9PY~)|~Q+3dhgK+W%WGl>zV`W>IGJe5K`n z7vfX>d0FP6PMNIaFrCi0!12_-GeTs}T+wvx;kujm)>E@}2NHQVXv{}A1*|C?IIryDQ{B`&u zfD$PyD=t`3e>kF-p>Yu8#mOZ3`}H3=VWJ6xma1z)*eHCKL8)f!m(sL_%l(wkAuRR%UH5%VYg3ziOtzI0z% z!dVMn@a3-modY-q;ne6~xUdK6h2cqcU%yIUxUj+K8fTgpwsCI2eS^P<@`gHZ9qz(W zz?#v~T${QNc3LJNd?;JzjULl>?sC*9bmND~Aw)EC?T(@h3=N8i&+7b$A^0v~+GB{a z%w61@H9!dDF-ddZC|R*to?F6w`dp-uHkmbcGc^^wgdO|5@8a)7iXzF5&@jf0TDQOo zOoVz)5q;>xMM=IbI7D#~)aa<~y|IYi!nT$F@+EmGK=_PHtWgQRZs&8tvRQ-%1LKrg z^HR_{(0`AYv_zB$MK6uI_ASUxFLQF}z}cvgW$_|^1=pv(Ua6QK2Wp7m#b?$jU=t5n zK!!7rg?GbqhlKygFIuJi=#vXrw&NjaF$}fUzp(bc+~vrTlbSJ)L1=?7N-MqAEqTJc zN>0cz{*ZFC{JwjfbJY^m{ZFI?WsqYz%#@V}q;K5o>?m0|{gxV-IVC-P+XUXwpw`ax z+fF$nFW7Ylsu#6W z65rtG^ZNDcbnvDx)0B{1P0~QE8L?o4KP_N?xr-N1`xn8)AO~2Y1JZg~nck&tJq$cv z)J2N8Og*NWN#ym+e?tbjjXFo<+B9rh@U873HWTTjr>4eh;1e}J-OpHP9h8b!+f~Ob zr3Yvq7Q|~K)5K`!vs}XnS+?`7gEjdxbi#Ix1G~2!PtNWK``&a?%{@apzsi9_M6$#s zHFs&yhxx`sigpHFlsJOBBd|sZ;6;K;F<&Ri0xdmKc6J%=U4ZvyQ8mdE>r|(dkm~Ov zuVT>XJFl#1a**v9wPd$&@}+hu6yaX_rd9%mIJ8!e&W1&|wl{ev6ft=`^Sl-B+Bwjs z#xuP^WP}!D1#}+XMHoy?0ay! zwgg=&v@g&)uvkCekCsau)55|+DtlVpg<{*97u99O!3So z5=$>DXL1@G^mDK}Pi`k^1A42Z0f84K^I=9f@{Nk*~{av+xq6 zh2=PL;)E2q!6O`I1*R~aswE_6JYIhE__Dln$17oSKu9uf;nvBchW}ZNz-}_j3toPc z+7qakWlDB@4yTZc;nHV`t1hFvQ1PP4xSB?4ApBlPid=Cj70=O9MFC%5( zOUUe5d+y;$W8>SfRYaFd$iCO2C2jL1y^Y9(&X4^tY+|wN$8K9CWtx3G9^!=?Z zY^*EG22&8KHX>NlW4dG(Cy|X0{@2pKQ&xeHtYEsgRH*gZ`ITF1z*I;YpPrA@NH<# z#6gIOi61_FiXXeVM@n;<_2?$bfD`L$C@e$)QdNEd>Bn8U-%tdXrCz2jOE-^Z)g+q3W=F z`?0T{s1;b`)uj}irK`{ai1x^XWxUpNs>d`w%J2JY*#QU%)=T_gN=Zmgc9i7e=9aay zI|X&Iy$24Ar_@e5R{9k*aMOVme2 zxExe;wrwDt68QM|s4Q)@VjhYu_w1Ljjmc5$JU*qeJgEY`JD|-M77=TxVpgD|V!O3x zVu@8Hj?NF<$Izmtq@^`JTR7|htp;3W!!nWckl$gQ+pne-Cb!QOvE7G zLXHQgB&}V-KKO^D+b!Fw;^85g;MLPIT_HO(E{Q^_3nro)3k~UB8*U+vpw04w;~cQ8 z^S#wG6dC3@Kg)|WvQy~JEIpI`ew=ioFPhS=Z7JBX=?WIwuXhUqXBQEH5e=CV*(WyK zy`O76ZHesW#TdXKb*n~q$$3NJDE>MqE96dc7N$4kM*Kk zNm4G%e|Zm%k*>4N0=lF>ntm0yIzlNNC3oOvR6_ca($`0F zz88KzxE#oN9Tn|%gU+STm>k!c|5v#y@LvoJKp}<()~=_ulSG{ASO`}ktSlQ%*vcvH z$YJqgTO&C6EPh-B0P60#6Ws4&10UF@&NQ?y?e!z*um#-~@p&claV5YRfAq>^jej2P zj-(IH)KCNV^$)M{+!yzp_~7|qt8)OG6SkF|63Y>O-XDBUfW&rtg-|*?>z+$?R$nLbMd9#aY3Q$wlEgU(L&KY0=(efRDx9zW_5A-QQyJKflbch*k` zL3&cS&z=iPzA+21Gw?5)s@9^Z%9td5Zhe!?ZF5QF zZgJ8=j;%QFxF*y?YhTp4@@E_(ksP z*EKK8{zD{p$sA=kX}%)u6{4K{7~Fv}3{suNQvN=wBI54_M}mplWm5ee|* zV=V)UKjG}Ogr7w2=KOq9^plAR2hm6~m2}65-dZRw z+U?!$t>^3HbEK@DFvp2oZYbd!c4)ONYlH%>;v4JRFTC#+4G&jS!#bX$Mc{ZSa-fC3GGW07c*yh+cupm;VPe~N( zdOAltW}M*#P&qj{m*!pu?~o1s@%0Jm{k|Wac3*BQqa00|)?B|~#GK(A=1}Fd{?vdD z4dh`%GE6)77h1w;k|`nU*384D@Gjho@Vru_9Dj>Iw@Gl~cteHaUo6(>p2<6<@2^w#q*$0aJb80=p2Vy4*{Azl9nq4&-oUE- zFNT?V9T-LQy3=W0>>*l&hnY9Vt5wAMg#@0= zzQYJIK3_f4-rs4}`u^j8@VWOb{M`kN)|Y+3FDmMp5ay5ZI4cN*g#`e=%5-@TRmqe zpQZK?>r5B9dTn~FQ9hVtAwkd*7SopQ@l9RdqU-;okR!$2_>z0(h zit5h?g}JDahfnl^i@nmx%cGeY-lUKO1A*%E;u%h95w3jT$cvKp9KPWIfz7JbDatrjJT>XCO&%P!5?!pmRo zC2)X0iv=j9%S?em&JCX4wADQR#T2nzNh=|+$j4I8j3{P2`k4HsGI@^af0U2vL)IOK zmDb%Z??Ym*3eHw8s~f;6XUV<~*MEc78es}kl5i0?>Se}MKgjmPDhS$^$tI@jH+UgZ zcN%U{Yzi?Ou2rn%7{MYoYNAfJCNA9L;&}-A<8L0YDXM6Y?C)3Kh#_CcdYmN0@8I5hvFwTUpE9{H*?V59JWj9R_h;l=9mh)mY(3P zYyY)`L17snUWcbQzKy-~?Fkp``*b?NSg)Vi1?MZDT5sK9!5#cW3CInMyXmWVB7dMg zr-x`Cm~_xkRMc0Cx|P_Y6EH0>HE&y;7rhND-~RN#`LFIK2YouC7k=O$ ztRZq1nORD*6*6==jKtRR*mn6R&JNYNdvs=`7(u2yaDYQ1W~YXVj`eYO1xX_XVX<9B zK#o2{OCJOF&ZV|%>xQLQX9W|Np8awGb3Ri1!y5?^^Wt>BCFPBlna+yWwnJ4LUE|SO zGbEdHt0LPiiOu}K48lfpL^~^2(2R6jC$mrR__4lkFM0RuvpJBH^@HyKbCEvYln4jB zr>jSDx-`l8YHU*bP}ws_3FsRO4aJim2+29SL$tCv{1bUvRD@)E@Jci$qY@E-{3 ziMdls^hm;r9+Yw>lH<3R_WlP#24){M2^k9tNfoDuDjVW6dG2XW?^_mL?)dsj&T+=8 zb(KAToXUC&p2w#>>5`%&`wpSE7sm6gT4jQ4(y?oZ=7ed z&S|9Ka%+n)EJvRvZSNzmsn8XI>UGxPh(8D=egVhd)`?Y5Zi#&|K>3JaUki9gk(* z7H3n${=|fT7gEHm-7aXP66oix#1a z+32adaGwYtQEmLWQaxgBJ1MDd;C(JAM5|Q<4h(c#uUbl6-9@I*$5uYR7$UP4(=ap6 zdvVHE%_8owiVvw}3YIZEpY_6~*&UsdJ;G-(Q|_S$h24 zW#H=*N)~1BTNeN`cyHbyOTpUk6Mr3yI`Gpjccw2oQS>s- z6mw4!M+xp$&6)2nMV9jhu;&M6^@2Wqd2^E`>j8kGt<|ox_0V=(rlziQcQ5Pt-aAwZ z+b`(o%6B$|cLqR%2YA1JH4pYrvX3zK93Zh4B?}9h9?{LVQlzi-Hf3X8pC$-Z!!Fqq zEqJ{7Z?dR@o;H^c85yS#DQ#`-%%1;~!!I|inmv(su!wZs^#$(Jk0PO0wyl1yCvj&q z1mqhGa}A!&JbOk~Hls21)zj>z`@oRlq>6(EC3C(ForGG5_e5e-o?@B z*)z@Lba8W462gq+@S2|*PnxS*%T+A)k8>0e9?vxg)cu9FL;^zO#RGQKr3cdV_ItIEk~sMY zv}X?~fw$bG!OLP0P#6TD5{E(1_dY>S7lMF0{ltj`$zsmar>VflhxkB`!OIUHsF5_A zzQfB)r%#=NcV^*k7k|igTEMdRFWq7Zn;-u#Er1;#6^(M+R@R0uJ{Nz-eVbzIERA}u zO^>%=er%%1I!7ld%I;iqdQy>%}`kb+Y+`^n%S)|Z0 zHZkXE*6NpC9);W5iaI%==MZffJO{58jqp^kwB&@B)^&wcS64rZjC|1Fe;wMb@)GUy zyQb2U#zo*&74Di1d^f|J`!lMCZo_M4;!IB@CVeIfX(UV=tF8LEmkATQ$&JA4xpdJ! zu`6HECNa`78*ah%qb)x&4CqixU_?hqZTs=CYJC4P5YmQqmyBbUsMY&lOlS2Kn=iW! z^4YuKObMIdJ-!~HR`QdYEBK7?#&pO2}1`MCv*uGVTzz-Pvwhi<;U9Jm6 zZ^@>eAE^AZpc8M0@t~ktI2wVHb-GV@nzf6n_|0NI7F$YM8(ztBxCtF7mX-o+{}wrb0CiByV+QFk zehu`F#Db+oj?rj}HhF`>7uQNA9LROL#_JVsuB@n6q$ex-&3h{ZkCvYebe!4pBP4q0 zyCe@00MVX3`e)2+3~phA)#wg|zFe={#|t)6mkm%QEsKG=Te}`N1w5jsgV98BYI`FI zw8-nb94M(r=={p3|L@n9th;y&ZBN)ojGVG2JS-gVrc(FW`RQKti1d@LU5>rhIGUWy zH3nz+#dWCbtu*yA#cgzLdJ4Hn=FWaxlIeA0x%Qa%zZBUN?AwDOl~$fkW1?RMG=3Jz z@JTVgQ#%pBqdpT`wrt8)91|9%#7d6CmRW(63p8)shyY~drtN%}U@~#@N=JsBQrp=} z6RFJ)*vS}TRU1U&kEff&7K&E>lQ@I5^G}art^~^|*2LKO)axM;v)?VM1HM~A=I5hN zeR!gOi{&%_%WQW-)t_fOJ>8mqYb3t#bV+b`BDKg~eMWGk<){kRA$^zY-;JY)_N(S~ zK!u{Q6Wy?JIbLf17rAv!?fPd@+~oSI%waQF>e0frB}Rx0c|mnu6f+$4^u}Xmh7f`_ zwtIP!Sz{YQF>*e2acih24*U&nZ21idSqu}8>IXCouTQVsfUZFQL%*q0wExNhB1cc} znge!GDMqp9GRLkhC5ANmix3BnV~0jsAePf!D;b^Sq95l^EvVm-wnELiz;bFP^B-{# z#AI;%DOJ(taGAr|bLr>vskXWpb3nw}$$wlwWsnU{Z>`~(;bBeuPRJ=qf9pW0(^lN> z4({*=Yi9bxAHs%0ciEvqjdrZR;+oUIZ5zRjIMTt*Zg1Pcn~T^UEtjLFLz&pW2lDUG z>m|Yb(#|GfW92k?<7$O^<3#nz;Pmjg#U++pp_NaoL*l!^XT%RPGq<*~hQxJvFy>z` znI&-oh}3KEk17$!7SNfR-e6_gBgun8EghOkg`U9IylU2+Pwn#-O)-);0=)*)IE?hR zjUO5}8_rBTbTY3t7`_6PYTj@fS+-0`uKQnGTI8Un!8K0XB&=#XSVD7=p?`TMSGw=p z7XimZRx?OpQ2Y6sQ^Tz~Oc`arUf%k{_kbQ-Px7=_1FNWu8W6#+W?VLu0SNNovycLZ zq#z#7Uvkh=_u+m8x!xEXNvU~rXXHw<{=4O|RU_^!u$9Wl`rJFS| zupYdn?k?l^<-D~szEhzQ_I=l}z1h0c&qUvIJtHSyqM?eDV#bxZ>Y<7!H1k8DRn=YU z1M$1;o z{<#Fb=dfVxQ)UIC>d;aD8CLINHb^3*DC> zz5uXMM?Rk?VAuv;yZ*O;6dTX-{LwVA&adKi@(87}+GIBVAW}luHG5NiIv8~bjTe+6^ z0BJ6+;TWyagRB|UIJQKyC?my|JT^&1A!nH=AV4mp_^b8=mZqJHvy0&T88q4-)SPS+ zl!YpQW&sgAqo}Pxmd7VZu{$oV_#!_VubG;@yHlQEB3T(~@a2}!xfq*fcFyt#tGwQ# ztkm)d8Dozmv3BT29SoJI83vLH`aTc#nP#8tiQj8AOtADL|25r)k*eepK|XfWI7vJ4>GPMF(x!3!-!hu=0EeBOCm-;{S{?RGa5gw^F8wo!okTP9|iC^t?k=|}N z_RVhWVDRK6h4WQkzBWGT-$n&=8OJFxQO7FFd7JMAh*u}wx}qi1^?NP76?{bEnU%on zOx?#p6^@pi^;=K<%dPt3_&MHTEfZ3{5##w3^Qk+KznC~)XWlWIf?R5>@01n4f=%0ZmW(k&|;_`c`9*@jtO7INc7864H?^_4e zNe0%ac!K(|1X*dv)ihSdXOw17*9hJusi*yp^U8T(h1*TkJ;0I)LvUGCRP?x|5z-+E z39RC0G6@bOXt;CozA&! zITr0Ybo=1qAMGB$*+ckh$5gA*5^BE&>Kc@ZKVuvy@iG5C(mX{FpGi7cd3&Se!TL|WPB(!n9+-UUKBl`Q^ zCQH7cg;m#}fB3|aH0*Q1S`joF&N4-7`o37n`h4~q84VNopjeS})=?rS{Bt~4|}o^7yR&tI&iC7$|4Ymw|AW6fZL2Rhws+GxkW!ptm7s z2(%$j73$j>rykO2`pmDW=Jx?(R49in9M%*+aFi8)t4^yG8fz+ZdF5zd{xhtN@l{-d zU>pvaXpUSftDx=oTA!w7ofEsJ^dJN#t)tHfW0Xon12kpCbqLkocA=rBx=w^a`9jO) z$~n_}P^f|!AP7k19NgSZy{#)Y?(k&Bno3XtyVkgghhom{F5;*rg-Kip;W>mEg_YSI z6K)`FxVXeu-&S#LXzp_0JYzc-@u~O>!H?>+P@%4UP)8`vB9&Dr`OmE)JKWIM!^G2n zH0Wz9afDfTAd;;Kl9e8uYql|jN5<|*ubbGcMigbe@0oCelF?g2IudR`G}xsr|&CFiR;3-hZVF-CWFS>UFN zWROR=1}EC<@sTR7+GZ|h_@WGYqt_-Ydb#R@M9&g~v})C_@p(Yw5p3-DxlQ~SZ+y#- z%g~a7h|SA^%O&#lcE&tQjPDOrqe%FP<`%h@;2v$)Z@2|7ChK{f6z?yqd?pbbZcSgP%+`bklO9D}&?I2x24+X~~ttY90hB zk*bAVGEWdPC6;5oh)|ed@gF9d;vPXFX!?PGZf7Nw($5yHi49@OoO3qY%1QfjMF)6qRbtc%56<{r9^nk@%0-X}Z_ zb|AA5T2-4SitpeP?0uBneT+gN`*v%|yz3pF%ODyPJgX<}ZY3 zt)@@?zP%K@pNN{h+V?z3W~P-=wXXykd)a5`v_wSy95b3q<*JNYC<~U{L}bvwE1Kly zrmpG0fm|Q7h?KB`_;i=Ow@k%W+^5*tLe`z>_%Fgnm?1BJMb)IGj4{%h96DHr&8I4# z-PTxxB^wxW(gDV!x;3jS{FeQBN$L>x5AHweb5Fy)x!KJS@?n?4Z2HsH_fDrzz_vG5 z2iH)#0IwGIB;+4s;@fTR{Mr1Z!Re;Q5%o|-?+X`iU;CuBWttzim!hVXuftfCT%Y?W zWyykM(1Jqf=wC^CAqt_^ny%UOJnpoZJdZn~zvieyWLL&u7~n#TMZpkC6&6dl*mF4l zCQ+Ro`n#`lAdL?wqh1^ky}>RC45FhR27hGVu2>2kOteqz(=`lUAUId_;wNPZTXx>z zxm@}7^zWQYFOr-0nQgx>tyDHqhAL``v&%5!c?YHY8;WOOB{qeAmHO4Tkg8bwdNGM)gcJ#O5N{p(S%mMM(GndKeb9mQEJ>|j=%Jk_GsAGDbf|on zG*X`R)4RA!_BDZ@X$>bsF7VG*CDoJs>awgCSqYnX5Q~B$F&3I3>oi(1McgB8FhP|Sd>QRNTI>xfgP-Xi?wKuu0r zFkOkMqjLfSgGG`;&-}I~9h@`kz6P!zE=m{#S6a0)ghD6*OHy3C36Mn7W!--*QH9`0 z*S24jA>ALZeZb5VBBS}Kuh9AISDE;2r8*eRuo}S*4mtfq;b{!2rbF_TGht$wPddJKP^txu#V;O~GBhqJd7s{`mKnXK_la!!^Zu>bBY#A2c+>ra>MC~^rwBusb<_UJHfClvP4Z~p@SwYbrNpH&aVe6{F>Qh)`Pj9+n z>xMf{&Py4De|&fj_HhgSb`IOYN(w7)%B!!9TB~kHx777u<1NLw_@v6J7^Mt>R7obM zWxM~)eWXtV6eN-uF}&lkNADhk3J=Rx_#whKN!QB|;#IHP#FGQ-ST44uJG*nhqux1? zb?nnmCZ<_F;&`YB(=xeljcCY+HcNX%DB*6vo*dm$17)8Y;N)R#)MXj@aj6=GRl%l# zLq^8<*YD1r9`V%d2kcQlX`-ooGm^bhYJt+y#ADA*{Ce*2d=5V)NsCQVr2syPT(1Y% z6)ZiAXKgli<2cAY9L(f26KS2;>NPg^T^@1gbN6TON< zyEm8evfGP36ym~?n6&Q)yS$8GI;(4H)o1}7!!RE_Tz$vR zjSaWR4+GXQ_US8+>1vJMzf@_UE%E12Md{Dqno9WgMDBNf_MRf$_YNxlMji~J2bf2- zDKCiYq|dCkbo0Ha-{hCj355c+LF&k!z`I*`-d7}sDnp_p>qGn}PCYBMc^i`Cwbuk^ z83U9%CCR0y@LoWvK}|!Wz!rcMy*baYslH3scinefRNhbaGdUal@LVqpc4Evm8x0M; z=t?UQZtgc%WZ}6U^}ZJ;$jAxs{ZZVfZyYJ&)q1hf$uqIS>0Y0t#IeeZISegEA(&kT z%@|`publ9ZLZt27HnP4j<8q?k9v>1Ys?I2>60qf_q&U2(p%+-0DFPAbK)RjquhXK> zDEfJFtL{j@H9UkRD}`U}IY3)v8d#6+7#d1}@TTww7jlpgi#7HyQc6wyb_+vwZzocB z<59P{!*vnj7VE;&yp#BfbdhItTiZejJvUC{wo-`UU4r6FcqoPpZm;YK4FyA#-Gb_` zET6r&BYaXO&=gcUO>wvrK&#r;rl_U$@F$)CBErHw&z6N9p8XJp(lEL$JS&pdP_=rJ z_Hl1oVIKW%^IT{W1#_<5O71OGcAik-{-rap1lz7$Agc_ICc%cJ)CVbXQD;00BXKhY z(iu*CY`4LWtuaagQ2%g2%t1ijow2<9Av74?FmHhyzZh+_uG@K&vJO4i!o07Dx4%ml zh;l`9{BJ8h_~_nRmy$hIUt|9y-^ z;qpx%L`+^RvGGXc7kiKAFV4k+Y;bECmVc5C@8~$D#rlR_JPa3kap{P<9o+Wt6xJ5C z>gQrb71w`kAt06`b*q4B4eDP^n$&y?q)UrB2qrIZVr*Bu6gN2+c?$T+H`x|72aBs| zLtV-0b=}_3N4&yCB65hCMVVjN37W|&M${>Bd5g{}N5y9IQ)8}(Q_;;5_Ei+V57v$O zlX^q)$9O2?{UPO#ITxs?wu4#(oR&3MVBDGd*To6O`(8CM0HhVA3rY4C$ zAX2Az>=c~d8QI3%G8EuQ_@Eo1;Yq3Jr}%G%8Wvlxjx5Fy99X=M1%jA zW!TSufMOzFxti=f+8*_t?9}1T&y@r}4Q4({o1$u`C36MeF@vm9KPQXlW+ivGPGqOf zS5zsHZg7(_9Yn_x=9|R0qB~~T>NDUO=mRE80Y>0Ebj_2y1iED_`d{P}r-!y&KJUmG0<1)hbv^1#<0BxG+fw87A+P$p2u zs2wxc|EhQQei?2d*z!SjOHi=g!WZ00fFLwDxqZ|aFWrG}sHf-8*6{ElHHu8`{W^il z1b|h*MsKhs>6L8fd-^9vd<<}F0I|7lCGzF_IaJNTJ^>}ub^l#?u`K`~ki(hWH3x6g^T-fg)G&iSdfIv2o1gq$NN>S5mOJu-^Y&kb>Ce#Zjdn zAwzc0)zbw4IuWsJgv#$_L(j2b)NEuq0$+tHqV1~B5rA@y_+?^ZqWzt_mC%688gTzV zE;l)$+Gc|1gwKu|bRV!w1lvGFAOR^aL=~ZEWtF*`Yl?c3X$TLnR?BmW@121n`}A`UR(RuhfOsze_mac{5E}@p zNv+6>KUX7Vm0|>$ukn-8wM1HxIWURTgl{tY++$c1RB9Tqc)f`T57z{&sLjSUKyY3_ z9t8ZIQnbKjO9xbv1)z-4n(HlCntDtSvN7cX^2pb)mR`5Iefu?x_8h2yUPastq%j+= zE-iR+n6k!Be0AY77=DwECcCuInpIMw7tJE6_Z(aRtY)hJ6rX?GIOpZg2#B2}P82pi&pWt9XOZ0_^R*Oabbn4_JZN9ndU;^%`P> z*gfqqQe^~=ML7fZ6{>cFWtx>+a~(1oDLV7=W6j*GQH0&O+;LK-*Y@`xfgQ!m4qD7wLXiQn@$>wKp(w z@CqzS$BGtVm}-LO<$Ud97N*dEWKF>Pf+w5>e85Gwq*sWu3@Apgzbm*7y3xip9Mh>U zPtVJwewm*sGy)@d*D(vA9L$Cufys#*v&>FesS=? zS74DuRvWN1VGc2K)qjTtX(s#nfkEPvVj&H$__p@NvI%l z-ua6~?%KL6^yb6KfC?C(@$N+g4{*Q2=nY5A77!O1N^0uyxv66xL4j)M_2Utu-KAn< zTbi5F5Q4M%<;%Fko)cuxTsxEz(=tc!2t)SBHMdT}bp}SMgRKd~Y-$ zDY2(M0oWu8e8hmlJ3mm`X0Z<_k#9$yeVxc_lRo+qWJi9e;NXdHGQh55E8q53jkSXe z0R0d!?pN@~+LY)N#zBE{3e3+55FP!~{DHthI}7;Jzpa781bhrDSFv z%(NpvJv`|G-gslpJOydYC&EJ+uGugE$)fO36ZT{bjf zfJ==&II5NdN_(j75b$Gg2x+^oj`xZPy5`9M@f0zu0Hm4)2RR*ZXhIxq&Tw(PX0k7ng{9WjAB$H5zB(iG zhg@V&zkpAy>eIh6KieSYD$oSfohBRD1*2eKVL>KI_3Q=(4fKcN1Dxsq$$UGGft*=9 z09ept@O3>3S^xn6CWG{RhCi{7p)oaC^=zik5Fr}Pb5&ivF_^(P46t#eEE2THjtv_| z0DuR2dR0%Mt?5WEp)<&l9g&{bAOOazIas71EC6UxDo9q4?*eqIygZABkx?{MFVNst z2vf8HcomQUMQ2!yUqF8Q04UC2R#Gdh3g(fWy{#N*YZ|afq4Glb+ORb=BAgM}TM_WH zLA9SWH8nM$0?`?I;2wuw?EudleFQCy_+rDk0AcL_I(-60+J;+aSlA6H9i!3wh#fZg z2uUHPE&!61?G*L_iy{DD5h0s|iCAn{w(j3{D}VpZ3kf4|LnnoYg!2Xpi~FB0EdQ^+ bKBUNFYI!=?)Jy{A@F-;kHTnF@ruY8`_@iy#Prtc-*Tf}HY3 z5R7pgZ1@}QuHk9;AO5G3nom`&&7L|L+LhEHuCTR(klVMOO>YG-d@{p8k7-kaR4 zbmmW=+Sm)Qv044s2X0#1J!D%;Ond-ea@I!Xfjxo{8KS>2-V0}1AjnFxtc0kVbNo_* zlO|c?N#mHC6Lp0gsRY(XO%f^=bu#VVItc~&r7LeGj9+jZ8yUHhg)oi1x9E@7JQosj z756f>8d-rd7x@yMs4IVN+X|5#mEw!)B0)2~4*M|sul8S)20TLTpO`&-%iu@MPJxf@ zTVuffUesg(`RC6R4gTE7#lL@l)%X9`KO#4@3afjwR6`sqF)*(>@*MjWTF+g2zR;c_v!$vF zL(u{bELw%;h70TzA=Led2}NXYuUh%xdh6=;c0hXimGbiP)%8|3h8s89dQ*NS6c%#1 z?@k0tS!_l#EDq!~zP+Kn={d|h>Wm2&t?y31Ti|ivh)=~MzcOcFF8=f3(L`ZCKI%VnZ(`F1`lu zV`_b-b#-IomSGyVs*1`fZEfue>boqF3p0GdA50!TtZio*1Qs=!jVuAb7s0ZY&kO>Xlr2n(kLelcW8u`+C{x+0Bi# zUh?G?6+uNs+!xsm5LsDS^k+?4-duC9Xp7?|MHqQ_-hNNG9rdNOl!Td?Sqz81#CnX$ zey;sWj&=!?y1IH@eZBvklfw;028Ps@#U%H5KHF!+VlpSkN3!s{$msg@>kPE+s~8r2 zAI=vP7BVI#CQko&m~@5r$?*G`ge!Fe15v@${I976UEe%i9*)t}(t7Rqd+61e*GYxk zNO}34Pc{=`-5(#^x*RDdDtbH_x4Ec-n~0HF(C@CLCVg~u5iI(l&H;ZdT~*ErsCIFm2j!c z1`~!z|HlWaSGIR|olkkgUS{j7B;u}^qkJ-2E}Q6e>@vBuB>yodN46tTusKCMurSni zvhFN=a(^+;1R|-fwe_V9we`Xl!fjBp~MU2m`4J$Jn-&jj=)_yh#bojG%c(_ukP z1%AzJ{VcI&hGn>dzP=wnV&tRQeTFOCO5_9;ZhrjuQD$d(L{>*fC$gg}Vr3&jFSdi0 z-Va{$tGOhv#NFLp;w&|}SZ+%x@*7$burZ4Cv`5CYXR4xxgcc*1m6#M_x#;am%U^Va z(~HY2_UBkSYn)7dkGW;CyE;+V+#EDGI2ay!>Gta)%YjFIL7|~c9tYc3czAf~y1HHk z(+I|v`qRtbP_x z%y*@tD|8b3@gr+pUER1Zk^2&rD1YUFU= zwb?y5n1VVW@!-M5%Dq{t&0jrdF5J|4&&haHSe}-VVbuHn-h6KssnGsB17x$wh7hXf zVPSI>=>KQcucZF`uw%BW2cDuWX$roPLdczuNiqI8Ea^oW8XAO!N&Lr;ABJm__4%Ja zi_6R7zh%=8{q{}lqgK&E*2-4TG1J`l7~eo5+R4EJjBji~*^9E8HaQq`V)d*D}3=2_&E)lVMJpNWEQA2!R(oj=W)!G`4CcE2B5B*0Z+ z5$Se_i;Ih92w7!i;;P?}GMI{si>FtrPWoMsAfw-f$RArw`(D*+_&CS)~|8N#DIX>Le z`iW*rBwRI9aqZ`e7cZVCCDB@Ry*oXI`Qg_HWS6}DoQ#e81DJds`%iORJA@8cE#R6b zhugn@O0begC$W=mb_u&5qM!F4rKZgOr; z+TFt=d$U^oI1Vrdo~@G;a~Q4gYzbPy;EJ3s>(eYWO0BIL`W})+mnQqknp~2jg6n^)99!V7=P~+eoW3@J5F}~JO;A#`?qCI z=8wEAeOf4gdv>Ct7*IkxtGv9>6a(Liu(;w*+wtqU{pQX|%!07&a~^*1*-Vs`e8Bmp zznD*B7;UO)>suQ;yKj(b7cSjK4v!C3Y#bcwE%PeWP4yKj%p=#<*Si<$?QCqSr<)^Q z#m5IjOd{t6g;G*dY#be>6cq_iaPaVa<{uEs@Evp9oWD0cJv}}XrT?UpgMono$^y~m@1Id)W)B|* z{`mZG3YPi4S&C8&r(|mmJ(`fUI>Z69?XDS0d6nSn3$L^&TE%56cca(;d**yDkh*g zS3*ikoqxG!{T=@REIHL3lG^U>a}Yb(pDNbf^wiY+;^V3P0s`I@7LsY?>3J;JF0S)F zprxj!W`}gOG+IIBN9=i!tNXPxTZ4_udX(DRb2Or5X{ZSATo9K1?DvTh*>n}ixo05p z$jHfc?@3-o`>GBE=t7y{3-!&;wyfwNp`{JW&u1ST8v54UeD=wcC(bJsJBYV6 zGFobX2I>58|9Z|qS!t<-N0xd<*-3IsOH01{o*f{TYPem9NE>^5aZgVnuiSmRjah|4 zi=K>{&$8OZj~}F`43;@qBI8lBUp$WlLaF&r@$&NKNNZ|Rc)w^SjqGyYv+OLFc5!hz zEFK=#K~pni%KYNu0D!b7iQJnN=>EFZamQERbI*UQ*tn6<+0D(kGg*{CU^d~lQzt+_ zmY>|Uk+B^C*dFo{u0sb-NBa)MVsOjZEc~)cN^=|cdgl7SeLDqMg&=fgdE~2mZ8OwI zc*c2vh~5EnxdKc|iNT*faW4HVArh&_pG-?*EMITAUR1d}+ZKm-6J8SN)s)^C?S1nT z7UQ^^fB<6F?<{F&rV{PekQ-&rk(ryfcy_KW>DoLyY*$EfX0JCWDs z=UXBdu^YR(Nc8mdzQ7yL*}AD;8En=TyQUw{z2%;z#*#C~Ydu;AP^hceO8eu3&)7mj zLeObE4-T$_yprC-*1zEDg9h>A(ejIcro?1qaHua`sOSr0=%}3CN5S(rT5+Ww;u%UW4PcjK zL*Xy#i%rkM@}XfsBcrTC3AW+>&I%Era`(+HX&f9J@5spW zH+8;vU%C6X_m^N^>zIj$%WTX;6y-s|Om$ir9jtKUmAFiz=R9%x2CeQ`L<`&?51(GZ zu>pZdz&&U9@al-u*hF5{aW`c^kIh2wwc$z+s+SkoW=r^Q-)0aNu96#JcC$pm>-xGK zHJ^>h;)PFUI10Xbd2HS<5~ErKnW2qRs_+y&fyNR=h3a{FZPw5e-Z#o=?aT@A_D13b z-N5NKQt0LdP1L)$&@ zYpbY27{IG9M2zFopbyk#$QGMff>K2ji;FKU2XgQK$i3*;`Blp&J`2jipt*;vLe64&+5yVGOep(L&XVl##wLo31*pkqH>RHjEiw4%ZFFE23M zxVGS@e6tBilUG3Mlu4!n%hMZRe`?hF% zW_EV6Peb3|*B6tVRkPZf2nR<*RMbbk!_dr(N%&2g=*Hsb^}O+C&rU&LJjm9AjKs>y zy4uD&X5N>xlJl+3$Tm`cc+7o6AySXL?Si&L$RP7Y-Y3+M_E)P;j`CV6%MQ!Dc2`sS z4^%WYp9Attxf^wJb#oI-xl7w=f71fm-olo}jg5^IHI^jT#1^%kN=yeYVJj;uOLuF= zb7%G3*Cf#d4p~wZ&}mneYNl(wMhppq6dec0E85vpEV*t6P*JoG#W(2q7JjSfuDmfTn1chh2;8{^}#b}J;)&h1)wo5Fs#Kcd@`bC`K+ch zMVvlTZteT64g-L`S8v{I0B#F$a&j^;Gh5#p9v((_#sp-NMu7k9Ms+xh%*<2JfqWcr z2uzg<`34waVt)QPWS)2-4@v+vC{+wXC_D_Kr579(ZcX5XWIfJv#Tt;LMvvHAwxQz2yy^iMp5 z)O_CeQl$(Rdb4l{38iFZ&*tisJ{$8qAOMV=J8&>QKJJaSWiu_2%2&yNc#~37Q%i8; zf(9CWX!rGIX7eAPt_+Wi6uEvu>lVBbN{FgXErE3U@`L5CyU?|wtxWmx{-TYoZ4KZk zAhdh{%#N6gw?>}D!Kr3Wa(|Y7H);|do#M75DPnb?H@)qc)=j_=UAoO-k4WMpOJ>?A!6WYhIwY8<-w_^l={VE|L1bXgN zz@~@ul91uzk% zfIQKcXbuaG)*&belMr$b@eHZ(Qrr2>lsh1gHr-{bkhmi#7$n1Z8F>z+NLE21r`S_X zjqF!v3MR_hLBvf%#mjYk@B;G;VYp&~00}_cZV3BzxWM||Y&E@e;^``{AnS(%-08QV zpNFk=Kj<18TbO(tPso?DvU9g@-#$k}qnN8#p*2I{N4ySAEU}lD*JbjTNoB-OEiA4* zD1Ll#ZDWH0SjM`Zo(PoIn}&*ARGArh;n_0uv?zfGyZVaT_EMHg$@}-uS#`_a6h8j( zS|(j(a$!LZx>|+HmoM+8UtnPqIAC-(YK}PAS&4w4e9OnrPXbiZ`Zqkba5OPzX^gBh zK6voJWV-29-RNj#iyads&^Ag?VoSYFJablRA~=Lf@$u;rV zWjndW8~01_zen6P>n)UMg{Dz)fBW}cfC)i4IX5f-F~kUa36(njj#aOKRo}pS;six% z$Um7o(a_NF@Y^upRvLcCCCx}vEueB_AgvdC{>%Un)dXtEG_cb5-|}X)a0uV85oB&UZtgV&cg0cY7n|R0EQ}561u1%#WB?;9H4aM^D@rA>V z&1mpXPI?fk;z*^Cx+#+zRhbvwu*{UD$kE%)D%_Y&|O9>7ZTTwxS@d70rAR2Eu z@R`X1hFaihQ*X+ALUJyT41uR=cEWQy@~%^kxW@{z5o~z$y^N-vG+r zGW%X#?y@0&iO?75b4LO?RI&lJ#x&pIe-vtTG2dgx{`>(6;xk^{UnPfvKnw@?gYlV} zzU2C)2bjudKp?=OrKMF3{QF(LD$qFu0`TrTaQKMv9&>wp8(|rB2b4)FUHj*1zT~)g zcoS1owIz}Zc$Ub5x7px8O-&6zNWd!~Zqzh3HZty9=RVCPfcZU+$+{?Z+*9u?jnH?A z6363knYs+ZX7kQyYj2lZ*0o)ocRS~s17hB(XQSvC$Qac?BQEo&*wMTSqnzouN9qZ6 zb(g$S)pFS#_@w+B+~FHt@Q8{DlL?`-{RaI4H1LmcqH7F#V^m7d`Lu?eLb`=;14G^r zG_X?y@26N&`E4P%)RA)jRJ>0_(p6#v$Wld9Z&jCDZ1ju7@#gHAjegfQ&reD?6-fLH zsQNQR|`}JcwfYq5;qX94&E2R5mi?wU*FhJxP)8V1gWE@r|0uf5KURJ=wL}` zHSnuwqo`H%$h1@cl-M(=hu$tdvpFe7&z~pe8EI|gye!FBn_FHcq@tqwT(*YLW6jLW zpf%Fso8LZC<-E_}caBw;XV!0@3ffHSgW;&ReIQuCf+V z1tCU3$5JbXgF%l4LDkERIBWq`g^i%H&B%!Eig2_hrt0Q^o$gA~Dv< zRXH|&#Jl%dZk{3K1_9TwwtZVtNJvPfI43T3V~o{OSu&e-8wVM#|0tFBWAc3Tv0&Si zY|3Bu5dZTX>&0)LuT9+g&7N|FE+tbH3rX0+e!9amFnUW22fFL`@k?n$*5b1X|E({I z+YnR(ZlQ*rC9tAG5D)+wusUBI6|&pCF)*ub8NX6g_74lJPAA9 z8Ds6OXB|3aU}Z%RIz>!Hg%>*YKDtfkhxU7%<+wTM(V`(61Mc#LCQvj**L)Pa4ni6H&UniUQ-C(i1;;=Yg?IMq%^N?!S8G459zP!c_3OcJj(6|g zg};3p08K+JpfwbT0C^n@$!Z*msGk~3J=Fh+xw$}~(l8PD+Q6#geLQ`St@Gb%YYCVK z5w2T4=6|;7pRLui&}K_LZ8ve;dF{pxe-I4Orm}pkfy(uEZn)bCg0YPV$pY7gRM*!_ zS{^`aHNLR$0w@Ml0sxA8W+wq)=%w3^eu05!s&3r5L+$VHFQcF^v*_pVZ*l1s`~~m^ zJa6PGl(X-D@!-FBk7^)g*Ol^qEjGYaDB$TW7Y#-WT`ZH^;`-ftW(5tZ5mwE7#JYS9 z+uhxr{qZ2Xr#@ugGKWQM2q9qlzqXj_QSm$$0ptWFY^cz}7nEZvu73CzW6I0 zjV(KQ9;}ZHkBTT!4k1a%11&0^2LNvTlDv*D0s^i6NAX1k@&E(`XTmSGl=-Q2X$x6?CKQkLuZ$H?EW6k_oogtR&r#*3!X0M|6VWF9!dq z$-A*>a?xA;L5{j*B=>-DznXfMnwF_J-~F4GW%b$iDp$$)+_qWY$eE_vez=Ma%+f+%1)w>z ze;Zgz23S&9@47VEh-)y+ZHzuLteZP12Pd&TU6823x%b(p&l?tmjT)-1}F>E zjog%(Wz)Z4OTiXD-Dn%qkag3gyV_Rg&$6{A3gQ)L;)hOAhN$YUlaZ3vj@73MDZ*BP zdqY@`r`T9nzTFx-^w&$0PMv$$*tPiCd~^Q}Wd+wvMF^clfojXSt(VVP& z6&2ioi#mVnVY^>R2`@RDUNDDo!|K3Zs|jaigcMVRC{u*t=6n|j3S4N~gQRsCUO9+R z^+1fEEGlT!PZ#?M>g(&>Ys=7kIn;3cFl^w~{YzH3NGqq0ByI0IOQamxg$v@WT=I@-+%QwIPEExvt34(W@R!LeK7Hlz; z`hxsZ0F)rmO~?f{y=hc7N5wu6tVBR~0Zt_lp74?G5~$#2tXhT7X&pUZLtF@56sZra zZEQSKT3QMu{H(BuQGsUmM>Q6p1@y^-B#46~xZiT)31u|UNcTGFV-y;6rhOtK!kv#{4B3>9~X3T zY{XNg0Tph6;aCf~6*{%4p1Xw7C@8%QiBRf%W{YWvs)DIU{ndzOn}cmDl*k?$(pvni zp`$bWW%;x`P7R0{0nifD^YCnEg$N_=;!5^;<>BGP?)$$o4}Ys7j^b<qku&_X*; z@ZkfbVuj^YzCEh;sr%NlEa@s|si>}RIEjs{Vy5wamvL6GY}6bW;^ZBe-!pzAq%D+X7k!s!%@) zIRexHEQfhfql?`&CN!zRLxnUfwD=K^9yA~JWqx$I`ATNDV=}@^i9ixi%HGD@6to~U z&{G&%TX*Pxo|~V?v9z=tE_eCM8wOMO0Q4FM1mHXs!rPgrFZ^_&_bh^{^}uwYR-2lZ z$m_2nBmGYfs!nP_kJ-Z>207-%$%REF^xM6$zTDu=aedHrR=sWR782qN-x0!*jPlR3t`Ju8SR z`!$G2E{3&)3}<%b>fWwvY%~TN!(lO)Mk+Tl1VA$l0a~@+Rma+TMW8X6ZDGm^7i$0+ z20w!Li;EJGKfpDCDmz(fF91gvgINGj5V|V?I|QQ7Ftzxi-Uxay9O&F7fq{qilEbux z3}vX@F9{0^QwzCMpd}jE8nnrn00=@he2(2G#nQSvYTv8#)${{jF_+ z?MkUFEG*pjfSyGasimc*&QGST*q1(6j%J9!@@4A2o}ZfXg+LP8;zR950nSM#;4M2m zT`g9hY5ef{jhtkbsELNsTj5cWVvk0?Sbxk>V|f)G?hPc%Vovr?YJ<`5anP#hj>e`a z-@g4-ppO=4CiKysKgU615WHRBGqI0Ur1w}~oe&3bz1G2S`7$vHi4W`<1|FVb(Z-KU zH+s_z=J&{AuaT?V-95ci9pcA#b!O&^?K1wU0|HJ5O>9ZVbM$m{-YxW(XBE!)5jVYg z!9jdw!#zD;|g5U1<=|@rS4DAYr7p6qnl187&W&N(GT3V0ByTZO;s# z+oTZ@5DGF2mSGetS-p3-QK-r+PZ45kFo46c(x4b4=1)jVBR<%0{i?=UJ9=zi(SKgJ zuNq7Ga$qRGbI+D6m-rjGE4e~Ve!klEk7vJX+)UwP8>qW?kvv@`2>fWCAxv5Zqfh@u zWed741cI^bXRP}XGCs&|Q2r&pR$?NE-+S8|^zDVB7bzgW0Vq`7xf|osGn3=PW39EJ zkj*QyA{g$!tT4vh^&QeQpnK`Boj!-f4MO-IZ8LtqeElDvEiqFNrzi5KJY~O{u^3?< zaFq-f7-=%X<;2xh>(fY6`V?cm9l>Tut(_etxv#Hqc?G#Luk$>^XVC{Xjk-ywu$|A=Aw&+FM!H;!S(FnzCy z%T;=J@tyKjo847IxZ^b%Py} zK@Mx(;Hn*aaXX=ZPEr1amwmi{z9w#p52yjWGUo5Z(Z0Nv&?bjY0WDkY^<2x5$j3kZ zJTg8MqpHmFO+P|Opy$+v*(g_a`L7M~m-UcK3knM0zrk;jLD)L3woT50z6dhh(@?`Z zh^RC*AEcOu5hEflvS&G8cf@es@m>9MGqVcN!QZsDr@&QEP^kZ6JJtD4vOeeHK(4N~ zRR!?IU~?!$bu8EIr65RrlwlTsUoq$i0fd)6a6j%G4D`vi&w%bc%qdGJNPR`xaKhs3 z^tYpv8oIw-l0rvjI8`OsdRBLK8ZYR(V<;(gJX%DhVgNA>XK3BMp`a#1e!?Lll15SN zR$EGA6gSf5&0NJE>$G%=N}3s|&cO=`KYt+(?V5i^rD4yuC1b$OjM#w~%QvVw1vmp| zd3hN`LW(N3YuC<>lsWi@gb<*{9uOETDjY$mLM1sh7D?d6Q4ZF(VW`5*2}RkU>iMOc zZVgQ5f{FEZ5i76ga_9jcNgVPcZjf5aeZ#4)t}iwIG^ z7P))yT|Wc2gHCNu{?EH4D0JzGwWh2nc`}0LW#N-UQjp8$O4vY#%GSthfGS1*N(QYY zo?tUMKz%@{i2~a68Yp}0RzqCzeD^jk<%6b%0ZB}Y7U;l7`GGn&rJ;R-?7C|Okqew7 z>MbeC;|O%a=-IHZlYb?{NWozw5vd>QlY(hWZzqh<{Z_*l@F&P#o-vbEXb-tGs}KbQ zO1VW_iC#t3A`zK}@TR%)(y$Ld^P)}#z*EgqyDK>Ku#12bJ%yln1BB{#q*K7V)q|;y zKBC#c)YKnRDGF49lgC6@^~x!r@QX@FU_mFX`P=2+f<^ffd{B zer+t}D;X?i=1hSB>w(-$lT%Y@W%kr|cfSJ!uBCejTq|AW8ayuvNUwnMPQ5qJmy(t? zu6cI4uQCIQrZ;R!LV7GP0`}!zeKOaLRhdvWzMYPD^>B`4Szp0l$v~P^@`Lar`zPPg zv?EC!=wmgUtj3Z>6_CO)hK!5?=xyH-@jUolg1S+l9MZG0;)5Nk4hpI*sNaiFM1BDhA3?FPVm2c)cr~hHkD`|E3KkGHPpRld&Jlg z{7CTv4j&o}eXEas_v{v$&5iv{l@n)&H4R5MowXl4Ups0Qjh>G@!@)dEe_8x5rr;(R zD*{c5&ETsP}TE@_-Qh&HpS^o8#tx3L*Q%sUxg$6e}@X*Aq{59jMVBppRPPh)=;f&L^^q|5KlUmI8(GOr+d> zuuOiuxhIvY@Y<9t8{lpuJC01s;gwY)V&9#S+yK2e~Sb0v)8ZBiSqj4`}{48 z1^M|R5LY?3PFMUa4osDPU|Q?W)! z&2XSvqPb8jM@mkvMYW2Hg$OeB<`~ni?Q1OM_|I#>YcAD!(x0kAQ1iD41A7g~|Ffb#Wscjnzr>@n$QN?2b8`OQ zI^kOTe7ye~62KI3;n%%2!i_WX=iwwSqb|_}uEz!z#=h!;VhHl-wb5`{xcU4lzL-Zm z73udl8f%iql*}YN*ZoovxgQ^ufsgH61QO1nqzdzU?LV)m0lY#BhuH-j!<4y|?IEY3 zOqN9YQ&hDt1Vz@C)1AlL5ytsv*Po?_4*U!~*8f<^7u-fNYI>|kWA(C|GK>M=+A}lj#fhtfZBNC@3@?U1Wv*GB)))afdIYs zUumFsaa+{qh=l;NF=mY2Mm(MhN-NC@ND8{bFU01E!jxUE^9`*Nbk6zQn-3E|lDZxawN2meKvUfs)ZcZ3DQMm`jZ$>cy_(26p9su%S ztV6v8B9f`;hKkWfC|l8mDVryg+nc{_^&_&DPIAusBaRgJ<7JA3)} z3EK>~dVdTXjy@Ip8BUk8*PprLCr%o!YIf*W`Bm$y(ZG9MhukqR=K*|r;guK}c^cUT zD|Hu`IFV{_+P-|nAT7oeQ3D%}goFeg?*Ym845C@?%z=_u03qr@0hM53nV6WUal<1ht91EJ3_AsY zRVpAC;+ORfp1N_PN5o&Zq_3X0OG|noStZ*;&k-@hM7i*I`A@5$hJUtuWG<9^$xnZ28*K+Xt6RBVwNKQ`1t|+dprk6RnPA*c>ft-eyB!R}l}|DJ`A@2A`r1TZEfDF(w*d|U z97-AJZSkEqXS9z1W1s<~q7blTFPGb zGhBl|ex!f_7-7kkj0Z~_fV}`3L05ynxH=Mc@) ztDvNgqfeRB4iw{q1{m9q-mrtvn>ek$K6${NRQ?jVuc+YVP;qJNGfgcf#AkQa@8Jg& zT>PJCgj*n08|2#yx$i1xJMjPnfvt`@JB^HuF%Yn3R|E3*1;U)+`gJ^1-GP`WWD0QK z>eGN08ZsU=*rQ3v$pfIxpk#fbJAtEM-@PSoXh;t=`kt~f0urxkpy~~@0}UK7L;R|{ zYDsVPdlD(ush=1EHnA6BilAp;KnKJp{U&so_AaxEkd`^z&$PARtFUkV){_D13|985e4aHLm&8!RT#j}#!3L*dJkNFV;=Z@!kwfEny z9#Frvwe|DOd`EpCU+CVdeLPAazknGjQuTcE*JH*1VzQw=+UUEil!^@Ij@1Matd;@zs^nG_r?zYtUXBWzItBV0xmp~fJiZry)aH& zw%;#CLvx7mCms``JxuE~3;u}!4J|Xto9q5=A>s5ucOwa-AdC2euH78Lj9Lmk4}ks6 zcinmvFYHAFSOm)K+zv>o1nA2m>=+!1Y$Pqgq6Pu{88bs}2AU?}2Hyx3?F&aX^_- zN|+&44}SK`*RP95UoM>qoO;r71ys)YRCixx9Ez3)^YAoubYBv6@sV+fZwXh-am>H- zChM;R+jfY@M5IO;_nisKzJBO=lGDDWUG&Hwu*h1+=TDyy1i<1o)DDg~?~FKc*iFko z9|;?q9_j=@wR*4(AqdKU!6X$*Kl@;wQFluO%_J3eeo@2Z@87y}Uy{HwdImJR>G|ow^&w~BIPEgr?HZa=252${B5}tDBVSkolCcP{-OLpdSy57q^roi*89l~=i~27 z%Ia#*se;a4y}{}o;+r}enuix)M=pT!egwGwr^5e5MLFM_eb$4TPm3{vx&`_g@CrcO z&y_eW^aio5&CiC)T*W#K(jdHsfC=maU(m6`j@a7q5Xd1k}l=WX%gz0A+>c*4o z6gy1K&$>CaMe_kPryLy|{54r~yW63;ArZC!10tziX}tcV)YOql!a``ukB5)3c0Oc_5oDlOZ{~!IYmaGkO;B4 zvVZlNbm;lIcaZ^4$stiGnwK%*C-aW&Z{~Kef6RiSc20IAjuO(OIM72G@-g46UxtNA z8yFabFVA+W1;H8nmklSwLu;{rmN42*XcwR3}k7wn;WbA!OO;2 z%AE#Pne@I|G=L1twG$W-8$~BeYY@^z`(Ho-l4K z@N`}TC~`TV%w%BvNDLg|P2g0q49+P&TpYqCIaKO^831J5i~l}vPX-uhwh}Gu!M>kk zY`$O~N>2t1yO_B6;uhWqp{LY``qp!iQ_}zOcK(ZE`KuZXxIDFh;WV(@UjjC)v(m%O z9r6x{U4Kz31uYyq0Av|T@xc%UAXOY(+;5Ow_XGf`iU7rij)dOSEgJ`W326M!)o)LM z?)eS1uEWN`r(j1$M@M&IFl6rt>^^~nm!4^|N`aoJ=ecK!&WX}`Y`&@)ek9(RBmu5N zARkh|whEO3)BD3ME3RIr3rGI#HjNR+-V=R%AP;To_j(4gkjje&zzRbXoU8fR}G+U~Y`J+00xm(YPjUC@JWdilvx`N6c>D0T-T(q9QgDaT^R%VBF?|L`#M%`Ny2|WcW*s8!vfg=1j8| zOT&P8{Ho$tBN;x2Kg$B?*B02ot7I>vrc9bv*1>QC^%WVfqV;+40Bi;4@1AX*v4i0t zNLgcU;_iHcn2h(tGYdIKK$>4hnqYhthu#l_ z!q@PpM}0p4rP*p+^YfZ1U% zINd*7m`V#OvHS5x(V;m)C2P8Z>Lqxf(v+{tfT0S=);~@u0Lf`zzC^<}OHF}!4PNOD zu(6>p9A4e)*jPDWcXwT51t1#KK4`VZuH?0tw%ND2t(p9wPWZ6l{7Gkzn;--n{%_y} zh*tnGS&{^G^DxMUiP`C~@+!6|VTb|di#5SvWbxa}E`tC8vMy)zmX(;8*si`dCDhIa z+Ejr*#ukNOGcB)~Uq_Lxk$HRX@0PBUxuCj?_EGb1C9n-Cof;3MTTpSd8a<_;T_ifY zW!Y5fMs;E`EYYKGg}<;__zA3cii$<`PguYaCOb6}4L&jEwz2!hcCN93nydk8ELKky zx7L!|vQoxoVh8S5hPJECw1|gsM<$hzh8^Ax&dN%?XvD3N*-|i|mb;YmGJCcZrniM( z?w5tC*@#!8!3^Y0Fih^8hE}Dir9})%W#Ty$NLo;MecjE>D?Ce^YJ?ThFy$Rm8e;nZ8Pcjc*>7hH(!~a-HH>7;kg!8fc z*7>H^76U^Ie}1Zxn(l@MgSl)1V}3m7&HpIJ)idXX3sp4m{d2AeI@LkLAW~al%6So7 zhU0T{jbX&mq-ot)XNJ~uh*pNy^DL<(!o{nVYJp*d8KxVRN83Ls7oj^=zc}fEP8Y0R zK=5K(KG)&<`$wK1+PdU@xAEBTolZdZ8grLRlZ|iUXP)2*R*7g{Uu^R>AO-LZFMpvo z2;hNU4RV~+MV@)S_uaAH92r#DJQ<(E|Ir50s@UU!{$OR$hT zWe%8OoKGkZsiUXlI(eP7eOAcc4jjm;57Kgt1 z-?IKg`|~)D!ap_u0Hx3~T(lZYwn{n`=gK}Aa?HO^k%JVSg=H1z;%y;`XI@g3!%_Bx!pa@4Mb!EGtr!HkH zK6!EzY*sK(dj>{6#$mE?dSyFh0u(tM0s?<96F{Lt{f@o|eSV4;U10PM@Q4pOJAyzf zloFLpagw5*{VFc5bu6}l;wcU1GdBG@=`!Xr%>UADhq2ailAX_uO|OB&W^Cu zzq(=TnE4Y6CJA8R)}W)GtP3JV{gSBt7#v=AoL5D%NAnG9!CKlZ?0K}Mk#hticVdqo zu|S>)0CExN5kmARjhzeR*UNnQD?>NSpKc2dAb@Ovn7{V)B$vCo~FK*b*NnfM=j(xp*MZ2anHr}6U5 zyDp>B_|{ucPE(*e21J4m`P=@E-9F*?pA-u)>==xeyP&5!3>90wgyT?1pi%4U>eAYR zBsuZxSF><@Lc*R0;B?ff>3O&&y}9&*6xGDRdEo*Q{eD`5N2t+~-Vd_m8+GNN$3s2GyK`rFz7{*u4TFvLr04w{Pvqw;!@q7T z!9RUHxckA(iq60Nf9mU19YVhXCj%%1=KHjMLZX=feh2Q=0L)X)hr1kAE70V_Y`z$z zMMMqXBMeU50zU+F%`=OeivwX*rx_8juup+u9@S#(-S@>Z&tC&l!^P9E6%aq3Yw(y3 z)HeVxKH1sX_r}I}D#S^?9!bB6dNZ!|?b>G29nyZsZ7Keetat?i)bEp_Y}k?^0j8kC zJJ+#z$VSfk!Pv|G*67g>{x{?;TVxML3T(rT4&6o;b*iBM--V-|_8i4v*cbWJU7Z}C zABe4j0ZEjv0xviWouLgsOx+s6N+RB1-$jp7LN#kR%OP8^1IDwuU>9l`uWfJV+7I7- z%jy#qb>WZk1KO-w`wqb~VBAAJjp)c(01+)JP=Kcw<(a_(=q}@PWjCb*7t~Os-=60Y2uL1@F zLsI!ACwZ7Dkh9!Nq31=smzEx*XGFlR)Uv`u=6f=Uz?7#k%hU+H6edieW)-!=)(PtD?9^J6kdPRMbK=k=dw^E# zn^;*P($LU=$q!6O9RYAI(x=a#_l|%h2G0;!oY9GF1djKZ1X4$l)uT=QR)unp z@t#W3<#atMy;<>Lg$n$!P+fday}u8JntROryJP>Adx%lNe!=~>@mI&+#9$D=rkb(( z6M!=b1%>969V6!{9s$NaDW4&j6K( z<*08I`1|=8z}aM4D~-Swz!4Z!sYEBI1h^32XV2&&69!@e{I z8V=QB`}6d};4~`HId2Q`9Q7$6kwD~QZ;mpZqi3*T+XCp`T_BUnjVSzj%<|U)@EGYv zJqtcBwncQQywMS6ID;h&dRUa`gJUPq`TbB@VZABKrC9bX~g+g zdQ`qqJuZ4CAjF`koE$FdBn7MYhnj*#EDJi+R$@N_mB0=T1PVx$%LnHoF_M;?Tnz(j z_~~Mb{T;CqD6NsrEF}JdW-eNQ5xaKj+JBQ8X=yr!3pNy|GN3U*@iCkP0>=}w0c)rP zWAg8de#5O`Aux|VRdR)y8P3gPVDKF)2KEBZOhPFc^dz=s&|vT>v;P-o{~ga||Nimg zrxCJ386m5UiXtVMMWtv+_H2l(C}qpcXp4*@WmHDP&JImcp|VrR7Bb`ec&cl?f1lg$ z_Pu@oxIXXeeZ8+wyw2BoKF{Mg9*@WU0Plfj@3)~6Mqv(BCIo<%V}P|Bq>*Q(rRPu; zT|q|qGD*g?D|k*vKE=5ESyj~qk|@RFiMm`YRUXlMF$_qSV?6(?p&{BGZ{^GiUBj7E zw!RL2Oyi2nX_GQCIH0SoTD2<$0{0=55*Z?dDX(SAFL zvHXmy5`T4si|s^xCfn_ZqC(7Ebn=Z;bS&Vao!m4p6Q&Nl$cyikZHp}}Epg-)4u4gn z5x;RzH4!J%JTlX?VUdd7)8`-y11Ue^tcUzF>rs-?RxYGC=}Z@)B(at#0t0 z=DIdK&>%i$r6qAJtLUOx#5|?z>1Vi;drVz$xNbK7$e78B3qc5D@+);T(>&6`+TlmT}YUg zGYro|1D0OiQH}BJ)pa})T<2?6_ZCH;75Fhztm?RNBPp-oJl1bmXXe4|bEVqJTtWHR zZ=-iRuV3PPr@=Z+ItQruXWIOEGY-Gtg1w6w9X|S}?Vw3KM6mhK=SL=7Jjj%52_w7D z4x^!O;U~ZFT{~IU7_xsyRfQjJjMNqME7N}YF07yNQB9Hg6DJ*UU8uTOS6AKmZPMm5 zJ$GsNaB$&e_yt8IeWfGLRQNBK5$xISTZ+cQNpbw~joQ@}HS<$le=<3ZIkMyoF%? z!@RTp-qq;n5U{0Sji7;+s)r;HrVLNIjSAft1E7`h=+QYO40O|sjyn`ZNu8l{zyLoe zENuI(UBU6%wW~+d~wTgiwQYEbPI7yH~XL?2eL|4^9JN9}@5lq&N!<=ci{d z17ckwzX-tr0H;cUVH!jpoNy83qK+L2J-xEO!t=h2ngz@k-5mF-&pv0GnJJFAFgri| zX2c_o+mEHuu%pN?^Iah*e6NMWk?v0kP%H~4YF9?y2j{!VFfH)i-G-#0J+bS$kvI@VLy`$%p2$h-=o6NEw_7KmNy&Wbmr||E)y#Ibw~(+O+|A zHIW7kumZ5DeoUdCM`h{{Ym#R)JecH@qD-KpASJN@21_Dr%G-TUg5Us`1j$&MCYcs_ z0xUJ{2=UIb@3ZXJZ{NrE-^+`8ul*JSR#BYiP&h$zQ^V_!hJq)zlti$AL!bo?_*)By z`Y#v^a7U`|-FvB3_tFAp;c~g@D;EV-doYweX(YNufa~|s+0*}P_}zBbqMvzd;s0;{ z_O`w**Ymz^D#2|K4jO(8gdY08oY6{D>;G40bl4P$yFzCmH8NhPWB^aB>cAdjfXJ$^ z&z-H9+9;8HHtkac%6LF!JcZ{1=K!nJ+-vXbJoJq($^*}4rhJz`tfD^|rIEZZ%9w{Q z=iT(OWlkrhef2I(KH( z(;JulIr-&I8*kgWbM?m4I=;70bA(SYjPw{^xfS@OvC=i@Wd`a&x@3964EjH6`}#LD z%Kugb7r41iF;u23F8Dxvcu#EV*#TW$qBt|&&LDiKtDr#Mqt6IP!dqL1mkCz3!4`=8 z;H9-maYB#*QKd!6hgzrTaUES`vGJgD-%8=Fe({e@-%sTH`DH)&wZ6Tk!L7YbJ%5oQ z2eJmrMa@}S6@9C%yUxmvm3o|1PlfblTK7cwgY_xj_Rn(`s~ZPwVu>Rk=h!LvoA}>x z_B*s!uu-Qv!nt{8=GafyD z|2Dr7+>Q8HJD zU0K)=iVZF-0|L%+=Psgh!7;_>G0t24kY*gTB7)%|XD6!biHc?v{Y1C~shbA#8Q1yO zbdjq8*jbdzYMsxA&kTK2fua-7RS=EZaeiw6+Qiy_eY8LC9GX6%qNE}CI*7v*l9MX1 zDCE$_VEYT$CbEU<^`fl=s{=-fDqbU((RHAQCe0mjb9)CjKP6n&tJ;bxcn~oN3?w{P z5V`o^$np*j==pE`PE9KKV0#u}jk zJ3b8B2){1*#35xbAD^nC{5yt}R@YFj_yQ44&_v|XF`7Y`Bz#&lJkm(S`<6#WLO9KdEjfL@Wjckq3{tVx78cT8%gJ)uK` zq->7111P5exLzUNnS6^vGJtbyQ?ge4UJ>{PD5O;l(s_z1%GaybD>pud`>m=C8Uv`_@ei7(l{Ujm~w~_V%}WFj>&k(=!!Dh**7G zRrsLim(QDW{g!{%0t|c8g^DZustW|Bcaks35yT_;+9u%F5rnC!KCmw=*GTRQ?*}%N zzV+VCg^_-ZfeZNEC7m4W73K|b!@*aE2q0}NhVO^uC24JB-54AhEI{ecWehc*a4 zghmWp7zfGKhektggKGFhJSz6E{dVkdZTOP&VHO zW+mUhi3>jV!AxkbcE!csYHWLSW2CQ(uYfn<70=8}*<)tK2q8`b_Cr!Z^aqW>;A1jhC2Nt?5 z606VUJ1?5HcsLm__p|Aoo9J@atB%z*EV~359Pn>mOk#jV1)9n`E?;rfM|y=%q(Dvd z#?~)2Wt6w)I&F=yV>dTAZbPYQUTC#~23{=HK{n=q83^ly;_tt8K2YL|F*NL`e|Nk$ zOvy4H0Xe`=F$+VZy7F8=SjVdu8tkgS2Hq);mNdqU!X#LkZsAEM<2 z6I3k=eFEHURvbic$uB19o1l9i{>HfXv5K!bJ*^QOYt zM}=ByOHi~xQe`Se=~ec*&b9Jj%@UM_KU7 zYrpgo=d^=z3uyqk#v4LEef$`CkP+Pz{xFo9N4c}py1L;T1e^*J8<=ShUn=57a{zW& z5USpI+|KOm>;{hnD<3k%u@iR?Bry3<*P2t$_!{#Z*eM6mlU@Y^-W^>J77&l^={Z_j|iIy*F7K|OGA)zE+)%dt>lVSvuc!5J9m@?cu(M{F4 zs`1Kjf=rHlS2HdIJO=fYm3FSl3LlVEzaYbmtX;jv?xVi=&N)=~>gdqBxw$>=RxILL z=$||wZs+qpOWP~d{a2rSQJqojDfm0vjVH83QX~RO>#p3K<%2aB9{=na&S%RAq!|t^ z6X8G$1<2(G18U#+WO+=LeJ=b$H!*gj2#J%Vs&v^y$BbbY{}VLbVBfJuX5<}(dUhG1 zc)A14M9c$@t;IB{j0tVHfqGktT6*(-t+{i(OTn3*1Pt0NKD7P)>-=%~yumL~UHyu~ z!IoDVH~m4n>$K90p|bRnb6K?IU8Y>M`(h$OggdkY4et7d$8J5E-tv0gB4dG$-Ulq? zw)hO2@vFZ+3rB_lCZ08V0$vr{#H8P{JnM@wuFi5$dN02hdsn1 zD&I-%&*P~fAzgJ-L96NFVJL6t`PV4$E6`7aA!UhoEIKGsqZ->Z;r|w>l+0gZ$wq_l zJyN5O`xIM7+wxJK(NcWb&hreM=ci?QS~}P<{dO;+NH=4(H*OGN>%TRP6HDs&yJfJI zwGn3ULm{{svS_R%WrW3??)C)LxYJ2}e@mxrOVM@=H13U*TXId};Q07new|(1ihS>B zba_T{=>Gm-a%Y$}!A%VNVcRR&tvHtMW}6)+o>6h}UDZv`wU@>jYH!LpOHp>FyGz@r zTx?QX_+kWT$%Opwit&lp#?!7dG%vsXWs)Pe&tUZ{NizfK3JwQL(eHzv&+MjXsA}`p zy?#Y6UHuo2>_5xn@L8iRBfd*Z1W7BU$P-LC>-{*$)(xVR;^B<<{GvLGTA{mLM(3TL z?!JLdR!my6KUrRCYF_dbXYg3@lyysI_dv)ey_g=B-x?w{8Q z8Uz0TlK^jHBcnl}94(&(aZU!kI02y4Xu>KvMy0ip`VXa^q%Jv{+QOO=o|#f+Ar;Dw zJfIX`o>-87+>#|rFeXmFs3tu(WtWr7ewmTC*<#>J@M(`JI_mK9@*SHUpMG`?dy%X7 z&%5x3+%ONn>v75xPmSI~i+K>)ofUQAw77Y;V=*ATFyNDp(O?R}P-fD|Ll$`d@1`Ab z3F3Ya4hhNp|83e+oO%NQ-fC;ibqYKB@FVKBZ?Aa8X1(+H(VdX?8EKlRtveTzXb8Iw%^uWsPH^}yrj49Fx%xFk-X3LYgx(ttL6>lB?+t3Rx&ZAGg64Cke+9bf`qhm^ZLr%{bB1a7^$hI zFn-{WWlqZLm`@>)L7m_B&ML<0*V=SVIK21(a{*6x&QC;{Vcdq90>iAE^(s7F7*FyK zJRN4YYbi{i_AlqJEn~>Gw)c+_l)m6mwtQv9DrlOVD3|# zmPy~1vcmHMRIBcOu}1^V9kC$N;Vm^k)4*1j7_X@6R)r(SjxCj!=SS|hhA^sd-@|D` zi~cx)c;BvB%X&`tY$Shu1=-5sp7u@H^r6KIgfC2O`F85XdnjEHhCe3b3rND??F(Xj zXMiGBiFH2LMT7ATHtG=GDB1y3j`}?{dnw|&+?sh4P0FVn$E9##!WexX^u6PKhgK~j zyAW`^W5LT=tO9wCL+eR5CV@zuCsO#1g4_T*=}PO&D0r2`kEjOe>fhaeJe-?(d+HJ58ceHFGu4Xv_;yL7CQ)QK z+cmB%6{sI-K(c7ygdIRPqi+rXh4dTZwG!J7eml=n?ii`KM+k})YO-HGcjb|Kn@;^4 z!E3@!QtOFVME!L_6*~Fj>rSzVR|13~rGwG6)=h<-u+gw}=^Bu>?dX_U7 zPi6MsT7P7h?}b;6M&bI!+9zjcJcuspH9f>glJhu`2!;lhK=gGCduwhBWBk!UE|D_Sp zZ@HQrulM<5+<}i$F-kcRMO%$-%}9{7hwxH}Ec>2S48oMgqOckYJz~n~`=8H(oiW1#bsjoSnDf znZo3eLr^f3IGylk@q% #t3a;IN-c=hMnG)RytUF{!>?RrIQ?YiHCymY?6%?0WhK zBppxOSa?~DoZ6vM7T`TvcB>^##lWb%{4rh9Yt?#A%iV&UzUF!#Ii!3`s<~AfS@gx-nmoNe@7=E(o#Q`p)uu$&Mc`+3$BEY0s@(q#XZT&bq|ece1P-?y zixTw{0m{s^H7?TulvQVt^(H}1%uHgoDx&6Y!qY`JOd`~|7YEVdQaG1>&=6z`YJ zk#J;XMve#Z5*{JfCA5ibKnq`?iuy zDL|EA6;j}XjhPRta1_T|={$$j)XNAT({=P-T>j-zpF=&T|ITj5G|svuxOj3Iocx6e zA_=^s_DU#WD+Z9)0?UNR5P&R4x)vTDkFgq`!Wk*D_y`khGqC@eSXrL|7B;eY!zug= zH{|IPsx5n2+$RRER?cfWWw*-e#pu=Qjx~qwJzP5}zwwqEeeV98$MkITY$xhqmQw9% zW)q_Q39baJX;M(1@$~2#COvq-N}i~~nMs>a0XcRIg|2IrFq3Ow$|U-6reAg1Dc^!; zf@Q!fxMPWY^d@lzwb=~)Vw9kVz#5DJlM(R9x{u>@Bn?NzQeq-Ppr(!tA+Rz8ItRH7 zuncrbcbuJ>_~enlhc9d>esz6#eC=^<-9~>J;^3l{`ABm!t${O^lOXQus$6NA`$eiA0A?+GElg=yU zI4iVJ-O|YFRVIT$!Eg4>T(KxGOKsS&fvh2c*h>~^;B2n5X6nGYgVtgndB&X^ekTIY z^{dy(*-nlGeyBqmyLCwBq;RvGfKG3V!#sXn^)2Jx~jsH6S*Q{Ssr z!#hrjL)VeyoDhPXBeQCfA%$^?z{Y?4;Bscs z8=Vs+FUg>`?ys*1TxoDDiOs1K0|jCOKv_xxG-6w}7)F$Bpa^pU7&F*lA;begdj3^P zO4p)|v{an#>dVe&ZkM9nZ^C%;EPx`z5^HyAVolzZW@? zFi7o~@Ji5PiOJSIT!9WSnL(Q`e!ghLpq#5c!F^Kz61(; z7DF31?(;mqjn>rE)L08y*vm}#_BhUWEnac2_XIi@cDg`nj&u>EVD_bpl`$-rp=(6< zUhVjgu9k7xRMsa=^CSP_0>_F)V~Pu37&&+`(Kg5O4G^i1X%=;HA_a*bq2d)Z^5$yQ z){3W8O;O146|-llZ+Dj`vc5GVSJA}&H%w=)4b!^32SJ0P*_ka^g+Ov+h$XiB^O}&! zeWLcQ3lPreF!7oU4&Q~c3bt#Uv4z`HysTc!DyRSZ zCARMJ8?31T^ApA|Xeis^3k9YJ^`x{@$8Ms%W7dgoXWlQ-cXFAV$6W1bO%+{QT~>Vy zJH{qUKK^&x)tv41`UQhM4@@$y8$|vdCjP!>O~kffNI*FL5HSWDh@fU)IKcouKe35m z!bGTJ%>INP9rih@h5JY{BexKm!@5RWBzPQ&w7r zF1}e3CtZ)gBy%{w(yf9JwR-0G+b5db|2m&WxIV!c?vIA;HhhnW(D}iANt9Ihmq_aj zm9R}XG;gVdZtL>}hcfJs-62Oq7)m#oKW$E>HL{#J-O{gKgEM!af+>vrm{(&-785f% z*IvHdv*f})LI=Woyp+sPQXf3Xu&G~*aRDQ29_l70LitN#E+}~Fh5Giay?sZSop&9t z!?v6vv5_!9Q?Qg`*_9}znLny9yOBS(G+?pxu3rbpk4*goeEU6O zQ19sJVZmA8A#}w+&?r?j^LpGJFkc-|ZeZSaF@f$5p~+3R#4 z$elepu$CkQ&-+hQB|o_D0!)iNTVs)p;X7JVM>?CcH?#HYimzTRZRPR#;iX_{=c8^x z%eZGZ8!31$a5*51_kKG8uX@eSB%O|xv;D*Xj3XTtEHvwTEpnBm-!ZA~?xv4%|NBBm z4GwFR=?B*kIaMZN{=p?}hZb+wm6BYZd1I^UO}5rs!Yxr3-j!B7?l<0pnI2}nPqd6X z3rr)QMZE2AI8xU`TUVJc{hxv_q2zKo_R(ZuE{ zcA4;+v9s`kVtZnM7F~3kUAJYU<|{S*ctPogchV<4lhlHo@37jvo>JqVcuSx-h=ajWp$Q7&D+okOA!wS-wMHFkn3a3ne#=(FCj= z{s38G8pTKest64%95L9Ws(yFB%J;Ji>V9(9XEp6S8!GW|Y>!~LIka{pd>#yMUF23Y z$YrWEZF%ui^U|Ec=CFDHDOm)~-=7lrhjw6=5wdl@C^APN8c~5WE{iFV>p_Fl`Aw~# zUss8A%EB|H|1J1ZJ7( z8eEH2Ro^c7Gki_;a<=uYew-Dw7!a_UrydKPg?j0bEUgWiT+O{J_vN$8i!V&hSa<)L z)+{daI(&Z0%UAViVS%@dYIOEC$ZSfJJV!i+BNzWp3*fhAS}fqb zV)p0cw)8j22 z;C*YE++%t-#Vw3?RoN&$>FlM`^7)IuHL7+F{c`*GqfWt_L+{fTvpP8?Rcl+z?hL+A z@l`t3)0d|yN^0YC0iH!aY1i|yHeoCoCaB7UoZZBh`#Cb1+gpZa*Dx)4+$SD9%wL~+ z=caR-1jsaTwga9NrN2)UCW!Uh&M+jzH+QhlodWi=rx%mIP9AA5xRxLfy>D)ei&X*y zpXP}DQ>k`eiwjK#2lm(Wtd!W-I(}UyxwiDzrP_$@n4VqFhu58J7gwz7T+qM$Y|4bB za#~3CX0?C!HFKYLb%3|*^I-0e6FbFr{g7~-b-nOYEt}I*JxnEedZLdXC=QBp4Rho4 zsL*zODS7&O26sn0r8i?ug7$)_^qANShR$0c5PZE zNX1?-1QKPNvWcZcHX`bg34*rd(%^R^u0rDyYEKp0QlaWdO25$bzt^p z)&G1Yf3v)Hr3#A~YG|_FXcijJen0Sf#_;*m<3jZX2Q`didQ>mgye)fI!9!i*7YM6d z(Eoj@f^YmfdnaFY`xHID^p&S88nyQCSk?0&>Cd}k?yKeq@Ro4;S-d9Z;!`#%!zt^T z-;u3mM~<0)j@$hjZPQSFl`Z96w{@!ZM!Du5bk49@^5vng7TAnf0|dF`_vu0XOAX_W zYidx+jwYLLXWxzf<`*-$Nhu5+dl`&>)Zo~sGYWaj8yh;mzi){76S2SmorK}xdBD15 z{iFq#cSx32FJ5AH#IUBvW$GmR+U+cEA0Nwiw@=GRK6p^wlRJNU_&||e?L=gN>aFGu z`nkg-aIMm_bT4CnBhf~G3pSd+8)_R0>t$cL^B(@TUQG^_IEdGb=kM4(rtgbc!?|(I zB`;6cmOTnDSIOv{VTdoR(oVq4V@^4N)Lv2W;T13$g*DmPxylLIMt3WEwybOOda*uv zbDn45-(5ns{Y~JYifd~#cio5wo0=c6>6H3(rWW=+95PSNmiHW_*YMiy^I)5KGRLTv zT8xdF&SF6crLH`8qrvGO|HRbK6h(lL*FIXhZY@mXduiD9 z-(LeKk{Xi=>N4MSeVgXO`FR(uh6WybCkr3g8*?bFl4`Wd^dcFpG9LBvO9OTyUQq?lWoR&O{Dw2the_`|b!p2Q5 z3{)p)?qCzUCN&JWb9;Qfv|c`q|tJw;QkL0Zm21Gatevxg43I zg_yP+)1;I;-q6N0yBshT?Ox)ssb0NH^FYaYamC3__TKF~3#9_e+4|P#SdCI;5Fj_M)r;l;T@grWK~2 zIBWzs(LszeG0kKFez8RM5ONHJ%9}qDH3QZGO)g8m%RmL$vy4)oEXaZwL13?#?vwcx zqRxOg(~kAuXj${Z&-?d;raABOmXR^_`~BQ5<$Is&j1T8a?7hZvKg6J=surWs(Efy5 zbQJh7NuHl9GMs9;EzhuU@)v}Q%U(!2YB79y+qGA3j$J|(a~P8~WxPE4M!;<88WioCfW&MK!_L_7x=sy!hhM4N2nYA?jJxUNSJrn0Wa z+zlioX+4&1l*?u6^-C6XyC67@O0lX864IrE233~t?(D?Ik8)%?9J;DSAv+L5*#946 zZTg`H70Qx431|*EdK^nESTv;r-hQXxS+O+P674VXaKXrFcVlTkr&EMKJjQZ$ywg$1 ze*JN^k9y6jq+FkKiT%|s!9kz;&P-z6x=J!&#v(h{B{op)#=l55iwKdNm@t5uuGTID)MY2eCQf;L916v@XHA-j+UkjY4geeQ6KW z7kJ1Ycl~UA^4-_OLZ~0&V9XfBGSXC!m9KWc(C+z(HhjN12XfLcpl=*oY1xqyf2&|+ ztW0y*i|@zJ-rH^I2sDCB%ki)mFRrzGQ=Pql#kqFQBuNg3c}c9Z3i1s2=Q9=x&Q(YrKe20>E$5AB>_SvDO*+5lY#@LQF|?#h(Qv;%OeQU%a59AR`eCje>=d zjnKBrEwJ$zOhOen8nB22l{@AVARSQNh`+#1H#hj;T)2>q`oFLSKEuHKH55Ift?f;{iuM|YX#@!bzFwPKSi-0Vpa|89@<3L8^@$}Ux<$pc z?&Dc2wDo}tE-v=yEx<ZX%#cwh@2}mUjkWCi*>+ zdJb>-c<0d25IuDk>YZ)@l36~X!b&$eRG%l^^4x`$?#AEr5Fa{>P$0Mk0j!HvA;qU= zD#^-*QzC%)%T_GC`eFyhu(L#uq)`S4cfP0}2Cs0&+`q=n><ORZd2RpQ~3VF3>YV7$s&9Gg1^kEH03rQI-a19bq}h%H%3w2bR`_;RkDVMipku!Kf7 zaB_tNJDF^`>OWHSh_&MnPQlJXgDgSq=Z@aqP*imFIWd}hK<#VCF`k1}72d&) zKcJQpauT4rVCj~URi^l)t+3h=7w6NbPo)TX>ECK8RwH57J@+#Ew_1uboxJ^#VEG7d zD~S{$0o*ff(k1ZQnmc2$Pwm~ke8xQ1lt@lP>D6DEgNF}mpADapPINkQA~vRG_|p(Y zseah>uumglEsN#fA1@+LPEGR%hhV(T66L=qh3}{npxK5^nLuE7UD89OWw@}hrh_6t z3i67A%0WXBBLvyijg6E7`1~43PU3A^`ZkSL{QBABSboW{KqwT`@i@R}4Q!@2%ge(S z#td5(gz5_7a)c~vlgbT6f6gL4`mV{4SG8g?LBC;kh5z(~G4nO%b}dV}ET<#qG(vr_ ztHLi1yDGla`8%m^$zENsXa4W>3Cw<0g9YA2<6q`h=3r^1HiWZYeJq7CLn4H2bGVsw zr5jECJpG5X`ASNGw=1~|y!u?_%8L(u{!zbO=Jbr>FfY63=sc=8H-$HGn>))xzch3C z0P}q*^SL&hq@lJ>8;S7L3}u&_NsR=)=$}W#K8T){NMet}%rt~S`rs7Q15%ce-L5!} zBc;q~ky93M6~9MjTOlDEtmPh&2}99j%biOje!Gnn9b2qA<@}wFV9yTSKvo|Ob@s*r z$DuCsgqZpd&57sK2TtB)UN_^wK>k|(=;VPwOP#+)D^?D44$!POc}{P_vIe52;}KC6 z4=+WP3w4WpK~Ns0umRHu>uGTuzdzRa0UjVs{>?`04$BFeD!inhLlTL=M+`Q)XeufN#H*q9*G;cDt$ zs$TL=FA%+Z$HnL!@+TQ7k<9C@6U$2+em;?P)TwBD9*~J_I6DRJ=CabGuRSoP3C-L0 zJL&21?mgM~sAEAi&VXD}#i#lPZ6hBA1`pyKKn)~2S#j$j24`@_NbAd;d|NX!*kg2A z^Xv_-zVrfqNX^}ki|xHpG|@Z zTu`eZn@Tk~BnPQ|e4;Eflto7^Ivml~wP5w$`>ff{{`ez5ZqASE{K!k`NU|(^d^SNP z!k;rZ`{93-+xUp*$c48xpn=V&3s3M5idOroB!X5_qMFleFh5d;~c=TsRO@8 zc4mRr2ys9s_ZxqL=Wq2wrzdTBVsZR=*?>3S(ZyL-sq|ujI{*=?A+{oV5|sVV(yO zd`0@CaMYQpNKC>oDcTz>a+C(aCQGE`2x$zoH1hnm!c$8!Y7dd!o<_Pd2z$igZ5efx zzlT~BNtledyvuG%^;~Zn&`G`2ytZk&Z~3)|?sbZy@>{^P9II)kDC=-$>FA>OQ+^zx zt^!2R0bw1Aetd)w7LEo({X_h82{KT?4;Y5agQr9q5`g65d%n~AO1n~Exr>XRD<~+4 zvn$s%Nqjb7dQ-zhw|vy$&1ZSb4>qx)JUuhfs>xNsCl_yAcQfR6OmKd4sL8YtPLR~> zc9z

gul4{R@q9Z(T$MK4Mk7h->R~QzXlut2^8EnW09LZtz3O0odhgN2QK3redJT zJhQso-gJeEuF()nV1OHgsZz8z=H+zM$NW6=&rxg{8Bf+b>aaTbuAzTpCgr@HL#*ee=^107wK=Pk@`0k=WtiO`c_) z+w&EZqeKMbUhkxA$2Nu=VQg)lpuwH+aXf2wNvlFUimc zi+pV9oD|+v7b0_W^BMRc0JG409T z`)kjs15NH_cNPu8frsU|W@aLo-5isdJOpq%Hg(ru6^RPY++Qpo%g%M)Z>*`)b!&)M zVeYw+=ztgT#s$679lli=PRWL@_v5X!6of-!Un$X1m{Xf~zs$^Va(FN{#2;+uv~*jO zsx}SFn-;2eqPXm;B9OiaqCpo$+!<~e^qhexUZTYLNaWM3tl(oRzjZqAVHeCyY+ z+9|RFG$*7U2>l(vOOIi9iqQ(gvAMOfJam$nzByIfl_N(Pa`ljp3$$9?4ZS*SQ6*3e#A;)$Lq?rSxw5^rlqFwlFXRb zH`7KLsYC4jD(YWSU4`$9e&4uK^}`y@^MhJN%B;sLXiqeGiO3%I>$3OmPRyIoC^%3o zwy)1ZBfvbazNg2Dxq0cxS;|IC;x30*`cryi{Q=Ww#T{MJD?V1}>IbmoXSI#n7g1Cc z!;@#4nupZ3xfQL3C~cu~;yUe7{Vd&dxJ&g}<|Lapg}sq?hpfMc`lM&7O(ZOEWIW*b z&A}k`r>#}g7B+$Ni8O0ys09OeTi@MWGAgg7_TF}FvfVvHroBx%axENHlWJ^>VzmNc z{P~e5kSkAH7JYl{+1~zNDM|}3pAqdznUa-xBleJwt|juq538|}x|V&BSB*Q{Wn`K< zRD<&LuZy&KDsCLuGCwTQvFfV964enoW!xXrWs(b3=#%p%(t zJ@rlWwyFFS>*8Sk)|m3%QQDg8QrgDbebg{PvN--$ee27Tt(14%+9oOMs>+R7L8XVf z_G2Sexy}iBPUp=SoRMWssI`zSCk$s8T?sB0OPOmJyz9J==)>Moh5>lpTjL*L&pFrZ z+aIT1`kggH{@m6U)3UBlv8`=~Mz5`AJ2<>y;mW1o-6ePZ7{27*Cdu`-QtXb6PU@I_ za^9Zp$$78L=WS%#mOPV~mtt3F&U620RSfZY5KkQ_Lx^3K;Qj{{;FzRoVWVIYhoozP z5byx@)o=H&XZ0;y7a%6`nvROw_dTPh=hdp0Yf>M1sMVh6Yz->BYTDT>te?$eePri9 zKaDfWE`Bna+$sV3`->4TBpGJBn%b~!7e-ak=^3Ee6PB98aK|2}k1w}aG3VG>)w)g> zh_iokVWFYynt$?bP3m8`eR#1dQY?8?3R%hg|WjW1~ zB5HG#kAbe`jKKaaz7_Wa$3$gktZ}zJ&)cJdtM>2DL0b$@JTgE+R5ey15Uwn7;WWDq zY(*t^qkYji|0DWfR}4>z-D6>1xmf+_LU#Yhgo112{Y%L-&}3H6RIR?Z@!7%x)0me9 zO5^Qv)%Wgexg5F^-;=gZwbuATwKGd?i;i6F8@bn4XbT(?nx&zK!dt$3~Ww*~`vxyjvx+L5!2(mkqp5vK=~)%U-7^-Pwb zX;dXBZ@Cy}b5||b!lhBncI$OTCk5hg+s%kR8Mw*7`05c-=pCcmbxfoD^USMz`f^N) zY%1S5_TRdup75zDd$DV1EIlRSSMD=V6ku}b*PWuU?JmZgx9x6h^UL)$G*8xEy^MB> zhv#+?&AsUE4&kFaX<*MMGg?$NQi#h)tnn!AG_OunK2Y4z)^USXu=!GPf!EO!O!Dl5 z+40*ud~17#rUD;dioL>01-Z6uAK{z2jSGXaXqQ?b??M%A?DaxlN|+SbCr=k}T=1>q z5Djjm>(?2eTQ+O0I4+)=Kc#GZyDTO3Jmr1BaN)i?uQe{V_3=MFdRZ^D#J(`3rK59e=Q#CPQpFW}=M|nSM{M-L-ubMJFRYnr=3WfL3m(&Z z6ZHUSaNq~+E|l#P-#a7dbL*C{{W>wHYDaIS54+Y>4HU9aIgdI;pSnNheD|p#E8qsr zTOOYv!3o+byB1pA^s>Ut+45)0Ux{4jYw=Va8okQ2&m~3cV)LsDbX3~f!NAAGw94@l zzrPZk1k=BMN!;~V-Jv3OISw=1OFQ^ySCyo_&Ds{|`G(b=$-&Bl_MLsl=ekuJYCJ~h zbEX!*`>gN6KsZ7tF>!+dSW~Hsj%u#HE*CNR6~?D7p5FOJTTv#o_R+ zym}Wdxf^rFcdym3%Jc8GC*QTe(fJg%XsDKjDsk#T}w5HDJWK_EwU)XypVyR4% z>ZlxE%)@MtLuqF)5Vv^%gL-gEQtNW(MH%0!kB@%*)pYPq3l6~NearbaZ=l`tEB3^R z8^^m&8BEXI(m!)%-8_n8v$dq_o)a-+jCWnTwpEjpX?K{)Yc7hJ+lJm7rzr*@1VE!F z8Om7xN;aE`=~=Vv(3wk-3!n&Jv-t3U>CCwN=Q`zuXYr>My}31CH=V95NUH5>dTYbI zOc4(@reXecKFY+9k8P!YV+D`?f%woC-XPE+@xd25Ywyg?Iq|NBJB zVz&-w-*Tu@bjpO2DlR#Ap|j>1juC8{^#x)l(wvs%fJYiP8LnYqZ%!WHy{`CN2>0=p&Af_peXnL`Ur(@H+?X4d-?~wk{qFhw38t9@e6TmyT0LWOc7FNUyIE52C7t=@X=nQ6@uP&k ziToPb*IdPKzuNCi*h1Ay>F-LCwlj@pE0Yto(EPgn&G`#g5Go*Ha+FD#>GNPeeFYZw zNMl&y6r-BaHR=ipYba0PQ{#s_pGvl0xIExrmV15)X7KUM4Tp?W_t}-P(k{Ntq&Bm4 z;c51Ok)M4ZuLdcdevZnGSJb3kJXI)GX{%q=Eg@CMl#03zJ?_7bbMxyINdsyi(Sdm1 zMPw?T*f#K>j6jHE>tKzBFnK4c$M604ng0HPt4B5DwuDC2&ac|TX8Y2OR-k)>Pt~3% zi`FOK8>bQkJe56qVO7Pt87>Q<^rbsfl$^z^ zP;-hu<(p@ug~nSNFwD>;Te4~xz3Nm^C1WU9{oOfhW4ZMQ-Ny;hOK#q(J@2+6qif&l ztn{aT=K|Rtugs{|OfRjf&2Y9*f43&)Q`RRuhK#sI-2W;QKG7B<`av=hi2opNsfIkC z=gh=0rzIvCQmk}xa&l6k4jsYCAx9YxZB~C1>>Zsg$|PBJuzesrV((x^TUcVaL@(!N zbLPlLDm2HkZPh2=R484}<47M0{#U;uNz)2>aIimrzye{+xpftWh z%?7%{+%xjw;A7tq!KKI46>f^H&<^X9_;Tr1Q^q;co~Hd`h^zd#lVZK^*Zb@AybmwA z1PqVr<8C{&?LP-Eo{JEqmgXbAP!9THAQ`8SzrUz769fVeO5tZc zF=~m?S3cPdwEcwvA{om}@iW(oj;WN*zxnc%i|gq{(+1fXN-4gs@DVQBZfWw!FsS~_ zcE3c!ze?2W6pTH$@dLtNZgJ^j|EkqR+Nh|SjtbB??{%oLu<&O z6*R+u-XYp6^2nk=Ao1uh)4;5rKO85V0PJ(1w8Z{?2S_RHk|bI`iB(tV5;B1Qg2AJU9416AVGgq-y=X?B=&j`P zVK#?J+82{+dGYVRJK!Ky8NCyAPrq$d+4CEmgY!?*P*%G}KaRG%+L3?RMr*^Hs;hZw zVs0W9T}+g>$^CMr`$gV~oNKvPKC=-dM}WEVso_J}`n1K^l})yI!@;;6dK*aRWT_qb zClI(Wu2qAMfN}1L3-9NX-|dlx{yBuOBMP-H_8<u;mO3@6YCM z2jw$YG^OS#`3{yK{Vbo#kgqFyvy;aEzc>GMW+-j31Ls*+*B_H&>*cOjzYgLXx%rWq z(>KcL$T643l9V;)MI@EZ6(@b~AAV;uc!HB24B?!M9yW)D#l2J4;m(r~`=`j5G0>$$ z>rY!IrJfGq!)X*-1J7XjDEtRBAe0a)7W7c{N@7BQsD>Tx3m~Kcjv5$Y3^AJrg?XUv z{mjhOx@G&L76+(&7*+{#RSc5Ica>Sf=$Xr2a9y@1x4Z1Uv*zLT3R|4k$|;$B`NU`R zG4oB-3IqE89#`tVtQ1WgJO|I8KW{Zb?fIpfbcXl`<~Y{LJf4gy5vg$+MF|6+O`v*$ z3f4RjZZNWVJ`u0#&chDg_c^@&)r&|0O=~n$u?&oHvtKuwZ40Q0*vb27=&j44xP6~~ zGAi3T=bxRj=o%Tz9a||k6Z~OZqE70q1=A3JdeA?u5B~{j6$J3UzBdJMnb1<;LKS1T z86hPTs}C?76_^u4qV}Cz*TXKj$s2jHR_s1fgGB(VCW65YCGiDc^@N9aC7159Z&Yrc zWOF#>(0WSlQ7*PNp=QiZ?%251U8*4xp64y#Oq1Y1hILGl&9ZA4P`{f4@pQT2xF)jE#( z)1NX|o&xi<#%d5#V26Ph+)&mY-?{K%Lr?17k|6OmCb^FRpgE!wj zqY{&OSBS+~uB5#0C39>!&QW+8$F*T)~xyF+%Wn1aow+F7JRIBJ?EJg(CQ}H zEQ+%3SsA{od?Np$)xEG=7+)m){dcDan%TnC-&@JlR%qiCg=Z(FQNX<$!`d zxz@z*N8@J7TLz{Br_Q#?!DE-bx@DZ@mymc9CP$bvqj+MZ01NShvPqF3h~wAioRqSb z7CT1UG!&i`Wjp~C!M=CCth$V>Cqg4sK^x1*9*`1ZkphL;eE^(WS~%-|Ug{`?uBp3ruC;^d=8k6Qa| zBUv$#%YT>L`eW>y?LF1C5g@*Q>YAtE8DuEQb0~Ms!`?@EUzu54*L%C{Sfo;1Ao&I}SfF_H5J5UPCSqo>TXH z_lf&I`c5`14rueVJ)UWB|M*Gg$7IHrJL-FipJ&Q5sIoTU_|Y1jeHy2t&;XGqEmhah zW{;wZDBmVqc^6wjlWc|?JF`>2Bz-2&rQY?qr*}g{;%6f_$yX;=msH$N+2|?eXNF=f zroSQ%|Ap2Yw+GIwjr@BuhZa%*GyAe!L>kaU6T%J_{^7<9@hMuW zdS8uR=xD7erYG^5ThFw~)@@8K{d&Aowx;TCe(L5E9 z@TvH@fkXfAZly+qt{7ancJS76+{V_zVljyITmXg>^gE(LB{t~=0YH+q0X$C34zLPv z*qwNJM>*fP^%iF#uHtax%~t|El#G0JZ71Gl z73NK8c`ljj{I1QL7(PwmE9Rk+!Ue!wd$ub48Zz#g(tQL(0cHZ+XF*g$?lj5hkuAA6o$n zhlRw>+WkXq2PlqD4GAv{?M1taJ#)19{-5UFJD%(I{r`X2viB~lY#}>)gd_>sSs~dY zku7`gh!mlc71?`*NXTB9A)926-|_5vkI(1!{r&m<U?8q41dIO6mgVbppY zd4()0Rh+oU7&sB3?wh8YX~|4hG@MqC?A9F(|9gf;D+%DX=>Rw*Z5L%_&*9fuc9J#7 zewY0Uv)3UW1*3$Ob`dVI@_@eJED7x+(psJ`144$0WkE)3$Fp=oxgkGfl-!%oVBV z^!!)KEuB&8K^*oN28WQ%5f(EsQONneZU4!LiP=aAhHg=fH*H?xJElL;k2va`Tj{LQ zC>u^}b{ywp`dzQP4zy7$eb^o;jjZw#?qFFGyjQ6#=qGu+fn!S3TIjP_EaSwzmm{wB ziNkwa^z-%qWK!r*I}C8nz}X`CN8V+bDx3=Vmt&RFvQ#G?g%b&h?D5w;2{wAuuzIFy zsVasgAs8*++H!5f|6P;$$Q6$03hme_+v2(JXI@sf%hPYUcZXeFmU_gs?>n6~)Tvfu z|HkckpyOGE$bWNk;FFVoRKly0CaJ%KRr`c_Y1J{)XKTheSdU;t;6dNQx}U;Po8oBF zyX>h0b<&l5gbXqAj394Xrmzt9+-PjwRe!W$j!bpuHY0<%`V>Jc(@}Kg>`2W; zRBPvq{VnP@WmTgK57OxyjwTYqXs}P^)8FXErOz}31^=r&724O}MaKXoJJ4<`&=oCJ zVGBI&=3_pXL2DfphzSY6S1eT8IQpX!aVf08vOBZ8N4Ro~%`C#|Rx3Y24UuCw^EqCi zqE(eo=_aMw>i6HBHHOyTOL__oS(42cUdGNWPC!=wnCYT~jyK-@G0$Z0fZnqT3OI{> zy*bgzy?--V9UK$4ko_mCn}#le*6`LpUjd+p$}*=>b<=)0c^`8wK5!)7hhFHj74EpI z>ao6-kQ?frqMqyz^1Ih@l1y}C z`ML_{ZPz@9+h3;f-b}Po_b@Z2b~qHP2inUYQ77}?_RDT=l~bBE_a*DMPjK*=;%Swx zO-uSPysJF18g@;USJd2QL7ezKaovXUo9w#)c^{y@D5fgryUX;)4&zfpEU2c%1y1~_b`|3C4e-|Wi zZlk5kD%?WT4HaR<5SKaeKWVH$qkKgS3A|>XE{dwg6SB6eS4GO=&Pn8*<_9rej(XD< zd~>?R`O=bRM@ix!WQ)Do+V08`iSwu#}{a&wBS&>1FaqAe^4}KIDN~aGo>nloO zRBAl;_-=hK3)`=_CaODB(^i=5D_sq1NR;f-!`C<{k{0uQzeahRlY5aF#UetAe(B^{ zzo-yf{~_J64;8lr#Tbb0^NkcWWn>O`4l(6mL;VaE@E_%`ylANT43<<-4qAWb$%(7A zpdhbJsIYK4eev^eH2bfMtcS<@R+aUh4fwImHr1^5B@Ii`eKn{#R`bMyLlg(K>~{U6 z-tNM+L~>rwZ)w(@XCgTN+iqC%aL%ms<>b!x_pWMuw5M13oTreZSrtQv75m6hc5%~D z0^=z`H`&Xnr#LBlQ}bPub?X8i1~ii1J&iF=_xo2Lk5=!vf@TR3D$~iOJ?KuuGq15E z0g7(eK*)25uAx&{T~k(dfwBG6*c+zJbGTo&aj5gZWsGGldCEeE6;wrUnMvv zp400={lRwIN}Q2VC_S=i>n)8$Be&!U!Rr_5-E+(54s$bnqFlK_g@uM(iSvim_%J#1 zvGittcId(Cr`Rl>vW?}<_Jz$OrvK&uz$65;zd|n}@-buH@jbs--FRk_D~_?w$~wC} zp6|3gMXxrN#7ZIiu77^;_01~ZIceR+@7e*)jzL*)2+$i~1KiwfpoKW@^$SQCjn`qd zUp-*JW1lq|7=Ejr-;rcO@N3`1BswS~$;9?1VwfM;5XfW5!ZCpAEaib-=^4=NAnnz0 zFukQ3S82(8>9mySC{$d@Z6}VTs_}@+D1>;P?Pbv)=cff@+GKndv!jlpS)9UO39lsZ zFN?Ebt{)!-keCZjVU4{QEpxgW;Y!F_FJ*{rw$|~w`CIc&FSm%5-v}nuRPfP0Ot{qi z67-K|kd{$qa&hwS;MOAbUM8uN(I?Q4q%z95E9{(0br$u2tG{$sN^D@Vg#aO&!-A2i3?OHNM0QO4iYZ?>Df=JPkmD?6yB z|I}#v3~J87sG%N{sWO~C%pfR`619|aNPAMm`WoZhGkSS9qqCp1XuhM_XU9UNzRu2q z*{{!5zs=!Jdot@IqZjp&1Q=|Ua=)Kk68W@x*OLw^s5tk=T>Tpg$4$S6zLvFm_wmJT z$nMGK5v-%xEkJC7H0*kczi^?6$5qF`&iu?sPx{fBl_49)=~isX=g*z@_wf_n;LJ5o zKGnG2XuR%Bm#}!m@;qYeYZVEBSH9E&Q3$7O5*FRs(IQ@qV+~eDyU4gy+9ADWqh;^8 zR8@xmx(QE6McJVF62-H|*NR{O2iS4?ocpEau41tZ{VVfkjp}s8Wk!U4hE&PCTN<=5 zPe6+ypw_+BZEdup7tN-e0~osQ9d-V-fH-J5#gTp7o^3||VezQ1^nsSzNfmusrQ1^1 zT!mDgP?c1qWNAsbrO)0DTDnCKs zlX{;%%V#)}X|(7(I(JBFrD&l^CFsPio4DPBY%`NY;reJlzv&g$ZK?|2ovg8?3wAss z9zOrg>0=Y3=uuqBUBhgtgz@!KlWbASGcvU^=f8WMmIu=FS-B{8&PRi4SziS@G|MNw zl)TU~PpbV~)~>j9S)6oPNDRfXy4$V7)wFnJ$?2yP9j@&Zkr!lDqYSsi*X|pkp~l=3 zOa3IDLqkt*!O|nO5wV}Y{^Kqpz61OsUP+=@INg)jsdU77YCcZ-==Y)%y$sRSY-NSH zMJrZ(`_{lVNrp3Fv|#aA9Mc2LY9s^a^ry7)18?lcyV>3=jBmvFs8KVE^Pwe-e%S1> zY&zxHB~T$p>Xe8P&K_9>hGFljgN0UnrQLAA=IN5v$ z$>bDgMn9g^K;`{`SEWk>q%Fe6-w-R{yZo23KdRt9yQP0gzNuW^SMlnlP-;}{G*AbTqmWPQXysCcW`BGDN!1Hz0y8Ef%2EIqn zDT=uxw)x>N%~7|trt$}fA>Jm=krX9!@3AE!Bg%fkG_aQLApF5gQ&=Xrg00}oCOc$-|5V6THbOxJ(%^pcqeQ`4C!5J@n2ai zcA!IvpeXQWd0YSFSjA^tG+BjAEGQpGwf(5yo@ph{in2}Z z#T#xPk!~(Z-MV}EI^l(xMI4?^9kY&=Zn3g6Z0#F#x6&_E*Gnn&nndYLy+%WQ^px5w z)DL!*I4c-gy85>`5sW>=%X3ha{*Q66k8F64izV@d#jl&{z2kI|HXlCTv(;1qC)z`X z%a zk?Ci8Eal79^w+qeFZFZBVxWk6RxpCUUjOBV7yR8LlXFHX2C50*mS%aGSC_S`*$YQz zyqr1vez|ONThzdA3VmdIp4r8_PBoZKoOTR2B5l$>@n_!%C>UjkTIM$njt<6Kdr7k& zy30@B%!-?!e@k{ZW!Y)hPqf7T@P@@ScUe+33I(a3v*Nn&SMw%|U_CE2nY`xp|4{Yh z*4Tw@I(DY2*TpTT-jrq63k!Qnxc=QshLk2k&1kBss{=^;RGMp$lzGJ7`s^nUQhD`P z{=L+Jj$8E<28y}yZFq*Q}pP|&$Zp!&8d_|JK z)tM`NozaxfQ&5&!KfkvSv}PVJZ$sLIs7+I$*knA~_Fz7HKmK^ew{VYoT-ccOe>gyY z*FJya-FG>erV}Cyj_348FJG$u+3C}$lH6x_`Pzp{lkX0_o;B@vhlRO?=M$aX-jaD0 zYRqyApLj`!M~KZ!e){f9Rr!!hJom%1l#@m^eQPFVwK~P=X}vyJm)(>}E(hL+R@OhS zLjm%p#kKK0(bYoTh800e$D6DR%cMfb8-`*;G$<8DnUuE;Zun!}&zbW~WO5qzuH!w~ zz|1c@ORN2vdaKgU&^_v0j7-V|H{|QLExQ*=xk&%b=*h3AIal-?%04>g4|{A6P+HG1 zqEH;XoZuJuMcwjp*O3rY{NJ@jIWN@hCORp9o^BS%;hvqHk%R}478`1Qy2?i!?6v>K zsYCsJkx`y@@90G9(-~Q@SQ@m@jj1M;f)T+77pA)83BR3WLo6z%PR9fB{O?p z=n%0+S)&VKO@2ho&$A2(NcvKj{rZ)>@_A#!iB5>*n?h)F!>xz>b0HF%g3w;cUOB0x z(={*0On^}qvm>_uYr??}p=e+gsqMwW9Z486C>V27@{FKVE-jO&6|B<>k ze3Q=pUyl){@ZWb(F4S#MCObeYNb-0Kh3dxx z&gNk%@#<_n#fixI{hYS@Gvlx!9yT$J4xstQuq?ZsTzuhh;Xkq4I+_rgZLYr{Xq?W^ zv!-si8roP5*}z7OX{q%B{(^5{{n2pJ=z7%?sSl3A-M_3$^l?7D(n!*@gTEAfww zCf$2EmOr^Bz3Ki)Wj)e8vew-trF!Rc%+J@%eDmAyXRfj;&A+(77f2e8)AC*Yw z7!~$#-)*fXk)XFwK(f1eAp$0>W`ERki2I@|8a{0hdGyK3DIz9hATvNCC^Ybqtcg?Z zX9J^AFY1w)+`u83Gdd@yiM6CFQFy}#dS znd*X)P9@HeM0vZ>IiA=P9(ZIJVjwq(4mXJ*OK)CC;RFQ?sHm~P^KGyl{`oTthMOtYry>(R zE%|flP?JKWT;gaVc0;+fWJC}$fii!pDuz$_3vrj8tI)*&oiRaH)KPd z5@UNGE_3{|=uu_N!?>t&172h-8pO&JxG0tQz z1a^$iQB$6y$!NJ_uasz~=+p~$JST^di#b;oXp^~L<}8afF`D_L$rbTSIaPZupoq>( zsqBIu@_F_&R=wuA7cRgjlW~EQ{>&s2CPrr0?KFhp%IRW!t*o}t{*+xq0%j9yR zyT`TC&>0Q+8njHpEKXBZdnTU9?*|@`U5%N0(4pZuuQWt>g>2M)gCO*k)NJ1;oVD?? z*`fDyp?No|%FZt(T(!iYY!GfEBb8^V?7*q)r+$k zW~8dEZf53+z{kz~agxe)Mdns`RbXD#Q#Oo(ZwITbqHUBHkB_r&bg+?x+lb1{C7ylj z?P@zK&yRma(pz5Q`dEJRW-(KNqfqg)xg(Zc3a1ARjroBP0rn6QqU^(j(Piavp@((` z0{n^#p@%VXLZ9dela{_^A5>+zYotHdu{YG$AYt&_^;hV<X)8aPgl~8?`{QTDD;^4-aAbH$WwIbM`$9wU$B9GOZgw$DJl$OAAGR7 zX(n-$dC;S0B8f}nGJEoX;``}+QrLweqC&6^g|O5F*n>!{wY2Vf9|@_#jHje4m7TNm zP;{;)M@x$-BkhIbqWflyqz<3_HMBmo77D(of|CBBetZ23^E}+<49WgIT9X6J+QGxf zkI?+zaklwzQqt!o_X8TzmLp=tUT41M=Aj-Qx`JrcY~@|mz_$vYj%3Y3?hb*Nyxw0M z#En(=u=f%oPs~5HBtJ-?J$(D|_4h@_3ODs(fWWi7OB@FcMHbG?v^G9OLh&Txqo=Hj z-m}3VBO|GVkipgJu3_Ha0LMIy!s{qZ>Dkqg!X)Y@-$llg2rb9?Qp0$4igWX3Q4|-)?_Z|M= zkVl$P4-*mHua)o56Qm%WjjPfxuH@nSgt^_ZIX^t6_CC4&msxy}oT-Oa(39UG;1q-@ zsui9T4p=)ugWt=k23or02ySrT^WAXqoEo z0%1z?YY(epkL;A2El~C){VsL6E+PL(lW@1O%?oNb!|C{PXCH0+Okn^b8UxSniyCm}G zTZ1p%Jk8Ns#7=@D$hJN!&yYg72>lD&j%V~~n| zjv?_6>}R(0;ED|6GuLx=p5? z!lyt<9r}HBM6N6uqc}nZBTb?ID!~&ovyb?;IJFh_%ugN*jXjT8cH_}3mT4*cqz?rU z9M6k}dC|4S80f5NEF5n*p(bN{BKh6XSo!PXblj8qw7b>$Da6-_8H1nr;K*_<>H1&U zz1y@4;|TN;WjG(>6<(v25x|1fGZxL)$=*F8d_Omr3FVirswx{@j#*QhPJ@b|`+5?{ zJ{c*xxR8})g^xGHburrcj`yEsHPpioHN#Gp$V8`tqm-*C?7_-b(84^qyVzjYmSAu4Kly_Hs zw58v;A1(NSJp2l7c$9qTa@LmP$d9-C@qO&i-tLPIO^yEbpy3*4Cqh`uIfmY=eGQvs za^v?DSG}0e^C@Lq_k(NF^%gm+(dXe7(bB0ogM@De)$Xn%<^iyrvu(9qLo)?`}t^lkC&MY5b*&CvO#g&Z{{k5Qu! zT^Sdi>%6+tW2B;(pxQi_D3@K|c4<%3Yol2H4L_Hb3dLkaMjH`J`|jP`$BQ1mzmUU_ zocyjQ@1!?OgzK{mS||E7Nzf2bSQ1(T(02c zZO6u?#o^mO8ggZh$Yt@sx!ziwOn+if?Sf`D+iGVdUWGFfb1CRUU)95SGezqk4XjB0 zB(9UAbG=Z|{Y{@%lXu-WUP5Friyk?&ZsGV(xA_DbSzb-}o-i}nM$1)?_c_g+!%cOg zSoNLfQ9wZ$Tt6O}EgntxD))2F%XpnMv{&1W+cJFvXDpU5tM!Nk?kKyaC5Dt)v6<;x zZ%&6E4f7`bsKNTvW5`kOgFikfq&;|JDaFr?FYMJ8ZG${9wU4gPNx77Z*ztuFkK_`l!_UmPhPq zmMxE72*_X|9TflmMk)~I%|w${Ctv>O_@8anbh@sCldB$1cKd>A*%gjqs%%9=^IfiC zDuujzI)=Ipg~KwZV|<3pY(-#JMhm1Dk3)5vFQ@=MRs{%e0A zr^RRj*?V&GEOqseqi+|j6Es8p%Ldy`il*-<} zdqffb;!p!PfBMgFfa-wJeOu9+MV!|9yh#SRcTL}a|ByMzNmw6ES&?j@u=(Kd$y3M6 zy2g8z+9tncEE<@py9v?W=zXiVFsEH>dlDCJIe8r6cIWw!_P#}JguSe(u!$kPPGQL6 z4=L-7NdY2)&8d7huEU#(uu=~WWLMS+!(-ugr8(SoT3G8PO7Ryuznb4oGWq=}GKeJX z#u!N?c@~$NzxsV`ONnqbqUk=7EysyV(#%;~Hnap1QR9klwQm@>2oqOw~mpgX3RZ7cT|7h1jp;+yDTB{lQb-i>T+`6ZmA7A}DNA8WR54$`_^Us%{~E-;8|5 zOj^Ra#^;^&pGvWEO`JPt^h*<5s{Hw_*`ME|1^aHW2!AOxN%C0Ghjp%eR?2bLnh)Z{ z__7%2!g}wU<+0F(Tjg)-CCL*D>m@xON+xUdBJ5h}`jMZ-m(R^NKzl|eRc4yS>}j2e zgWvpvE!nh5mVmTPZ4BQ$jFwa}3YRR5$*RrrXKW?R8@JGmgr`PWn#0uynlhOyk_Zsw z(4j9G2{Zh@HLH;spwK|Khh-zSkRl&n{LH5T&)j}ps~gi_bvB%Z-H$@z;7waI^=G06 zj(b9RlR752^;anit`ZiA5Hfu(zaFYr;IC(c6Ru5EqDB;ML=>)u9j}cZuZxv+wz9M2cC;Mk@l3c< zb~B(k1pLmti`1Z_7IZV9qw(Gb$d_dm@hcmcWtq(x)EhhQ>he(0+pj*p&QH*4AF0R^ zrufFD65=kHcu;luK6{}%8vX6yrHd%xR@ol@zNFJaPkN0=MGe9CrBjKPx%UPi(O{34 zo|Y3}@X}a+cyTgHMMAQi85*-}mRzNW-Slbg-WM8dbuIx(JhWIA^bzx>a!e;qW~aM! zXLLU>h-?(Z%!<20>bdBRJ?es9s@sK<;V>qN9mwWcOMBBdnhYNmM+jtzwb%d1+*ovWMstcy#;yx(Dvq%cLJuZGnF`5PgK;X|U zn@f2Ba|ksEXh9Rv5P#xfz+lGpj1cAO;sQgkpq6f_z47yBFu)9Ss(XR?RN}Ecr}`7x zua}4NsmaI)xJh765ZZzA5f~J-=mDF1W^pkDWP=b+J7`$q!b~t^$bh+kNIU{JnF*!{ zUcGvSG%nUWS&vYTqSt#q1N3_|E<5_8^-CiuBjHUh_FThKW5#WL#)ac7XiUtQL?SEv z?EFD=L6m$FaqKc2Gt=ao_c)c zq5EPQLz4?Mle!1a6ObF(h9=rTm{nqhOdOal7(kOk)HCYpB%!I85@x2LQ!y+o?7bGl z?4s4U8rHMhXfhsKh~g(Q4heb|x@VvnGy`2-Fkxz!>+@j+ZFhfP85rA>lapV6U>vlD zQ&4aW8Rhhr+7(uU(FyX;`^fvjCf$TmsyD zj8+nU4%=Q73%2DkU4_05F=?wmnylW%E{PJAr{W4tEBaxyC=WY)1zg;Ih0EwPn#0Cz z{v_xhdD>W7g)TF-nM#-urqTE_^Wd5>n-i?lnu}T8sKqri!wn?&yQc9gqT(R(&@tRzM8}ovVM)V`T)&dlmpbN9h2RK*@J6*&c zk=@T5$#nIw^XDoQS@H&+9AacU{vkx~E`|PF|Jc}AQzeZHcLinL@NZUv%P?a38>N1H zXec&*Pl`WRfaRYAEMZY1&Uh!4_TgBV3)N`Byf4(np3qhk{cG^xF~h9z^dg#k)NB`^Vr$fttHQNgaA>Afv8 zWc29!R;Dc)k_{IS#wN8RW=NM95H=FYrAsmJ;R68nmE)ZiV0D)wM6qJO8>#fn zHZY}<_3M9*K|Uiwp&^8GV`iX>Q2reOMkes_|5f{oPfBVd#Gyw2dwbfI{K#ktb05j$0cbxVk&jDB!C`hA z#6zCv--;5{*ku)v>?zyd5@N1&lFc8FRnb<>$F?Rb1QK}%oL*q$A_V^CoLB!;J^ju0 zH|GrTu6AYI<4-{|%Y~t0o~z7dHC?M0M6Ldl3jR-}Rqx{8`;Ooz`FGQ!5SASTtZG>r zD;WQ~B#g`fA`>G)?`bn@g5TCM{EzwuDh}#zFphJ>^q(>xq^9zS|9%y<&x15UzN@LS zPuGm}^sGxF~Nd4E%TbLwf zOBMQrJPq*h2>$KcFk=EkZ|l#WU={6s=8MSvvc$hPzU%Je^yCTEU=Iea7P6iRKN#w1 zOtrLpuK_u}m3qF5$TLAEj#&7uQ7Zrau+tT`3e{C3+-f7SY)r_v{A*lDYf3(odHHRi zEb=+X1!J$H<^25u@}x9EsQ>>D%LHEg?9qDeE~9~hCZsQiZ5ZWosd)5IjEs!%brhAA zSp@|8-`@ti%XT{r6L>W6kLJopj1`=nORy*gV{69U_c!kI6KMUk1-I3x_QQ&({kR3; z&GI+WzP~0a9k1L5*@Cxs(*dqPz$neIx3>q1&TTKnT{8!M@o?s<58iW!ul?ic(#4>V z5V&eYs}@oB!^6Xi8sF}jMkKWnT|Nm32}C>{*mU_Nr!Za94^tSUV`Et$gEac-VeDNW z`wcf~4eBYG(_L~HJ-~u_JgCHhS5u>T407H&JF9Wb3Ou~o-v)2&gN{AdBN!+bv>Bi| zy+5>1-O5h`@_i#An`hEcMFGeq8u0p|!2mb_$9cyL5>E&~&i5VEs4H^GN_|x3fR(mk7YqI2fmkDYF|(fMMOD9=HY- zAijM8-clzFgWrU&_d?A1O|bT0M$P!UfnO?0N+JEWfG%t6?~kXTq;y_K#Q~^`zC8b+ zdNl3mAotE|=><>{=l>4sTTMgYm61_VDZRf88*>9?B%o4fXAN0ElLWwPP_{puYgPqXmGnF$3QZ=1pR4 zwMKgT$)KN(y}+cgK?jYgyR>a$BAv&uB8t1ax0fAs)DS&M06jWPR>`5_Kzz&99@NEd z(S$IAXChj=?)KSeFE}(b5mYCJ-WQ07zAJoRydmv#6z4weQ#pPFz*Q1x>fi0PSRXG3 z;kOsS75g;mfVa4_4{GCKYHX|qh0nR?6B}7sS-(s;Hultyto~^m8A;)}UsKk#H{Tx3 z3IkT6T(Cdr4@Vl$Zs6uk)qlf*$9EIvQ=DB~SQ^icxOGJ=yC|-?EU37q+vvdmdo;Z* zzrFwAUVd0&B01;PtBYN9&liyx5amT~&odRG@Ewg=%YWZy6e*L~uUiu%{I+lg+|3j85g@&97$D5Cu3N?pW6C zjoT)#Ag%An`3#7kgP$oTvL4q4;yG*lK#d3aTQf7WR2e^MkTmRfhRuyAw-woe;xtfO z+hCpc@a4p$u6putaQMUSx=c1t$#Xd47_Aft<5P4~DKH`ll1P(2hnFGJP+CU|obH+! zSv0N`{?mVY8L&byI|BGIrJt}v0zvf#Azp!O`z%bUS(Q&wtmM~%b}3>Z+VH#W&- zL_qC7rH2j+=X{Keb?(y}YB5gTxdvkPAZbN+D#&on0vFN9g9XI`vl!=xy?*>K#|-aH zo`)*^o`TQ&*bM+F1DKr;fq{W_L~u;I%l#NIZl(-_rLY^XIjenlfh@ix^x{k;V*c6byl0UAiXRV3UP&Z+>GOs=LGELDV#!98@3 zCo(Jy;o`vcSoQlieXrrhmICTnC~(z4hSDDjF93-eI<9(a&g(}_MP&{oW0(lxrQ$WP z(0+*Tr}edi+Qd5m?8_{O#VcX3u&Jds*#qGF67cI}fzOCYi=bg(kG5zhhDSsonvnYq z5F=O*yd^}GIC%6++d`h}$HK%lV&Ng>BgEwxb>@Hz)eiJJu*v{ivx&Ax$G}*1vjhGu zNF3`>d3$?T?H~-|&`?)TXNT-C(#Ce@)yZH5ESy%L zjRTL9x8iba`QwyTMPE8%bW-u!r*&PT{c6 z=846OpG?c&5poJ^j>s{AN4eAmYv2O|rps&#R#6WWK%a`@LTAMv(BuAY-M50^6~cku zR&OuNzPQs3Cm=4$1twhNJLlz?D3xPRLHPtRWl1oHaz|DwuXqCya;*1q{|*QoP56=T zI`(}UjDQ8Wqmy3s#&=FmPHY%3cK}sQmkjRAJvvOyfxlKPokSi418#v`S2KU`z}77W zwA5Po3C2nugnrpei#%E`3R8c_nw6Dh0VC`|aqa7l@&xDe;KO|R^5yA@_`&j~i$=E` z6POkyONfmNgmBxERy!ErO}B%HS}dF=&RS{kdpu0zW#`=ntMCP+fvWZqLWQ%N+ZE77 zWP-`JE3C>f1uk@{Kof8Qrlt#UsX>{mrS_4jO(~z0R0g;g3BY8fTTe+%RRPlv^FM`; zL50>m0g>!9s(liDnT;(02Hr`)6Dl=rMvq+0<8>R#NJ>&f1pbicoRE+p|72}c4wl`` z%^&sEK3(quXo1pR?R|J{33f5ZS3**fg^*MbJc6LD-f2L+ zvcQ3ajMySVm1^lC(cnvM|FtW=D?H*_PmY>KMPvyrzZ+ z@9o2`k^^DYL*O$+pBS8F=xS>#0=xJ&Jb5_a@x&i5ll&UYBZp6%{Y;$Zvhu+ToCyn< z=({W~{=%rvBVH|)KLGruc%Zhs+S6q>c0UEadnbh1+{vPt$VzuUPe=Rf(!k?(0miaC zE;B$CSos=Hu8lN!)1?BeI1UChcR=X)o;ab}?bq4DnP{2OFRoeQp=tbb+g-j}MA)@> zof~@Uci`A1ha&>O$Y1=?Fgn57M8zC0_p@FDO6J`1vI?l&eF5&hIfeFk+PkrogaplB zu1rC3(A-51K%O@vtK1h|;Xz-Plzb_6{W=*A4$gN-0`CCeQFm-u2V6&hNc@7%3ukwC zd0`-`mIMr3G7=KmA0G3B`DYIbc-{I>fhOf_Utd?(2~)D2 zxwlfHVn%3AZ{spUBzYOt8GwPqbupj$ptfc++a&|kotb@(cJs>az~FQI7mLgM{C&%Q z9^%42F~k1MLF}#;|9JIT8U^SXIR6cV{zpvof9J#0Xt2L#pew9o33KhwQc|dCLYg5_ zZSPDEUN8zg#q8gqp`jyHy+CRduo>it9Jj<9K3KE)EiNns7T=$eTW55f053QNyff(a z4Td3HZ}7$Rs+^d0pTnRpcpmeh2PLbdgbf4UHqH|5009EO!tg|LxQ*`h6!<942U}*5 zuUaSn4=3DI`y4#dSr~{@kdq6rC&>Q7@(1=n%ftk| zOZDX36R@Y?2N;}4-ciWzPhHo8>jly^%;=l-yK3i@l~M3?4||;_B~c*Z3J_^qf&IG; zuWuFs>VuuF76D6z3$$0q##1YzM9w57tiRuu6qM$IKuAjZ{KU10hm8&U+KYnrOK;!^ zGy{^yzoJao|gd8yAN(HX)Ur9+xe*1ADph&y^`N_Es52>}I z0|%h@2=O3Vx|RLuH7xJ7Q31fxw=_4S0V!^I>=Qe*N$LJ<0EL5d93Ucq!F91bKih9a zf;o=lF0Os?Cu=kSo8AI9XCC+?vM^>`UU3i6xs3%+#QbvvCE_6D@E9|pm~ zD-;tjo-ebq1_Bu#UZ)ax5S<_%aRabB3TH>FK*Ziqhn3#6UTg7%%mtJT{+<9ElQ-cS zJ5x-2zYJA^NvPZXe(N$ba{$PPr9*Zo*r>t#1$;rE89-QsKk@`Dcw2LG08kEIBgfc$ zM=a=MnC{F)^11vCl9XZ?zSN(?fqfHPw=V!}QvuM2ext8ceD}``6h&G<~beU64V_orX#nrHeMbH1Ja40 zIZt+UME1Xd z)d`1I^-)`!B6{XH3Qq8df9FDJ}~NCJvCpu>djOh!u=uTYwPV(71^J z%6oU26iwqn)A8l%>gpWGBrFtG{$K&5!~&wN{HGy^#06N2r2zc>4m@ahbm8EIkAa9~ z`MQFk8W;>rR5koLV=YUAPJ+zvkEA9Ch3_Yb7objo;6Gpt z+B~{9Gf$ff<)4vpKe~g5lT!}X9D+!%+is^fs)i;SSi1YZaPl7AjFGuxN`9KvkME_s}Hn8 z<+D)!Hd_4@5gJ*&C{2zeuAA!W?SNW<3*GopZ}`BFn3x#yL|_i+CX!{|?STyHED+}> zxgidNxoyztXm2+M-5G=h3UVKeu>WUUiDiW7v`eh}S&SNeFG6OZ1CFP@3VTDybW8~~ z-x-B5b_Z5^$H}Vg!6g(ow&l-`Bgh(9K7I-5BqjlYl6LR5_X%1DAN74ks3|~u zfEX5lXFb+KA1IfySCDRCcrYLe9`(w#AOf8G6EH9ya1C(M1t5~VkiA4gc|ajJEq_oxF%v5C-0Yw~vqW(zlBMQOWBP5fBi7;JFEo`_ANh!UvgPzmRK1 zs5AibFo%;Cxw?EwEAZ)nO@o3A9I6S-HbR4^qcg~ZF@1aBXvC+bML=psJ3m(hfgp)G zOoB+dQ}Q2(FM#rez(OEJh(_bdWQ&(G0iiwUjAiEJ1mTN8=InmqB`qZ4QdGoT?0p^k z{ym4?WECk00u-0pLwyd3V!-I(Lmd5i_t(HSEO8yLHHaC^A@75{L5N=h!OSUK{^0W( zkz5Bl#slpq0I>t}LYs^Tl)&b-hSyyfdpipkjSg8cs@?~)p$O{+cu|CK<{=Q0;)WwG zxK~(_w4fIb0`I~5KfH?DAMhF10ciWHKWsfnoS~N+M1$=_NUpHqXsijr&Xn+kV zpaFeFWMxV#PaCu{i4eVM$v^pMGcBnRDa(S3a2YrzT42x_#}gO6`Oj2je;Pi@|n#Hm+iO?pY%JIwvZ zQye6KNI4Tu2G0uNmV0beFM-lmB&1-N1q4XJniiG%+`UU`3~>-5Ab}7G;kNtAZFLc~ zd%n6Y`Q+o>wJ~yl0xkT|6?V_AtaMLitwEas9i%p(1Vs`{AE1yYi!=m-?_#>YF{QMw z{z_7RK9&sc#>dekW<|I{zL-l=s!;9~f4Xy7_c=rWNDKmL{SQ-{u#ptAG~%F-x2WKQ z98(a<&hchr;?|ZE1O{R0>GVjXF$UQlU73g980KbY<-l+J0+G`fNYYgWT(l`qLxL*+ zq>w{S71sV2SVZBfxhz52Qn)#AFj|1%Fp?HT!Y@RU24uxt>|ZfpeJ$nDulE$bKuCB6 zaJNVR0mdtpLB_Yv;1tqbN*JPD;_e#^v|>(7&W|5o2Go2GJRK#B%dkyk!EaWDSQ@a? zOz>nbLsfYJu6k)avxSL79=F=_57#g79!oxbVujQik~(IB;C*NX`~XO9roa`xGdSiZ zLjUsh>*Z{vXcmBgD6%ES11JUwfgu9RpWIK@HO>>#umWhCziAW7Fnu6XzxD}lh2wM#SnknrC}X(y$uYq!mmMT?31VVmDnzp7 z5NHoV+~?%vgi3L9&;{~7@KAR*rY45p3VQ&(qyfS?!*sc8yy|p&Ymt<{!!cC=;YtkB&Z}IwU&scOc-AKf9S&lzj)qTXJ)j{Pf_sW N<_&elB6-ti{~zoR89e|1 diff --git a/class02/root_finding.tex b/class02/root_finding.tex deleted file mode 100644 index c72fe70..0000000 --- a/class02/root_finding.tex +++ /dev/null @@ -1,251 +0,0 @@ - -\section{Root-Finding} - - -% Slide 1 — Big picture -\begin{frame}{Root-Finding and Fixed Points (Big Picture)} -\begin{itemize}\setlength\itemsep{0.6em} - \item \textbf{Root-finding:} given $f:\mathbb{R}^n\!\to\!\mathbb{R}^n$, find $x^\star$ with $f(x^\star)=0$ (e.g., steady states, nonlinear equations). - \item \textbf{Fixed point:} $x^\star$ is a fixed point of $g$ if $g(x^\star)=x^\star$ (discrete-time equilibrium). - \item \textbf{Bridge:} pick $g(x)=x-\alpha f(x)$ ($\alpha>0$) so that - \[ - f(x^\star)=0 \iff g(x^\star)=x^\star. - \] - \item \textbf{Mindset:} start $x_0$ and iterate $x_{k+1}=g(x_k)$ until nothing changes. -\end{itemize} -\end{frame} - -% Slide 2 — Convergence intuition -\begin{frame}{When Does Fixed-Point Iteration Converge?} -\begin{itemize}\setlength\itemsep{0.6em} - \item Near $x^\star$, $g$ behaves like its Jacobian $J_g(x^\star)$ (linearization). - \item \textbf{Contraction test:} scalar: $|g'(x^\star)|<1$; vector: spectral radius $\rho(J_g(x^\star))<1$. - \item Smaller contraction $\Rightarrow$ faster (linear) convergence; $\ge 1 \Rightarrow$ divergence/oscillations. - \item Converges only from within the \emph{basin of attraction} (good initial guess matters). -\end{itemize} -\end{frame} - -% Slide 3 — Minimal recipe & stopping -\begin{frame}{Fixed-Point Iteration: Minimal Recipe} -\begin{itemize}\setlength\itemsep{0.6em} - \item Choose $g$ (often $g(x)=x-\alpha f(x)$) and an initial guess $x_0$. - \item Loop: \quad $x_{k+1}\leftarrow g(x_k)$. - \item Stop when residual $\|f(x_{k+1})\|$ is small, or step $\|x_{k+1}-x_k\|$ is small, or max iterations reached. - \item Report both: residual and step size (helps diagnose false convergence). -\end{itemize} -\end{frame} - -% Slide 4 — Tuning & practical tips -\begin{frame}{Tuning and Practical Tips} -\begin{itemize}\setlength\itemsep{0.6em} - \item \textbf{Step size $\alpha$:} too small $\Rightarrow$ slow; too large $\Rightarrow$ divergence/oscillation. Start modest; adjust cautiously. - \item \textbf{Damping:} $x_{k+1}\!\leftarrow\! (1-\beta)x_k+\beta g(x_k)$ with $0<\beta\le 1$ to stabilize. - \item \textbf{If stalled:} try a better $g$ (rescale/precondition $f$) or a better initial guess. - \item \textbf{Optimization link:} gradient descent is FPI on $\nabla F$: $g(x)=x-\eta\nabla F(x)$ solves $\nabla F(x^\star)=0$. - \item \textbf{When too slow:} use (quasi-)Newton methods for faster local convergence (needs derivatives/linear solves). -\end{itemize} -\end{frame} - - - - - - - -% ---- Newton's Method (animated in 4 parts) ---- -\begin{frame}{Newton's Method} - -\uncover<1->{ -\underline{\textbf{TLDR}}: Instead of solving for $f(x)=0$, solve a linear system from a linear approximation of $f(x)$. -} - -\vspace{0.6em} - -\uncover<2->{ -Fit a linear approximation to $f(x)$:\quad -$f(x+\Delta x) \approx f(x) + \frac{\partial f}{\partial x}\,\Delta x$ -} - -\vspace{0.6em} - -\uncover<3->{ -Set the approximation to zero and solve for $\Delta x$: -\[ -f(x) + \frac{\partial f}{\partial x}\,\Delta x = 0 -\quad \Rightarrow \quad -\Delta x = -\left(\frac{\partial f}{\partial x}\right)^{-1} f(x) -\] -} - -\vspace{0.6em} - -\uncover<4->{ -Apply the correction and iterate: -\[ -x \leftarrow x + \Delta x -\] -\smallskip -Repeat until convergence. -} - -\end{frame} - - -\begin{frame}{Example: Backward Euler} -Last time: Implicit dynamics model (nonlinear function of current state and future state) -$$ -f(x_{n+1}, x_n, u_n) = 0 -$$ -Implicit Euler: this time we have $x_{n+1}$ on the right; i.e evaluate $f$ at future time. -$$ -x_{n+1} = x_n + h f(x_{n+1}) -$$ - -(Evaluate $f$ at future time) - -$$ -\Rightarrow f(x_{n+1}, x_n, u_n) = x_{n+1} - x_n - h f(x_{n+1}) = 0 -$$ -Solve root finding problem for $x_{n+1}$ -\begin{itemize} - \item Very fast convergence with Newton (quadratic) and can get machine precision. - \item Most expensive part is solving a linear system $O(n^3)$ - \item Can improve complexity by taking advantage of problem structure/sparsity. -\end{itemize} -\end{frame} - - -\begin{frame}{Move to Julia Code} -\begin{center} - \textbf{Quick Demo of Julia Notebook: part1\_root\_finding.ipynb} -\end{center} -\end{frame} - - -\begin{frame}[t]{Minimization} -\[ -\min_x f(x), \quad f:\mathbb{R}^n \to \mathbb{R} -\] -If $f$ is smooth, $\frac{\partial f}{\partial x}(x^*) = 0$ at a local minimum.\\ -Hence, now we have a root-finding problem $\nabla f(x) = 0$ $\Rightarrow$ Apply Newton! - -\medskip - -% --- second part appears on next advance, first part stays visible --- -\only<2->{ - -\[ -\nabla f(x+\Delta x) \approx \nabla f(x) + \frac{\partial}{\partial x}(\nabla f(x))\Delta x = 0 -\quad\Rightarrow\quad -\Delta x = -(\nabla^2 f(x))^{-1}\nabla f(x) -\] - -\[ -x \leftarrow x + \Delta x -\] - -Repeat this step until convergence; Intuition to have about Newton: -\begin{itemize} - \item Fitting a quadratic approximation to $f(x)$; Exactly minimize approximation -\end{itemize} -} -\end{frame} - - -\begin{frame}{Move to Julia Code} -\begin{center} - \textbf{Quick Demo of Julia Notebook: part1\_minimization.ipynb} -\end{center} -\end{frame} - - - -\begin{frame}{Take-away Messages on Newton} -Newton is a local root-finding method. Will converge to the closest fixed point -to the initial guess (min, max, saddle). - -\bigskip -\textbf{Sufficient Conditions} -\begin{itemize} - \item $\nabla f = 0$: “first-order necessary condition” for a minimum. - Not a sufficient condition. - \item Let’s look at scalar case: $\Delta x = -\frac{1}{\nabla^2 f}\nabla f$ -\end{itemize} - -\medskip -where: negative corresponds to “descent”, $\nabla f$ corresponds to the gradient -and $\nabla^2 f$ acts as the “leading rate” / “step size”. -\end{frame} - - -\begin{frame}{Take-away Messages on Newton (cont’d)} -\[ -\nabla^2 f > 0 \quad \Rightarrow \quad \text{descent (minimization)} -\qquad -\nabla^2 f < 0 \quad \Rightarrow \quad \text{ascent (maximization)} -\] - -\begin{itemize} - \item In $\mathbb{R}^n$, if $\nabla^2 f \succeq 0$ (positive definite) $\Rightarrow$ descent - \item If $\nabla^2 f > 0$ everywhere $\Rightarrow f(x)$ is strongly convex → Can always solve with Newton - \item Usually not the case for hard/nonlinear problems -\end{itemize} -\end{frame} - -\begin{frame}{Regularization: Ensuring Local Minimization} - -\uncover<1->{\textbf{Practical solution to make sure we always minimize:}} - -\vspace{0.6em} - -\uncover<2->{ -If $H$ ($H \leftarrow \nabla^2 f$) not positive definite, we just make it so with regularization. - -\medskip - -While $H \not\succeq 0$: -\[ -H \leftarrow H + \beta I \quad (\beta > 0 \ \text{scalar hyperparameter}) -\] -} - -\uncover<3->{ -Then do newton step as usual. I.e: -\[ -x \leftarrow x + \Delta x = x - H^{-1}\nabla f -\] - -\begin{itemize} - \item also called “damped Newton” (shrinks steps) - \item Guarantees descent - \item Regularization makes sure we minimize, but what about over-shooting? -\end{itemize} -} - -\end{frame} - - -\begin{frame}{Line Search: Mitigating overshooting in Newton} -\begin{itemize} - \item<1-> Often $\Delta x$ step from Newton overshoots the minimum. - \item<1-> To fix this, check $f(x + \alpha \Delta x)$ and “back track” until we get a “good” reduction. - \item<1-> Many strategies: all differ in definition of good. - \item<2-> A simple + effective one is \textbf{Armijo Rule}: -\end{itemize} - -\uncover<2->{ -Start with $\alpha=1$ as our step length and have tolerance $b$ as a hyper-parameter. -\[ -\text{while } f(x + \alpha \Delta x) > f(x) + b\,\alpha\, \nabla f(x)^{\!T} \Delta x -\quad\Longrightarrow\quad -\alpha \leftarrow c\,\alpha -\quad (\text{scalar } 0{ -{\footnotesize -The intuition: $\alpha\, \nabla f(x)^{\!T} \Delta x$ is the predicted change in $f$ from a first-order Taylor expansion. Armijo checks that the \emph{actual} decrease in $f$ matches this first-order prediction within tolerance $b$. -} -} -\end{frame}

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 N<_&elB6-ti{~zoR89e|1 diff --git a/class02/figures/tri_paper.png b/class02/figures/tri_paper.png deleted file mode 100644 index 3cedfd792270cb06484295d9118b9c00c34119ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74894 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 via a barrier; follow the \emph{central path} with primal--dual Newton. -\end{enumerate} - -\textbf{Rule of thumb.} Penalty is simplest; ALM is a strong default for medium accuracy; PDIP is the gold standard for convex QPs and very robust with Newton. -\end{frame} - - - - -\begin{frame}{Inequality-Constrained Minimization} -\textbf{Problem Setup:} -$$ -\min f(x) \quad \text{s.t. } c(x) \geq 0 -$$ -\textbf{KKT conditions:} - -$$ -\nabla f - \left(\frac{\partial c(x)}{\partial x}\right)^T \lambda = 0 \quad \text{(stationarity)} -$$ - -$$ -c(x) \geq 0 \quad \text{(primal feasibility)} \quad \quad \quad \lambda \geq 0 \quad \text{(dual feasibility)} -$$ - -$$ -\lambda \circ c(x) = \lambda^T c(x) = 0 \quad \text{(complementarity)} -$$ - -\textbf{Unlike equality case, we can’t directly solve KKT conditions with Newton! Why?} - - -\end{frame} - - -\begin{frame}{Lots of solution methods to use: Active Set Method} -\underline{\textbf{Active Set Method}} -\begin{itemize} - \item High level idea: Guess which inequalities are redundant at optimality and throw them away. - \item Switch inequality constraints on/off in outer-loop and solve equality-constrained problem. - \item Works well if you can guess active set well ( common in MPC where good warm-starts are common). - \item Has really bad worst-time complexity. - \item Usually custom heuristics are used for specific problem classes/structure. - -\end{itemize} -\end{frame} - -% --- Slide 1: Idea & Algorithm --------------------------------------------- -\begin{frame}{Penalty Methods: Idea \& Algorithm} -\underline{\textbf{Penalty Method}}: Replace constraints with cost terms that penalize violation! -\[ -\min_x\; f(x) \;+\; \tfrac{\rho}{2}\,\big\|c^-(x)\big\|_2^2,\qquad -c^-(x):=\min(0,c(x))\ \text{(elementwise)}. -\] - -\textbf{Algorithm sketch.} -\begin{enumerate} -\item Start with a small $\rho>0$; minimize the penalized unconstrained objective. -\item Increase $\rho$ (e.g., $\times 10$) and warm start from previous $x$. -\item Stop when $c^-(x)$ is small enough. -\end{enumerate} -\end{frame} - - -% --- Slide 3: Quadratic penalty & why large $\rho$ is needed ---------------- -\begin{frame}{Quadratic penalty: need large $\rho$ for strong feasibility pressure} -\begin{columns}[T,onlytextwidth] -\column{0.45\textwidth} -\small -\textbf{Pros.} Dead simple; reuse unconstrained machinery (Grad/Newton + line search). \\ -\textbf{Cons.} Ill-conditioning as $\rho\to\infty$; struggles to reach high accuracy; multipliers are implicit. \\ -\textbf{Popular fix.} Estimate $\lambda$ (Augmented Lagrangian / ADMM) to converge with finite $\rho$. - -\vspace{0.6em} -\textbf{Takeaway.} The penalty outside the feasible set ($x<0$ here) is only quadratic, -so to make violations tiny you often must crank $\rho$ very large $\Rightarrow$ poor conditioning. - -\column{0.53\textwidth} -\centering -\includegraphics[width=\textwidth]{figures/quadratic_penalty.png} -\end{columns} -\end{frame} - - - - - - - -% ---- ALM ---- -\begin{frame}[t]{Augmented Lagrangian (ALM): fix penalty’s weaknesses} -\setbeamercovered{invisible} - -\uncover<1->{\textbf{Core idea.}\; Introduce multipliers $\lambda$ so we can keep $\rho$ moderate and still achieve accuracy.} - -\uncover<2->{\textbf{Lagrangian for equality case:}\; -$\displaystyle \mathcal{L}_\rho(x,\lambda)=f(x)+\lambda^{\!T}C(x)+\tfrac{\rho}{2}\|C(x)\|_2^2.$} - -\uncover<3->{\textbf{Outer loop.} -\begin{enumerate} - \item $x^{k+1}\approx \arg\min_x \mathcal{L}_\rho(x,\lambda^k)$ (unconstrained solve). - \item $\lambda^{k+1}=\lambda^k+\rho\,C(x^{k+1})$. -\end{enumerate} -} - -\uncover<4->{\textbf{Inequalities (sketch).}\; Apply to the \emph{hinge} $c^-(x)$ and keep $\lambda\ge 0$: -\[ -\mathcal{L}_\rho(x,\lambda)=f(x)-\lambda^{\!T}c(x)+\tfrac{\rho}{2}\|c^-(x)\|_2^2,\quad -\lambda^{k+1}=\max\!\big(0,\lambda^k-\rho\,c(x^{k+1})\big). -\] -} - -\uncover<5->{\textbf{Why it works.}\; Subproblems are better conditioned than pure penalty; $\lambda$ estimates improve the model; finite $\rho$ can reach high accuracy.} -\end{frame} - - - - -\begin{frame}{ALM in practice (optimization loop view)} -\textbf{Inner solver.} Use (damped) Newton or quasi-Newton on $\mathcal{L}_\rho(\cdot,\lambda^k)$ with Armijo/Wolfe line search. \\ -\textbf{Tuning.} -\begin{itemize} -\item Keep $\rho$ fixed or adapt slowly (increase if feasibility stalls). -\item Scale constraints; monitor $|C(x)|$ and stationarity. -\end{itemize} -\textbf{When to pick ALM.} -\begin{itemize} -\item Nonconvex NLPs where feasibility progress matters and you want robust globalization. -\item When medium accuracy is tolerable/fine, or as a precursor to a polished PDIP phase on a convex QP. -\end{itemize} - -\end{frame} - - - - - - - - - -\begin{frame}{Interior-Point / Barrier Methods} -\underline{\textbf{TLDR: }} Replace inequalities with barrier function in objective: - -$$ -\min f(x), \quad x \geq 0 \quad \to \quad \min f(x) - \rho \log(x) -$$ - -\begin{itemize} - \item Gold standard for convex problems. - \item Fast convergence with Newton and strong theoretical properties. - \item Used in IPOPT. -\end{itemize} -\end{frame} - - -% --- Slide 2: Barrier intuition (contrast) --------------------------------- -\begin{frame}{Barrier intuition issue: $-\log(x)$ blows up near the boundary} -\centering -\includegraphics[scale=0.5]{figures/log_barrier.png} - -{\small -For an inequality like $x\ge 0$, the log barrier $-\log(x)$ goes to $\infty$ as $x\to 0^+$, -creating a \emph{hard wall} at the boundary (contrast with quadratic penalties). -} -\end{frame} - - - -\begin{frame}{Primal-Dual Interior Point Method} -$$ -\min f(x) \quad \text{s.t. } x \geq 0 -$$ - -$$ -\to \min f(x) - \rho \log(x) -$$ - -$$ -\frac{\partial f}{\partial x} - \frac{\rho}{x} = 0 -$$ - -\begin{itemize} - \item This “primal” FON condition blows up as $x \to 0$. - \item We can fix this with the “primal-dual trick.” -\end{itemize} -\end{frame} - - -\begin{frame}{The Primal-Dual Trick for IPM} -Introduce new variable $\lambda = \frac{\rho}{x} \quad \Rightarrow \quad x \lambda = \rho$. - -$$ -\begin{cases} -\nabla f - \lambda = 0 \\ -x \lambda = \rho -\end{cases} -$$ - - -\begin{itemize} - \item This can actually be viewed as a relaxed complementarity slackness from KKT! - \item Converges to exact KKT solution as $\rho \to 0$. - \item We lower $\rho$ gradually as solver converges (from $\rho \sim 1$ to $\rho \sim 10^{-6}$). - \item Note: we still need to enforce $x \geq 0$ and $\lambda \geq 0$ (with line search). -\end{itemize} -\textbf{We will use another approach from 2022 from a researcher at TRI that developped an even cooler trick.} - -\end{frame} - -\begin{frame}{Log-Domain Interior-Point Method} -\begin{center} - \includegraphics[scale=0.4]{figures/tri_paper.png} -\end{center} -\end{frame} - - -\begin{frame}{Log-Domain Interior-Point Method} -\textbf{More general constraint case}: \quad \quad \quad \quad \quad $\min f(x) \quad \text{s.t. } c(x) \geq 0$ - -\textbf{Simplify by introducing a “slack variable”:} - -$$ -\min_{x,s} f(x) \quad \text{s.t. } c(x) - s = 0, \; s \geq 0 -$$ - -$$ -\to \min_{x,s} f(x) - \rho \log(s) \quad \text{s.t. } c(x) - s = 0 -$$ - -\textbf{ Write out Lagrangian:} \quad \quad \quad $L(x,s,\lambda) = f(x) - \rho \log(s) - \lambda^T(c(x)-s)$ -\end{frame} - - -\begin{frame}{Log-Domain Interior-Point Method} -\textbf{Apply F.O.N.C to Lagrangian from last slide:} - -$$ -\nabla_x L = \nabla f - \left(\frac{\partial c}{\partial x}\right)^T \lambda = 0 -$$ - -$$ -\nabla_s L = \frac{\rho}{s} + \lambda = 0 \quad \Rightarrow \quad s \lambda = \rho -$$ - -$$ -\nabla_\lambda L = s - c(x) = 0 -$$ - -This second equation has a really nice interpretation: relaxed complementarity slackness - - -\end{frame} - - - - - -\begin{frame}{Log-Domain Interior-Point Method} - -\textbf{Change of variables (elementwise):} -\[ -\boxed{\ \rho := s \circ \lambda,\qquad -\sigma := \tfrac{1}{2}\big(\log s - \log \lambda\big)\ } -\quad\Longleftrightarrow\quad -\boxed{\ s = \sqrt{\rho}\, \circ e^{\sigma},\quad -\lambda = \sqrt{\rho}\, \circ e^{-\sigma}\ } -\] -{\footnotesize -Here \(\circ\) is the Hadamard (elementwise) product; \(s,\lambda,\rho,\sigma\in\mathbb{R}^m\) with \(s>0,\lambda>0\). -By construction \(s\ge 0,\ \lambda\ge 0\) and \(\rho = s\circ\lambda\) (the relaxed complementarity) holds.} - -\vspace{0.6em} - -\textbf{KKT (first-order) residuals with inequality \(c(x) - s = 0\):} -\[ -r_x(x,\sigma) := \nabla f(x) - J(x)^{\!T}\lambda(\sigma), -\qquad -r_c(x,\sigma) := c(x) - s(\sigma) = 0, -\] -where \(J(x) := \frac{\partial c}{\partial x}(x)\), -\(s(\sigma)=\sqrt{\rho}\circ e^{\sigma}\), -\(\lambda(\sigma)=\sqrt{\rho}\circ e^{-\sigma}\). - -\end{frame} - -\begin{frame}{Log-Domain Interior-Point Method} - -\textbf{(Gauss-)Newton step in \((x,\sigma)\) for fixed \(\rho\):} -\[ -\begin{bmatrix} -H & J^{\!T}\Lambda \\[2pt] -J & -S -\end{bmatrix} -\begin{bmatrix} -\delta x \\ \delta \sigma -\end{bmatrix} -= -- -\begin{bmatrix} -r_x \\ r_c -\end{bmatrix} -\quad\text{with}\quad -S:=\operatorname{diag}(s),\ \Lambda:=\operatorname{diag}(\lambda). -\] -{\footnotesize -Here \(H\) is your Hessian model w.r.t.\ \(x\): -\( -H=\nabla^2 f(x)\ \) (Gauss--Newton/curvature-drop), -or -\( -H=\nabla^2 f(x)-\sum_{i=1}^m \lambda_i \nabla^2 c_i(x) -\) (full Newton). -Note the simple sensitivities: -\(ds = S\,d\sigma,\ d\lambda = -\Lambda\,d\sigma\), -which produce the block entries \(-S\) and \(J^{\!T}\Lambda\). -} - -\end{frame} - - - - -\begin{frame}{Log-Domain Interior-Point Method (easier notation)} -\textbf{To ensure $s \geq 0$ and $\lambda \geq 0$, introduce change of variables:} $$s = \sqrt{\rho} e^{\sigma}, \quad \lambda = \sqrt{\rho} e^{-\sigma}$$ - -Now (relaxed) complementarity is \textbf{always satisfied} by construction! - -Plug back into F.O.N.C - -$$ -\nabla f - \left(\frac{\partial c}{\partial x}\right)^T \lambda = 0 \quad \quad \quad c(x) - \sqrt{\rho} e^{\sigma} = 0 -$$ - -We can solve these with (Gauss) Newton: - -$$ -\begin{bmatrix} -H & \sqrt{\rho} c^T e^{-\sigma} \\ -c & -\sqrt{\rho} e^{\sigma} -\end{bmatrix} -\begin{bmatrix} -\delta x \\ \delta \sigma -\end{bmatrix} -= -\begin{bmatrix} --\nabla f + c^T \lambda \\ -c(x) + \sqrt{\rho} e^{\sigma} -\end{bmatrix} -$$ - - - -\end{frame} - - - - - - -\begin{frame}{Example: Quadratic Program} -Super common problem to be solved in control applications: quadratic programs -$$ -\min_x \tfrac{1}{2} x^T Q x + q^T x, \quad Q \succeq 0 -$$ - -s.t. - -$$ -Ax = b, \quad Cx \leq d -$$ - -\begin{itemize} - \item Super useful in control (SQP) - \item Can be solved very fast ($\sim kHz$). -\end{itemize} -\end{frame} - - - -\begin{frame}{Move to Julia Code} -\begin{center} - \textbf{Quick Demo of Julia Notebook: part3\_ipm.ipynb} -\end{center} -\end{frame} - - -% ---- Comparison (animated main bullets) ---- -\begin{frame}{Penalty vs.\ ALM vs.\ PDIP: what changes?} -\begin{itemize} - \item<1-> \textbf{Feasibility handling:} - \begin{itemize} - \item Penalty: encourages $c(x)\ge 0$ via cost; feasibility only in the limit $\rho\uparrow$. - \item ALM: balances optimality and feasibility via $\lambda$ updates at finite $\rho$. - \item PDIP: enforces strict interior $c(x)>0$; drives $s_i\lambda_i=\rho\to 0$. - \end{itemize} - - \item<2-> \textbf{Conditioning:} - \begin{itemize} - \item Penalty gets ill-conditioned as $\rho$ grows. - \item ALM keeps conditioning reasonable. - \item PDIP maintains well-scaled Newton systems near the path (with proper scaling). - \end{itemize} - - \item<3-> \textbf{Accuracy:} Penalty (low–med), ALM (high with finite $\rho$), PDIP (high; excellent for convex). - - \item<4-> \textbf{Per-iteration work:} Penalty/ALM solve unconstrained-like subproblems; PDIP solves structured KKT systems with slacks/duals. -\end{itemize} -\end{frame} - - \ No newline at end of file diff --git a/class02/intro.tex b/class02/intro.tex deleted file mode 100644 index 8b4f0c8..0000000 --- a/class02/intro.tex +++ /dev/null @@ -1,63 +0,0 @@ - -% --- Slide 1: Title --- -\begin{frame}[plain] - \titlepage -\end{frame} -\section{Overview and Big Picture of Lecture 2} - -% ---- Learning goals ---- -\begin{frame}{Learning goals (what you’ll be able to do)} -\textbf{Goals for today} -\begin{itemize} -\item Pick and configure an optimizer for small control problems (unconstrained \& constrained). -\item Derive KKT conditions and form the SQP/QP subproblems for a nonlinear program. -\item Explain the differences between penalty, augmented Lagrangian, and interior-point methods. -\end{itemize} -\textbf{Why?}\\ -In future classes, this will help us map classic control tasks (LQR/MPC/trajectory optimization) to QPs/NLPs and choose a solver strategy. -\end{frame} - -% ---- Agenda ---- -\begin{frame}{Roadmap for today (2\,hours)} -\begin{enumerate} -\item Big picture and some notation \hfill \textit{(5 min)} -\item Unconstrained optimization: Root-finding, Newton and globalization \hfill \textit{(30 min)} -\item Equality constraints: KKT, Newton vs. Gauss–Newton \hfill \textit{(30 min)} -\item Inequalities \& KKT: complementarity \hfill \textit{(10 min)} -\item Methods: penalty $\rightarrow$ ALM $\rightarrow$ interior-point (PDIP) \hfill \textit{(20 min)} -\item Brief look at SQP for solving hard control problems \hfill \textit{(20 min)} -\end{enumerate} -\end{frame} - - - -% ---- Big picture ---- -\begin{frame}[t]{Big picture: why optimization for control?} -\begin{columns}[T,onlytextwidth] - \column{0.54\textwidth} - \small - \begin{itemize}[<+->]\setlength{\itemsep}{2pt} - \item Controller synthesis often reduces to solving a sequence of optimization problems. - \item \textbf{MPC} solves a QP/NLP online at each time step; warm-start and sparsity are critical. - \item \textbf{Trajectory optimization} (nonlinear robots) uses NLP + collocation; needs robust globalization. - \item \textbf{Learning-based control} backpropagates through optimizers (differentiable programming). - \end{itemize} - - \column{0.42\textwidth} - \centering - \resizebox{\columnwidth}{!}{% - \begin{tikzpicture}[ - node distance=10mm and 12mm, - every node/.style={font=\small}, - shorten >=2pt, - shorten <=2pt - ] - \node[box] (plant) {Dynamics\\$x_{k+1}=f(x_k,u_k)$}; - \node[box, below=of plant] (opt) {Online optimizer\\(QP/NLP)}; - - \draw[->] (opt.north) -- node[pos=0.55, right=2pt] {$u_k$} (plant.south); - \draw[->] (plant.east) -- ++(0.6,0) |- node[pos=0.25, right=2pt] {$x_k$} (opt.east); - \end{tikzpicture}% - } -\end{columns} -\end{frame} diff --git a/class02/log_barrier.png b/class02/log_barrier.png deleted file mode 100644 index 6ca0171f6f4c3acb9adea9d0f77ecf282d47ae32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32399 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, - box/.style={ - draw, - rounded corners, - align=center, - inner sep=4pt, - minimum width=3.2cm, - minimum height=1.1cm - } -} - - -% Use outer theme that shows dots for sections -%\useoutertheme{miniframes} - - - - -% --- Title info (update these) --- -\title{Lecture 2: Numerical Optimization for Control\\\small(grad/SQP/QP; ALM vs. interior-point vs. penalty)} -\author{Arnaud Deza} -\institute{ISYE 8803: Special Topics on Optimal Control and Learning} -\date{\today} - - - - -\begin{document} -\include{intro} -\include{notation} -\include{root_finding} -\include{eq_constraints} -\include{ineq_constraints} -\include{SQP} - -\end{document} \ No newline at end of file diff --git a/class02/notation.tex b/class02/notation.tex deleted file mode 100644 index 9f93367..0000000 --- a/class02/notation.tex +++ /dev/null @@ -1,117 +0,0 @@ -% ===== Slide 1: Derivatives & Jacobians (animated in 4 parts) ===== -\begin{frame}{Notation I: derivatives \& Jacobians} - -\begin{columns}[T,onlytextwidth] -\column{0.52\textwidth} -\uncover<1->{ -\begin{block}{Scalar-valued function} -$f:\mathbb{R}^n \to \mathbb{R}$ - -\vspace{0.2em} -Row-derivative (row gradient): -\[ -\frac{\partial f}{\partial x} \in \mathbb{R}^{1\times n} -\] -\end{block} -} - -\uncover<2->{ -\begin{block}{First-order model of $f$} -\[ -f(x+\Delta x) \approx f(x) + \frac{\partial f}{\partial x}\,\Delta x -\] -\[ -\Delta x \in \mathbb{R}^n,\quad \frac{\partial f}{\partial x}\in\mathbb{R}^{1\times n},\quad -\Delta f\in\mathbb{R} -\] -\end{block} -} - -\column{0.48\textwidth} -\uncover<3->{ -\begin{block}{Vector-valued function} -$g:\mathbb{R}^m \to \mathbb{R}^n$ - -\vspace{0.2em} -Jacobian: -\[ -\frac{\partial g}{\partial y} \in \mathbb{R}^{n\times m} -\] -\end{block} -} - -\uncover<4->{ -\begin{block}{First-order model of $g$} -\[ -g(y+\Delta y) \approx g(y) + \frac{\partial g}{\partial y}\,\Delta y -\] -\[ -\Delta y\in\mathbb{R}^m,\quad \frac{\partial g}{\partial y}\in\mathbb{R}^{n\times m},\quad -\Delta g\in\mathbb{R}^n -\] -\end{block} -} -\end{columns} - -\end{frame} - - -% ==== Slide 2 (fits on one page, animated in 4 parts) ==== -\begin{frame}{Notation II: gradient, Hessian \& Taylor} -\begingroup -\small -% tighten display math spacing just for this frame -\setlength{\abovedisplayskip}{6pt} -\setlength{\belowdisplayskip}{6pt} -\setlength{\abovedisplayshortskip}{4pt} -\setlength{\belowdisplayshortskip}{4pt} - -\begin{columns}[T,onlytextwidth] - \column{0.48\textwidth} - \uncover<1->{ - \begin{block}{Gradient (column form)} - For $f:\mathbb{R}^n\to\mathbb{R}$, - \[ - \nabla f(x) := \left(\frac{\partial f}{\partial x}\right)^{\!T} \in \mathbb{R}^{n} - \] - \end{block} - } - - \uncover<2->{ - \begin{block}{Hessian} - \[ - \nabla^2 f(x) := \frac{\partial}{\partial x}\!\left(\frac{\partial f}{\partial x}\right) - = \frac{\partial^2 f}{\partial x^2} \in \mathbb{R}^{n\times n} - \] - \end{block} - } - - \column{0.52\textwidth} - \uncover<3->{ - \begin{block}{Shape check} - \[ - \nabla f(x)\in\mathbb{R}^{n},\quad - \nabla^2 f(x)\in\mathbb{R}^{n\times n},\quad - \Delta x\in\mathbb{R}^{n} - \] - \[ - \Delta x^{\!T}\nabla^2 f(x)\Delta x \in \mathbb{R} - \] - \end{block} - } - - \uncover<4->{ - \begin{block}{Second-order Taylor} - \[ - f(x+\Delta x)\approx f(x) - + \nabla f(x)^{\!T}\Delta x - + \tfrac{1}{2}\,\Delta x^{\!T}\nabla^2 f(x)\,\Delta x - \] - \end{block} - } -\end{columns} -\endgroup -\end{frame} - - - diff --git a/class02/overview.md b/class02/overview.md index 0e46eeb..a81d83c 100644 --- a/class02/overview.md +++ b/class02/overview.md @@ -4,6 +4,8 @@ **Topic:** Numerical optimization for control (gradient/SQP/QP); ALM vs. interior-point vs. penalty methods +**Pluto Notebook for all the chapter**: Here is the actual [final chapter](https://learningtooptimize.github.io/LearningToControlClass/dev/class02/class02.html) + --- ## Overview @@ -65,8 +67,7 @@ The class is structured around four interactive Jupyter notebooks that build upo ### Additional Resources -- **[Lecture Slides (PDF)](https://learningtooptimize.github.io/LearningToControlClass/dev/class02/ISYE_8803___Lecture_2___Slides.pdf)** - Complete slide deck from the presentation -- **[LaTeX Source Files](https://learningtooptimize.github.io/LearningToControlClass/dev/class02/main.tex)** - Source code for the lecture slides +- **[Lecture Slides (PDF)](https://learningtooptimize.github.io/LearningToControlClass/dev/class02/ISYE_8803___Lecture_2___Slides.pdf)** - Complete slide deck from the presentation - **[Demo Script](https://learningtooptimize.github.io/LearningToControlClass/dev/class02/penalty_barrier_demo.py)** - Python demonstration of penalty vs. barrier methods ## Key Concepts Covered @@ -89,23 +90,6 @@ The class is structured around four interactive Jupyter notebooks that build upo - **Penalty Methods**: Quadratic and exact penalty functions - **Augmented Lagrangian**: Combining penalty and multiplier methods -## Practical Applications - -The methods covered in this class are fundamental to: -- **Optimal Control**: Trajectory optimization and feedback control design -- **Model Predictive Control**: Real-time optimization with constraints -- **Robotics**: Motion planning and control with obstacle avoidance -- **Engineering Design**: Constrained optimization in mechanical systems - -## Further Reading - -## Next Steps - -This class provides the foundation for the advanced topics covered in subsequent classes, including: -- Pontryagin's Maximum Principle (Class 3) -- Nonlinear trajectory optimization (Class 5) -- Stochastic optimal control (Class 7) -- Physics-Informed Neural Networks (Class 10) --- diff --git a/class02/penalty_barrier_demo.py b/class02/penalty_barrier_demo.py deleted file mode 100644 index 2893eaa..0000000 --- a/class02/penalty_barrier_demo.py +++ /dev/null @@ -1,38 +0,0 @@ - -import numpy as np -import matplotlib.pyplot as plt - -def plot_log_barrier(save_path="log_barrier.png"): - x = np.linspace(1e-4, 1.0, 2000) - y = -np.log(x) - plt.figure() - plt.plot(x, y, linewidth=2) - plt.xlabel("x") - plt.ylabel("-log(x)") - plt.title("Log barrier: -log(x) vs x (blows up as x → 0⁺)") - plt.axvline(0.0, linestyle="--") - plt.ylim(0, min(15, float(np.max(y)))) - plt.grid(True, which="both", linestyle=":") - plt.tight_layout() - plt.savefig(save_path) - print(f"Saved {save_path}") - -def plot_quadratic_penalty(rhos=(1,10,100,1000,2500,5000), save_path="quadratic_penalty.png"): - x = np.linspace(-1.0, 1.0, 2000) - plt.figure() - for rho in rhos: - penalty = 0.5 * rho * np.minimum(0.0, x) ** 2 - plt.plot(x, penalty, linewidth=2, label=f"rho = {rho}") - plt.xlabel("x (feasible region is x ≥ 0)") - plt.ylabel("penalty(x) = (ρ/2)·min(0,x)²") - plt.title("Quadratic penalty for inequality constraint vs. ρ") - plt.axvline(0.0, linestyle="--") - plt.grid(True, which="both", linestyle=":") - plt.legend() - plt.tight_layout() - plt.savefig(save_path) - print(f"Saved {save_path}") - -if __name__ == "__main__": - plot_log_barrier() - plot_quadratic_penalty() \ No newline at end of file diff --git a/class02/quadratic_penalty.png b/class02/quadratic_penalty.png deleted file mode 100644 index 53c4cb3fdf69356f1f1b6a3f47c8510520e7d4b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61491 zcmc$`bySz_)-8M^h)5%d2uKKmB1%Xr-AD-tNVgy&CEZe@lp@k4B_gGSASn&fDbg(< z-Feon&-1?fobitFedBz8e2(qjZvFAQ@iy#Prtc-*Tf}HY3 z5R7pgZ1@}QuHk9;AO5G3nom`&&7L|L+LhEHuCTR(klVMOO>YG-d@{p8k7-kaR4 zbmmW=+Sm)Qv044s2X0#1J!D%;Ond-ea@I!Xfjxo{8KS>2-V0}1AjnFxtc0kVbNo_* zlO|c?N#mHC6Lp0gsRY(XO%f^=bu#VVItc~&r7LeGj9+jZ8yUHhg)oi1x9E@7JQosj z756f>8d-rd7x@yMs4IVN+X|5#mEw!)B0)2~4*M|sul8S)20TLTpO`&-%iu@MPJxf@ zTVuffUesg(`RC6R4gTE7#lL@l)%X9`KO#4@3afjwR6`sqF)*(>@*MjWTF+g2zR;c_v!$vF zL(u{bELw%;h70TzA=Led2}NXYuUh%xdh6=;c0hXimGbiP)%8|3h8s89dQ*NS6c%#1 z?@k0tS!_l#EDq!~zP+Kn={d|h>Wm2&t?y31Ti|ivh)=~MzcOcFF8=f3(L`ZCKI%VnZ(`F1`lu zV`_b-b#-IomSGyVs*1`fZEfue>boqF3p0GdA50!TtZio*1Qs=!jVuAb7s0ZY&kO>Xlr2n(kLelcW8u`+C{x+0Bi# zUh?G?6+uNs+!xsm5LsDS^k+?4-duC9Xp7?|MHqQ_-hNNG9rdNOl!Td?Sqz81#CnX$ zey;sWj&=!?y1IH@eZBvklfw;028Ps@#U%H5KHF!+VlpSkN3!s{$msg@>kPE+s~8r2 zAI=vP7BVI#CQko&m~@5r$?*G`ge!Fe15v@${I976UEe%i9*)t}(t7Rqd+61e*GYxk zNO}34Pc{=`-5(#^x*RDdDtbH_x4Ec-n~0HF(C@CLCVg~u5iI(l&H;ZdT~*ErsCIFm2j!c z1`~!z|HlWaSGIR|olkkgUS{j7B;u}^qkJ-2E}Q6e>@vBuB>yodN46tTusKCMurSni zvhFN=a(^+;1R|-fwe_V9we`Xl!fjBp~MU2m`4J$Jn-&jj=)_yh#bojG%c(_ukP z1%AzJ{VcI&hGn>dzP=wnV&tRQeTFOCO5_9;ZhrjuQD$d(L{>*fC$gg}Vr3&jFSdi0 z-Va{$tGOhv#NFLp;w&|}SZ+%x@*7$burZ4Cv`5CYXR4xxgcc*1m6#M_x#;am%U^Va z(~HY2_UBkSYn)7dkGW;CyE;+V+#EDGI2ay!>Gta)%YjFIL7|~c9tYc3czAf~y1HHk z(+I|v`qRtbP_x z%y*@tD|8b3@gr+pUER1Zk^2&rD1YUFU= zwb?y5n1VVW@!-M5%Dq{t&0jrdF5J|4&&haHSe}-VVbuHn-h6KssnGsB17x$wh7hXf zVPSI>=>KQcucZF`uw%BW2cDuWX$roPLdczuNiqI8Ea^oW8XAO!N&Lr;ABJm__4%Ja zi_6R7zh%=8{q{}lqgK&E*2-4TG1J`l7~eo5+R4EJjBji~*^9E8HaQq`V)d*D}3=2_&E)lVMJpNWEQA2!R(oj=W)!G`4CcE2B5B*0Z+ z5$Se_i;Ih92w7!i;;P?}GMI{si>FtrPWoMsAfw-f$RArw`(D*+_&CS)~|8N#DIX>Le z`iW*rBwRI9aqZ`e7cZVCCDB@Ry*oXI`Qg_HWS6}DoQ#e81DJds`%iORJA@8cE#R6b zhugn@O0begC$W=mb_u&5qM!F4rKZgOr; z+TFt=d$U^oI1Vrdo~@G;a~Q4gYzbPy;EJ3s>(eYWO0BIL`W})+mnQqknp~2jg6n^)99!V7=P~+eoW3@J5F}~JO;A#`?qCI z=8wEAeOf4gdv>Ct7*IkxtGv9>6a(Liu(;w*+wtqU{pQX|%!07&a~^*1*-Vs`e8Bmp zznD*B7;UO)>suQ;yKj(b7cSjK4v!C3Y#bcwE%PeWP4yKj%p=#<*Si<$?QCqSr<)^Q z#m5IjOd{t6g;G*dY#be>6cq_iaPaVa<{uEs@Evp9oWD0cJv}}XrT?UpgMono$^y~m@1Id)W)B|* z{`mZG3YPi4S&C8&r(|mmJ(`fUI>Z69?XDS0d6nSn3$L^&TE%56cca(;d**yDkh*g zS3*ikoqxG!{T=@REIHL3lG^U>a}Yb(pDNbf^wiY+;^V3P0s`I@7LsY?>3J;JF0S)F zprxj!W`}gOG+IIBN9=i!tNXPxTZ4_udX(DRb2Or5X{ZSATo9K1?DvTh*>n}ixo05p z$jHfc?@3-o`>GBE=t7y{3-!&;wyfwNp`{JW&u1ST8v54UeD=wcC(bJsJBYV6 zGFobX2I>58|9Z|qS!t<-N0xd<*-3IsOH01{o*f{TYPem9NE>^5aZgVnuiSmRjah|4 zi=K>{&$8OZj~}F`43;@qBI8lBUp$WlLaF&r@$&NKNNZ|Rc)w^SjqGyYv+OLFc5!hz zEFK=#K~pni%KYNu0D!b7iQJnN=>EFZamQERbI*UQ*tn6<+0D(kGg*{CU^d~lQzt+_ zmY>|Uk+B^C*dFo{u0sb-NBa)MVsOjZEc~)cN^=|cdgl7SeLDqMg&=fgdE~2mZ8OwI zc*c2vh~5EnxdKc|iNT*faW4HVArh&_pG-?*EMITAUR1d}+ZKm-6J8SN)s)^C?S1nT z7UQ^^fB<6F?<{F&rV{PekQ-&rk(ryfcy_KW>DoLyY*$EfX0JCWDs z=UXBdu^YR(Nc8mdzQ7yL*}AD;8En=TyQUw{z2%;z#*#C~Ydu;AP^hceO8eu3&)7mj zLeObE4-T$_yprC-*1zEDg9h>A(ejIcro?1qaHua`sOSr0=%}3CN5S(rT5+Ww;u%UW4PcjK zL*Xy#i%rkM@}XfsBcrTC3AW+>&I%Era`(+HX&f9J@5spW zH+8;vU%C6X_m^N^>zIj$%WTX;6y-s|Om$ir9jtKUmAFiz=R9%x2CeQ`L<`&?51(GZ zu>pZdz&&U9@al-u*hF5{aW`c^kIh2wwc$z+s+SkoW=r^Q-)0aNu96#JcC$pm>-xGK zHJ^>h;)PFUI10Xbd2HS<5~ErKnW2qRs_+y&fyNR=h3a{FZPw5e-Z#o=?aT@A_D13b z-N5NKQt0LdP1L)$&@ zYpbY27{IG9M2zFopbyk#$QGMff>K2ji;FKU2XgQK$i3*;`Blp&J`2jipt*;vLe64&+5yVGOep(L&XVl##wLo31*pkqH>RHjEiw4%ZFFE23M zxVGS@e6tBilUG3Mlu4!n%hMZRe`?hF% zW_EV6Peb3|*B6tVRkPZf2nR<*RMbbk!_dr(N%&2g=*Hsb^}O+C&rU&LJjm9AjKs>y zy4uD&X5N>xlJl+3$Tm`cc+7o6AySXL?Si&L$RP7Y-Y3+M_E)P;j`CV6%MQ!Dc2`sS z4^%WYp9Attxf^wJb#oI-xl7w=f71fm-olo}jg5^IHI^jT#1^%kN=yeYVJj;uOLuF= zb7%G3*Cf#d4p~wZ&}mneYNl(wMhppq6dec0E85vpEV*t6P*JoG#W(2q7JjSfuDmfTn1chh2;8{^}#b}J;)&h1)wo5Fs#Kcd@`bC`K+ch zMVvlTZteT64g-L`S8v{I0B#F$a&j^;Gh5#p9v((_#sp-NMu7k9Ms+xh%*<2JfqWcr z2uzg<`34waVt)QPWS)2-4@v+vC{+wXC_D_Kr579(ZcX5XWIfJv#Tt;LMvvHAwxQz2yy^iMp5 z)O_CeQl$(Rdb4l{38iFZ&*tisJ{$8qAOMV=J8&>QKJJaSWiu_2%2&yNc#~37Q%i8; zf(9CWX!rGIX7eAPt_+Wi6uEvu>lVBbN{FgXErE3U@`L5CyU?|wtxWmx{-TYoZ4KZk zAhdh{%#N6gw?>}D!Kr3Wa(|Y7H);|do#M75DPnb?H@)qc)=j_=UAoO-k4WMpOJ>?A!6WYhIwY8<-w_^l={VE|L1bXgN zz@~@ul91uzk% zfIQKcXbuaG)*&belMr$b@eHZ(Qrr2>lsh1gHr-{bkhmi#7$n1Z8F>z+NLE21r`S_X zjqF!v3MR_hLBvf%#mjYk@B;G;VYp&~00}_cZV3BzxWM||Y&E@e;^``{AnS(%-08QV zpNFk=Kj<18TbO(tPso?DvU9g@-#$k}qnN8#p*2I{N4ySAEU}lD*JbjTNoB-OEiA4* zD1Ll#ZDWH0SjM`Zo(PoIn}&*ARGArh;n_0uv?zfGyZVaT_EMHg$@}-uS#`_a6h8j( zS|(j(a$!LZx>|+HmoM+8UtnPqIAC-(YK}PAS&4w4e9OnrPXbiZ`Zqkba5OPzX^gBh zK6voJWV-29-RNj#iyads&^Ag?VoSYFJablRA~=Lf@$u;rV zWjndW8~01_zen6P>n)UMg{Dz)fBW}cfC)i4IX5f-F~kUa36(njj#aOKRo}pS;six% z$Um7o(a_NF@Y^upRvLcCCCx}vEueB_AgvdC{>%Un)dXtEG_cb5-|}X)a0uV85oB&UZtgV&cg0cY7n|R0EQ}561u1%#WB?;9H4aM^D@rA>V z&1mpXPI?fk;z*^Cx+#+zRhbvwu*{UD$kE%)D%_Y&|O9>7ZTTwxS@d70rAR2Eu z@R`X1hFaihQ*X+ALUJyT41uR=cEWQy@~%^kxW@{z5o~z$y^N-vG+r zGW%X#?y@0&iO?75b4LO?RI&lJ#x&pIe-vtTG2dgx{`>(6;xk^{UnPfvKnw@?gYlV} zzU2C)2bjudKp?=OrKMF3{QF(LD$qFu0`TrTaQKMv9&>wp8(|rB2b4)FUHj*1zT~)g zcoS1owIz}Zc$Ub5x7px8O-&6zNWd!~Zqzh3HZty9=RVCPfcZU+$+{?Z+*9u?jnH?A z6363knYs+ZX7kQyYj2lZ*0o)ocRS~s17hB(XQSvC$Qac?BQEo&*wMTSqnzouN9qZ6 zb(g$S)pFS#_@w+B+~FHt@Q8{DlL?`-{RaI4H1LmcqH7F#V^m7d`Lu?eLb`=;14G^r zG_X?y@26N&`E4P%)RA)jRJ>0_(p6#v$Wld9Z&jCDZ1ju7@#gHAjegfQ&reD?6-fLH zsQNQR|`}JcwfYq5;qX94&E2R5mi?wU*FhJxP)8V1gWE@r|0uf5KURJ=wL}` zHSnuwqo`H%$h1@cl-M(=hu$tdvpFe7&z~pe8EI|gye!FBn_FHcq@tqwT(*YLW6jLW zpf%Fso8LZC<-E_}caBw;XV!0@3ffHSgW;&ReIQuCf+V z1tCU3$5JbXgF%l4LDkERIBWq`g^i%H&B%!Eig2_hrt0Q^o$gA~Dv< zRXH|&#Jl%dZk{3K1_9TwwtZVtNJvPfI43T3V~o{OSu&e-8wVM#|0tFBWAc3Tv0&Si zY|3Bu5dZTX>&0)LuT9+g&7N|FE+tbH3rX0+e!9amFnUW22fFL`@k?n$*5b1X|E({I z+YnR(ZlQ*rC9tAG5D)+wusUBI6|&pCF)*ub8NX6g_74lJPAA9 z8Ds6OXB|3aU}Z%RIz>!Hg%>*YKDtfkhxU7%<+wTM(V`(61Mc#LCQvj**L)Pa4ni6H&UniUQ-C(i1;;=Yg?IMq%^N?!S8G459zP!c_3OcJj(6|g zg};3p08K+JpfwbT0C^n@$!Z*msGk~3J=Fh+xw$}~(l8PD+Q6#geLQ`St@Gb%YYCVK z5w2T4=6|;7pRLui&}K_LZ8ve;dF{pxe-I4Orm}pkfy(uEZn)bCg0YPV$pY7gRM*!_ zS{^`aHNLR$0w@Ml0sxA8W+wq)=%w3^eu05!s&3r5L+$VHFQcF^v*_pVZ*l1s`~~m^ zJa6PGl(X-D@!-FBk7^)g*Ol^qEjGYaDB$TW7Y#-WT`ZH^;`-ftW(5tZ5mwE7#JYS9 z+uhxr{qZ2Xr#@ugGKWQM2q9qlzqXj_QSm$$0ptWFY^cz}7nEZvu73CzW6I0 zjV(KQ9;}ZHkBTT!4k1a%11&0^2LNvTlDv*D0s^i6NAX1k@&E(`XTmSGl=-Q2X$x6?CKQkLuZ$H?EW6k_oogtR&r#*3!X0M|6VWF9!dq z$-A*>a?xA;L5{j*B=>-DznXfMnwF_J-~F4GW%b$iDp$$)+_qWY$eE_vez=Ma%+f+%1)w>z ze;Zgz23S&9@47VEh-)y+ZHzuLteZP12Pd&TU6823x%b(p&l?tmjT)-1}F>E zjog%(Wz)Z4OTiXD-Dn%qkag3gyV_Rg&$6{A3gQ)L;)hOAhN$YUlaZ3vj@73MDZ*BP zdqY@`r`T9nzTFx-^w&$0PMv$$*tPiCd~^Q}Wd+wvMF^clfojXSt(VVP& z6&2ioi#mVnVY^>R2`@RDUNDDo!|K3Zs|jaigcMVRC{u*t=6n|j3S4N~gQRsCUO9+R z^+1fEEGlT!PZ#?M>g(&>Ys=7kIn;3cFl^w~{YzH3NGqq0ByI0IOQamxg$v@WT=I@-+%QwIPEExvt34(W@R!LeK7Hlz; z`hxsZ0F)rmO~?f{y=hc7N5wu6tVBR~0Zt_lp74?G5~$#2tXhT7X&pUZLtF@56sZra zZEQSKT3QMu{H(BuQGsUmM>Q6p1@y^-B#46~xZiT)31u|UNcTGFV-y;6rhOtK!kv#{4B3>9~X3T zY{XNg0Tph6;aCf~6*{%4p1Xw7C@8%QiBRf%W{YWvs)DIU{ndzOn}cmDl*k?$(pvni zp`$bWW%;x`P7R0{0nifD^YCnEg$N_=;!5^;<>BGP?)$$o4}Ys7j^b<qku&_X*; z@ZkfbVuj^YzCEh;sr%NlEa@s|si>}RIEjs{Vy5wamvL6GY}6bW;^ZBe-!pzAq%D+X7k!s!%@) zIRexHEQfhfql?`&CN!zRLxnUfwD=K^9yA~JWqx$I`ATNDV=}@^i9ixi%HGD@6to~U z&{G&%TX*Pxo|~V?v9z=tE_eCM8wOMO0Q4FM1mHXs!rPgrFZ^_&_bh^{^}uwYR-2lZ z$m_2nBmGYfs!nP_kJ-Z>207-%$%REF^xM6$zTDu=aedHrR=sWR782qN-x0!*jPlR3t`Ju8SR z`!$G2E{3&)3}<%b>fWwvY%~TN!(lO)Mk+Tl1VA$l0a~@+Rma+TMW8X6ZDGm^7i$0+ z20w!Li;EJGKfpDCDmz(fF91gvgINGj5V|V?I|QQ7Ftzxi-Uxay9O&F7fq{qilEbux z3}vX@F9{0^QwzCMpd}jE8nnrn00=@he2(2G#nQSvYTv8#)${{jF_+ z?MkUFEG*pjfSyGasimc*&QGST*q1(6j%J9!@@4A2o}ZfXg+LP8;zR950nSM#;4M2m zT`g9hY5ef{jhtkbsELNsTj5cWVvk0?Sbxk>V|f)G?hPc%Vovr?YJ<`5anP#hj>e`a z-@g4-ppO=4CiKysKgU615WHRBGqI0Ur1w}~oe&3bz1G2S`7$vHi4W`<1|FVb(Z-KU zH+s_z=J&{AuaT?V-95ci9pcA#b!O&^?K1wU0|HJ5O>9ZVbM$m{-YxW(XBE!)5jVYg z!9jdw!#zD;|g5U1<=|@rS4DAYr7p6qnl187&W&N(GT3V0ByTZO;s# z+oTZ@5DGF2mSGetS-p3-QK-r+PZ45kFo46c(x4b4=1)jVBR<%0{i?=UJ9=zi(SKgJ zuNq7Ga$qRGbI+D6m-rjGE4e~Ve!klEk7vJX+)UwP8>qW?kvv@`2>fWCAxv5Zqfh@u zWed741cI^bXRP}XGCs&|Q2r&pR$?NE-+S8|^zDVB7bzgW0Vq`7xf|osGn3=PW39EJ zkj*QyA{g$!tT4vh^&QeQpnK`Boj!-f4MO-IZ8LtqeElDvEiqFNrzi5KJY~O{u^3?< zaFq-f7-=%X<;2xh>(fY6`V?cm9l>Tut(_etxv#Hqc?G#Luk$>^XVC{Xjk-ywu$|A=Aw&+FM!H;!S(FnzCy z%T;=J@tyKjo847IxZ^b%Py} zK@Mx(;Hn*aaXX=ZPEr1amwmi{z9w#p52yjWGUo5Z(Z0Nv&?bjY0WDkY^<2x5$j3kZ zJTg8MqpHmFO+P|Opy$+v*(g_a`L7M~m-UcK3knM0zrk;jLD)L3woT50z6dhh(@?`Z zh^RC*AEcOu5hEflvS&G8cf@es@m>9MGqVcN!QZsDr@&QEP^kZ6JJtD4vOeeHK(4N~ zRR!?IU~?!$bu8EIr65RrlwlTsUoq$i0fd)6a6j%G4D`vi&w%bc%qdGJNPR`xaKhs3 z^tYpv8oIw-l0rvjI8`OsdRBLK8ZYR(V<;(gJX%DhVgNA>XK3BMp`a#1e!?Lll15SN zR$EGA6gSf5&0NJE>$G%=N}3s|&cO=`KYt+(?V5i^rD4yuC1b$OjM#w~%QvVw1vmp| zd3hN`LW(N3YuC<>lsWi@gb<*{9uOETDjY$mLM1sh7D?d6Q4ZF(VW`5*2}RkU>iMOc zZVgQ5f{FEZ5i76ga_9jcNgVPcZjf5aeZ#4)t}iwIG^ z7P))yT|Wc2gHCNu{?EH4D0JzGwWh2nc`}0LW#N-UQjp8$O4vY#%GSthfGS1*N(QYY zo?tUMKz%@{i2~a68Yp}0RzqCzeD^jk<%6b%0ZB}Y7U;l7`GGn&rJ;R-?7C|Okqew7 z>MbeC;|O%a=-IHZlYb?{NWozw5vd>QlY(hWZzqh<{Z_*l@F&P#o-vbEXb-tGs}KbQ zO1VW_iC#t3A`zK}@TR%)(y$Ld^P)}#z*EgqyDK>Ku#12bJ%yln1BB{#q*K7V)q|;y zKBC#c)YKnRDGF49lgC6@^~x!r@QX@FU_mFX`P=2+f<^ffd{B zer+t}D;X?i=1hSB>w(-$lT%Y@W%kr|cfSJ!uBCejTq|AW8ayuvNUwnMPQ5qJmy(t? zu6cI4uQCIQrZ;R!LV7GP0`}!zeKOaLRhdvWzMYPD^>B`4Szp0l$v~P^@`Lar`zPPg zv?EC!=wmgUtj3Z>6_CO)hK!5?=xyH-@jUolg1S+l9MZG0;)5Nk4hpI*sNaiFM1BDhA3?FPVm2c)cr~hHkD`|E3KkGHPpRld&Jlg z{7CTv4j&o}eXEas_v{v$&5iv{l@n)&H4R5MowXl4Ups0Qjh>G@!@)dEe_8x5rr;(R zD*{c5&ETsP}TE@_-Qh&HpS^o8#tx3L*Q%sUxg$6e}@X*Aq{59jMVBppRPPh)=;f&L^^q|5KlUmI8(GOr+d> zuuOiuxhIvY@Y<9t8{lpuJC01s;gwY)V&9#S+yK2e~Sb0v)8ZBiSqj4`}{48 z1^M|R5LY?3PFMUa4osDPU|Q?W)! z&2XSvqPb8jM@mkvMYW2Hg$OeB<`~ni?Q1OM_|I#>YcAD!(x0kAQ1iD41A7g~|Ffb#Wscjnzr>@n$QN?2b8`OQ zI^kOTe7ye~62KI3;n%%2!i_WX=iwwSqb|_}uEz!z#=h!;VhHl-wb5`{xcU4lzL-Zm z73udl8f%iql*}YN*ZoovxgQ^ufsgH61QO1nqzdzU?LV)m0lY#BhuH-j!<4y|?IEY3 zOqN9YQ&hDt1Vz@C)1AlL5ytsv*Po?_4*U!~*8f<^7u-fNYI>|kWA(C|GK>M=+A}lj#fhtfZBNC@3@?U1Wv*GB)))afdIYs zUumFsaa+{qh=l;NF=mY2Mm(MhN-NC@ND8{bFU01E!jxUE^9`*Nbk6zQn-3E|lDZxawN2meKvUfs)ZcZ3DQMm`jZ$>cy_(26p9su%S ztV6v8B9f`;hKkWfC|l8mDVryg+nc{_^&_&DPIAusBaRgJ<7JA3)} z3EK>~dVdTXjy@Ip8BUk8*PprLCr%o!YIf*W`Bm$y(ZG9MhukqR=K*|r;guK}c^cUT zD|Hu`IFV{_+P-|nAT7oeQ3D%}goFeg?*Ym845C@?%z=_u03qr@0hM53nV6WUal<1ht91EJ3_AsY zRVpAC;+ORfp1N_PN5o&Zq_3X0OG|noStZ*;&k-@hM7i*I`A@5$hJUtuWG<9^$xnZ28*K+Xt6RBVwNKQ`1t|+dprk6RnPA*c>ft-eyB!R}l}|DJ`A@2A`r1TZEfDF(w*d|U z97-AJZSkEqXS9z1W1s<~q7blTFPGb zGhBl|ex!f_7-7kkj0Z~_fV}`3L05ynxH=Mc@) ztDvNgqfeRB4iw{q1{m9q-mrtvn>ek$K6${NRQ?jVuc+YVP;qJNGfgcf#AkQa@8Jg& zT>PJCgj*n08|2#yx$i1xJMjPnfvt`@JB^HuF%Yn3R|E3*1;U)+`gJ^1-GP`WWD0QK z>eGN08ZsU=*rQ3v$pfIxpk#fbJAtEM-@PSoXh;t=`kt~f0urxkpy~~@0}UK7L;R|{ zYDsVPdlD(ush=1EHnA6BilAp;KnKJp{U&so_AaxEkd`^z&$PARtFUkV){_D13|985e4aHLm&8!RT#j}#!3L*dJkNFV;=Z@!kwfEny z9#Frvwe|DOd`EpCU+CVdeLPAazknGjQuTcE*JH*1VzQw=+UUEil!^@Ij@1Matd;@zs^nG_r?zYtUXBWzItBV0xmp~fJiZry)aH& zw%;#CLvx7mCms``JxuE~3;u}!4J|Xto9q5=A>s5ucOwa-AdC2euH78Lj9Lmk4}ks6 zcinmvFYHAFSOm)K+zv>o1nA2m>=+!1Y$Pqgq6Pu{88bs}2AU?}2Hyx3?F&aX^_- zN|+&44}SK`*RP95UoM>qoO;r71ys)YRCixx9Ez3)^YAoubYBv6@sV+fZwXh-am>H- zChM;R+jfY@M5IO;_nisKzJBO=lGDDWUG&Hwu*h1+=TDyy1i<1o)DDg~?~FKc*iFko z9|;?q9_j=@wR*4(AqdKU!6X$*Kl@;wQFluO%_J3eeo@2Z@87y}Uy{HwdImJR>G|ow^&w~BIPEgr?HZa=252${B5}tDBVSkolCcP{-OLpdSy57q^roi*89l~=i~27 z%Ia#*se;a4y}{}o;+r}enuix)M=pT!egwGwr^5e5MLFM_eb$4TPm3{vx&`_g@CrcO z&y_eW^aio5&CiC)T*W#K(jdHsfC=maU(m6`j@a7q5Xd1k}l=WX%gz0A+>c*4o z6gy1K&$>CaMe_kPryLy|{54r~yW63;ArZC!10tziX}tcV)YOql!a``ukB5)3c0Oc_5oDlOZ{~!IYmaGkO;B4 zvVZlNbm;lIcaZ^4$stiGnwK%*C-aW&Z{~Kef6RiSc20IAjuO(OIM72G@-g46UxtNA z8yFabFVA+W1;H8nmklSwLu;{rmN42*XcwR3}k7wn;WbA!OO;2 z%AE#Pne@I|G=L1twG$W-8$~BeYY@^z`(Ho-l4K z@N`}TC~`TV%w%BvNDLg|P2g0q49+P&TpYqCIaKO^831J5i~l}vPX-uhwh}Gu!M>kk zY`$O~N>2t1yO_B6;uhWqp{LY``qp!iQ_}zOcK(ZE`KuZXxIDFh;WV(@UjjC)v(m%O z9r6x{U4Kz31uYyq0Av|T@xc%UAXOY(+;5Ow_XGf`iU7rij)dOSEgJ`W326M!)o)LM z?)eS1uEWN`r(j1$M@M&IFl6rt>^^~nm!4^|N`aoJ=ecK!&WX}`Y`&@)ek9(RBmu5N zARkh|whEO3)BD3ME3RIr3rGI#HjNR+-V=R%AP;To_j(4gkjje&zzRbXoU8fR}G+U~Y`J+00xm(YPjUC@JWdilvx`N6c>D0T-T(q9QgDaT^R%VBF?|L`#M%`Ny2|WcW*s8!vfg=1j8| zOT&P8{Ho$tBN;x2Kg$B?*B02ot7I>vrc9bv*1>QC^%WVfqV;+40Bi;4@1AX*v4i0t zNLgcU;_iHcn2h(tGYdIKK$>4hnqYhthu#l_ z!q@PpM}0p4rP*p+^YfZ1U% zINd*7m`V#OvHS5x(V;m)C2P8Z>Lqxf(v+{tfT0S=);~@u0Lf`zzC^<}OHF}!4PNOD zu(6>p9A4e)*jPDWcXwT51t1#KK4`VZuH?0tw%ND2t(p9wPWZ6l{7Gkzn;--n{%_y} zh*tnGS&{^G^DxMUiP`C~@+!6|VTb|di#5SvWbxa}E`tC8vMy)zmX(;8*si`dCDhIa z+Ejr*#ukNOGcB)~Uq_Lxk$HRX@0PBUxuCj?_EGb1C9n-Cof;3MTTpSd8a<_;T_ifY zW!Y5fMs;E`EYYKGg}<;__zA3cii$<`PguYaCOb6}4L&jEwz2!hcCN93nydk8ELKky zx7L!|vQoxoVh8S5hPJECw1|gsM<$hzh8^Ax&dN%?XvD3N*-|i|mb;YmGJCcZrniM( z?w5tC*@#!8!3^Y0Fih^8hE}Dir9})%W#Ty$NLo;MecjE>D?Ce^YJ?ThFy$Rm8e;nZ8Pcjc*>7hH(!~a-HH>7;kg!8fc z*7>H^76U^Ie}1Zxn(l@MgSl)1V}3m7&HpIJ)idXX3sp4m{d2AeI@LkLAW~al%6So7 zhU0T{jbX&mq-ot)XNJ~uh*pNy^DL<(!o{nVYJp*d8KxVRN83Ls7oj^=zc}fEP8Y0R zK=5K(KG)&<`$wK1+PdU@xAEBTolZdZ8grLRlZ|iUXP)2*R*7g{Uu^R>AO-LZFMpvo z2;hNU4RV~+MV@)S_uaAH92r#DJQ<(E|Ir50s@UU!{$OR$hT zWe%8OoKGkZsiUXlI(eP7eOAcc4jjm;57Kgt1 z-?IKg`|~)D!ap_u0Hx3~T(lZYwn{n`=gK}Aa?HO^k%JVSg=H1z;%y;`XI@g3!%_Bx!pa@4Mb!EGtr!HkH zK6!EzY*sK(dj>{6#$mE?dSyFh0u(tM0s?<96F{Lt{f@o|eSV4;U10PM@Q4pOJAyzf zloFLpagw5*{VFc5bu6}l;wcU1GdBG@=`!Xr%>UADhq2ailAX_uO|OB&W^Cu zzq(=TnE4Y6CJA8R)}W)GtP3JV{gSBt7#v=AoL5D%NAnG9!CKlZ?0K}Mk#hticVdqo zu|S>)0CExN5kmARjhzeR*UNnQD?>NSpKc2dAb@Ovn7{V)B$vCo~FK*b*NnfM=j(xp*MZ2anHr}6U5 zyDp>B_|{ucPE(*e21J4m`P=@E-9F*?pA-u)>==xeyP&5!3>90wgyT?1pi%4U>eAYR zBsuZxSF><@Lc*R0;B?ff>3O&&y}9&*6xGDRdEo*Q{eD`5N2t+~-Vd_m8+GNN$3s2GyK`rFz7{*u4TFvLr04w{Pvqw;!@q7T z!9RUHxckA(iq60Nf9mU19YVhXCj%%1=KHjMLZX=feh2Q=0L)X)hr1kAE70V_Y`z$z zMMMqXBMeU50zU+F%`=OeivwX*rx_8juup+u9@S#(-S@>Z&tC&l!^P9E6%aq3Yw(y3 z)HeVxKH1sX_r}I}D#S^?9!bB6dNZ!|?b>G29nyZsZ7Keetat?i)bEp_Y}k?^0j8kC zJJ+#z$VSfk!Pv|G*67g>{x{?;TVxML3T(rT4&6o;b*iBM--V-|_8i4v*cbWJU7Z}C zABe4j0ZEjv0xviWouLgsOx+s6N+RB1-$jp7LN#kR%OP8^1IDwuU>9l`uWfJV+7I7- z%jy#qb>WZk1KO-w`wqb~VBAAJjp)c(01+)JP=Kcw<(a_(=q}@PWjCb*7t~Os-=60Y2uL1@F zLsI!ACwZ7Dkh9!Nq31=smzEx*XGFlR)Uv`u=6f=Uz?7#k%hU+H6edieW)-!=)(PtD?9^J6kdPRMbK=k=dw^E# zn^;*P($LU=$q!6O9RYAI(x=a#_l|%h2G0;!oY9GF1djKZ1X4$l)uT=QR)unp z@t#W3<#atMy;<>Lg$n$!P+fday}u8JntROryJP>Adx%lNe!=~>@mI&+#9$D=rkb(( z6M!=b1%>969V6!{9s$NaDW4&j6K( z<*08I`1|=8z}aM4D~-Swz!4Z!sYEBI1h^32XV2&&69!@e{I z8V=QB`}6d};4~`HId2Q`9Q7$6kwD~QZ;mpZqi3*T+XCp`T_BUnjVSzj%<|U)@EGYv zJqtcBwncQQywMS6ID;h&dRUa`gJUPq`TbB@VZABKrC9bX~g+g zdQ`qqJuZ4CAjF`koE$FdBn7MYhnj*#EDJi+R$@N_mB0=T1PVx$%LnHoF_M;?Tnz(j z_~~Mb{T;CqD6NsrEF}JdW-eNQ5xaKj+JBQ8X=yr!3pNy|GN3U*@iCkP0>=}w0c)rP zWAg8de#5O`Aux|VRdR)y8P3gPVDKF)2KEBZOhPFc^dz=s&|vT>v;P-o{~ga||Nimg zrxCJ386m5UiXtVMMWtv+_H2l(C}qpcXp4*@WmHDP&JImcp|VrR7Bb`ec&cl?f1lg$ z_Pu@oxIXXeeZ8+wyw2BoKF{Mg9*@WU0Plfj@3)~6Mqv(BCIo<%V}P|Bq>*Q(rRPu; zT|q|qGD*g?D|k*vKE=5ESyj~qk|@RFiMm`YRUXlMF$_qSV?6(?p&{BGZ{^GiUBj7E zw!RL2Oyi2nX_GQCIH0SoTD2<$0{0=55*Z?dDX(SAFL zvHXmy5`T4si|s^xCfn_ZqC(7Ebn=Z;bS&Vao!m4p6Q&Nl$cyikZHp}}Epg-)4u4gn z5x;RzH4!J%JTlX?VUdd7)8`-y11Ue^tcUzF>rs-?RxYGC=}Z@)B(at#0t0 z=DIdK&>%i$r6qAJtLUOx#5|?z>1Vi;drVz$xNbK7$e78B3qc5D@+);T(>&6`+TlmT}YUg zGYro|1D0OiQH}BJ)pa})T<2?6_ZCH;75Fhztm?RNBPp-oJl1bmXXe4|bEVqJTtWHR zZ=-iRuV3PPr@=Z+ItQruXWIOEGY-Gtg1w6w9X|S}?Vw3KM6mhK=SL=7Jjj%52_w7D z4x^!O;U~ZFT{~IU7_xsyRfQjJjMNqME7N}YF07yNQB9Hg6DJ*UU8uTOS6AKmZPMm5 zJ$GsNaB$&e_yt8IeWfGLRQNBK5$xISTZ+cQNpbw~joQ@}HS<$le=<3ZIkMyoF%? z!@RTp-qq;n5U{0Sji7;+s)r;HrVLNIjSAft1E7`h=+QYO40O|sjyn`ZNu8l{zyLoe zENuI(UBU6%wW~+d~wTgiwQYEbPI7yH~XL?2eL|4^9JN9}@5lq&N!<=ci{d z17ckwzX-tr0H;cUVH!jpoNy83qK+L2J-xEO!t=h2ngz@k-5mF-&pv0GnJJFAFgri| zX2c_o+mEHuu%pN?^Iah*e6NMWk?v0kP%H~4YF9?y2j{!VFfH)i-G-#0J+bS$kvI@VLy`$%p2$h-=o6NEw_7KmNy&Wbmr||E)y#Ibw~(+O+|A zHIW7kumZ5DeoUdCM`h{{Ym#R)JecH@qD-KpASJN@21_Dr%G-TUg5Us`1j$&MCYcs_ z0xUJ{2=UIb@3ZXJZ{NrE-^+`8ul*JSR#BYiP&h$zQ^V_!hJq)zlti$AL!bo?_*)By z`Y#v^a7U`|-FvB3_tFAp;c~g@D;EV-doYweX(YNufa~|s+0*}P_}zBbqMvzd;s0;{ z_O`w**Ymz^D#2|K4jO(8gdY08oY6{D>;G40bl4P$yFzCmH8NhPWB^aB>cAdjfXJ$^ z&z-H9+9;8HHtkac%6LF!JcZ{1=K!nJ+-vXbJoJq($^*}4rhJz`tfD^|rIEZZ%9w{Q z=iT(OWlkrhef2I(KH( z(;JulIr-&I8*kgWbM?m4I=;70bA(SYjPw{^xfS@OvC=i@Wd`a&x@3964EjH6`}#LD z%Kugb7r41iF;u23F8Dxvcu#EV*#TW$qBt|&&LDiKtDr#Mqt6IP!dqL1mkCz3!4`=8 z;H9-maYB#*QKd!6hgzrTaUES`vGJgD-%8=Fe({e@-%sTH`DH)&wZ6Tk!L7YbJ%5oQ z2eJmrMa@}S6@9C%yUxmvm3o|1PlfblTK7cwgY_xj_Rn(`s~ZPwVu>Rk=h!LvoA}>x z_B*s!uu-Qv!nt{8=GafyD z|2Dr7+>Q8HJD zU0K)=iVZF-0|L%+=Psgh!7;_>G0t24kY*gTB7)%|XD6!biHc?v{Y1C~shbA#8Q1yO zbdjq8*jbdzYMsxA&kTK2fua-7RS=EZaeiw6+Qiy_eY8LC9GX6%qNE}CI*7v*l9MX1 zDCE$_VEYT$CbEU<^`fl=s{=-fDqbU((RHAQCe0mjb9)CjKP6n&tJ;bxcn~oN3?w{P z5V`o^$np*j==pE`PE9KKV0#u}jk zJ3b8B2){1*#35xbAD^nC{5yt}R@YFj_yQ44&_v|XF`7Y`Bz#&lJkm(S`<6#WLO9KdEjfL@Wjckq3{tVx78cT8%gJ)uK` zq->7111P5exLzUNnS6^vGJtbyQ?ge4UJ>{PD5O;l(s_z1%GaybD>pud`>m=C8Uv`_@ei7(l{Ujm~w~_V%}WFj>&k(=!!Dh**7G zRrsLim(QDW{g!{%0t|c8g^DZustW|Bcaks35yT_;+9u%F5rnC!KCmw=*GTRQ?*}%N zzV+VCg^_-ZfeZNEC7m4W73K|b!@*aE2q0}NhVO^uC24JB-54AhEI{ecWehc*a4 zghmWp7zfGKhektggKGFhJSz6E{dVkdZTOP&VHO zW+mUhi3>jV!AxkbcE!csYHWLSW2CQ(uYfn<70=8}*<)tK2q8`b_Cr!Z^aqW>;A1jhC2Nt?5 z606VUJ1?5HcsLm__p|Aoo9J@atB%z*EV~359Pn>mOk#jV1)9n`E?;rfM|y=%q(Dvd z#?~)2Wt6w)I&F=yV>dTAZbPYQUTC#~23{=HK{n=q83^ly;_tt8K2YL|F*NL`e|Nk$ zOvy4H0Xe`=F$+VZy7F8=SjVdu8tkgS2Hq);mNdqU!X#LkZsAEM<2 z6I3k=eFEHURvbic$uB19o1l9i{>HfXv5K!bJ*^QOYt zM}=ByOHi~xQe`Se=~ec*&b9Jj%@UM_KU7 zYrpgo=d^=z3uyqk#v4LEef$`CkP+Pz{xFo9N4c}py1L;T1e^*J8<=ShUn=57a{zW& z5USpI+|KOm>;{hnD<3k%u@iR?Bry3<*P2t$_!{#Z*eM6mlU@Y^-W^>J77&l^={Z_j|iIy*F7K|OGA)zE+)%dt>lVSvuc!5J9m@?cu(M{F4 zs`1Kjf=rHlS2HdIJO=fYm3FSl3LlVEzaYbmtX;jv?xVi=&N)=~>gdqBxw$>=RxILL z=$||wZs+qpOWP~d{a2rSQJqojDfm0vjVH83QX~RO>#p3K<%2aB9{=na&S%RAq!|t^ z6X8G$1<2(G18U#+WO+=LeJ=b$H!*gj2#J%Vs&v^y$BbbY{}VLbVBfJuX5<}(dUhG1 zc)A14M9c$@t;IB{j0tVHfqGktT6*(-t+{i(OTn3*1Pt0NKD7P)>-=%~yumL~UHyu~ z!IoDVH~m4n>$K90p|bRnb6K?IU8Y>M`(h$OggdkY4et7d$8J5E-tv0gB4dG$-Ulq? zw)hO2@vFZ+3rB_lCZ08V0$vr{#H8P{JnM@wuFi5$dN02hdsn1 zD&I-%&*P~fAzgJ-L96NFVJL6t`PV4$E6`7aA!UhoEIKGsqZ->Z;r|w>l+0gZ$wq_l zJyN5O`xIM7+wxJK(NcWb&hreM=ci?QS~}P<{dO;+NH=4(H*OGN>%TRP6HDs&yJfJI zwGn3ULm{{svS_R%WrW3??)C)LxYJ2}e@mxrOVM@=H13U*TXId};Q07new|(1ihS>B zba_T{=>Gm-a%Y$}!A%VNVcRR&tvHtMW}6)+o>6h}UDZv`wU@>jYH!LpOHp>FyGz@r zTx?QX_+kWT$%Opwit&lp#?!7dG%vsXWs)Pe&tUZ{NizfK3JwQL(eHzv&+MjXsA}`p zy?#Y6UHuo2>_5xn@L8iRBfd*Z1W7BU$P-LC>-{*$)(xVR;^B<<{GvLGTA{mLM(3TL z?!JLdR!my6KUrRCYF_dbXYg3@lyysI_dv)ey_g=B-x?w{8Q z8Uz0TlK^jHBcnl}94(&(aZU!kI02y4Xu>KvMy0ip`VXa^q%Jv{+QOO=o|#f+Ar;Dw zJfIX`o>-87+>#|rFeXmFs3tu(WtWr7ewmTC*<#>J@M(`JI_mK9@*SHUpMG`?dy%X7 z&%5x3+%ONn>v75xPmSI~i+K>)ofUQAw77Y;V=*ATFyNDp(O?R}P-fD|Ll$`d@1`Ab z3F3Ya4hhNp|83e+oO%NQ-fC;ibqYKB@FVKBZ?Aa8X1(+H(VdX?8EKlRtveTzXb8Iw%^uWsPH^}yrj49Fx%xFk-X3LYgx(ttL6>lB?+t3Rx&ZAGg64Cke+9bf`qhm^ZLr%{bB1a7^$hI zFn-{WWlqZLm`@>)L7m_B&ML<0*V=SVIK21(a{*6x&QC;{Vcdq90>iAE^(s7F7*FyK zJRN4YYbi{i_AlqJEn~>Gw)c+_l)m6mwtQv9DrlOVD3|# zmPy~1vcmHMRIBcOu}1^V9kC$N;Vm^k)4*1j7_X@6R)r(SjxCj!=SS|hhA^sd-@|D` zi~cx)c;BvB%X&`tY$Shu1=-5sp7u@H^r6KIgfC2O`F85XdnjEHhCe3b3rND??F(Xj zXMiGBiFH2LMT7ATHtG=GDB1y3j`}?{dnw|&+?sh4P0FVn$E9##!WexX^u6PKhgK~j zyAW`^W5LT=tO9wCL+eR5CV@zuCsO#1g4_T*=}PO&D0r2`kEjOe>fhaeJe-?(d+HJ58ceHFGu4Xv_;yL7CQ)QK z+cmB%6{sI-K(c7ygdIRPqi+rXh4dTZwG!J7eml=n?ii`KM+k})YO-HGcjb|Kn@;^4 z!E3@!QtOFVME!L_6*~Fj>rSzVR|13~rGwG6)=h<-u+gw}=^Bu>?dX_U7 zPi6MsT7P7h?}b;6M&bI!+9zjcJcuspH9f>glJhu`2!;lhK=gGCduwhBWBk!UE|D_Sp zZ@HQrulM<5+<}i$F-kcRMO%$-%}9{7hwxH}Ec>2S48oMgqOckYJz~n~`=8H(oiW1#bsjoSnDf znZo3eLr^f3IGylk@q% #t3a;IN-c=hMnG)RytUF{!>?RrIQ?YiHCymY?6%?0WhK zBppxOSa?~DoZ6vM7T`TvcB>^##lWb%{4rh9Yt?#A%iV&UzUF!#Ii!3`s<~AfS@gx-nmoNe@7=E(o#Q`p)uu$&Mc`+3$BEY0s@(q#XZT&bq|ece1P-?y zixTw{0m{s^H7?TulvQVt^(H}1%uHgoDx&6Y!qY`JOd`~|7YEVdQaG1>&=6z`YJ zk#J;XMve#Z5*{JfCA5ibKnq`?iuy zDL|EA6;j}XjhPRta1_T|={$$j)XNAT({=P-T>j-zpF=&T|ITj5G|svuxOj3Iocx6e zA_=^s_DU#WD+Z9)0?UNR5P&R4x)vTDkFgq`!Wk*D_y`khGqC@eSXrL|7B;eY!zug= zH{|IPsx5n2+$RRER?cfWWw*-e#pu=Qjx~qwJzP5}zwwqEeeV98$MkITY$xhqmQw9% zW)q_Q39baJX;M(1@$~2#COvq-N}i~~nMs>a0XcRIg|2IrFq3Ow$|U-6reAg1Dc^!; zf@Q!fxMPWY^d@lzwb=~)Vw9kVz#5DJlM(R9x{u>@Bn?NzQeq-Ppr(!tA+Rz8ItRH7 zuncrbcbuJ>_~enlhc9d>esz6#eC=^<-9~>J;^3l{`ABm!t${O^lOXQus$6NA`$eiA0A?+GElg=yU zI4iVJ-O|YFRVIT$!Eg4>T(KxGOKsS&fvh2c*h>~^;B2n5X6nGYgVtgndB&X^ekTIY z^{dy(*-nlGeyBqmyLCwBq;RvGfKG3V!#sXn^)2Jx~jsH6S*Q{Ssr z!#hrjL)VeyoDhPXBeQCfA%$^?z{Y?4;Bscs z8=Vs+FUg>`?ys*1TxoDDiOs1K0|jCOKv_xxG-6w}7)F$Bpa^pU7&F*lA;begdj3^P zO4p)|v{an#>dVe&ZkM9nZ^C%;EPx`z5^HyAVolzZW@? zFi7o~@Ji5PiOJSIT!9WSnL(Q`e!ghLpq#5c!F^Kz61(; z7DF31?(;mqjn>rE)L08y*vm}#_BhUWEnac2_XIi@cDg`nj&u>EVD_bpl`$-rp=(6< zUhVjgu9k7xRMsa=^CSP_0>_F)V~Pu37&&+`(Kg5O4G^i1X%=;HA_a*bq2d)Z^5$yQ z){3W8O;O146|-llZ+Dj`vc5GVSJA}&H%w=)4b!^32SJ0P*_ka^g+Ov+h$XiB^O}&! zeWLcQ3lPreF!7oU4&Q~c3bt#Uv4z`HysTc!DyRSZ zCARMJ8?31T^ApA|Xeis^3k9YJ^`x{@$8Ms%W7dgoXWlQ-cXFAV$6W1bO%+{QT~>Vy zJH{qUKK^&x)tv41`UQhM4@@$y8$|vdCjP!>O~kffNI*FL5HSWDh@fU)IKcouKe35m z!bGTJ%>INP9rih@h5JY{BexKm!@5RWBzPQ&w7r zF1}e3CtZ)gBy%{w(yf9JwR-0G+b5db|2m&WxIV!c?vIA;HhhnW(D}iANt9Ihmq_aj zm9R}XG;gVdZtL>}hcfJs-62Oq7)m#oKW$E>HL{#J-O{gKgEM!af+>vrm{(&-785f% z*IvHdv*f})LI=Woyp+sPQXf3Xu&G~*aRDQ29_l70LitN#E+}~Fh5Giay?sZSop&9t z!?v6vv5_!9Q?Qg`*_9}znLny9yOBS(G+?pxu3rbpk4*goeEU6O zQ19sJVZmA8A#}w+&?r?j^LpGJFkc-|ZeZSaF@f$5p~+3R#4 z$elepu$CkQ&-+hQB|o_D0!)iNTVs)p;X7JVM>?CcH?#HYimzTRZRPR#;iX_{=c8^x z%eZGZ8!31$a5*51_kKG8uX@eSB%O|xv;D*Xj3XTtEHvwTEpnBm-!ZA~?xv4%|NBBm z4GwFR=?B*kIaMZN{=p?}hZb+wm6BYZd1I^UO}5rs!Yxr3-j!B7?l<0pnI2}nPqd6X z3rr)QMZE2AI8xU`TUVJc{hxv_q2zKo_R(ZuE{ zcA4;+v9s`kVtZnM7F~3kUAJYU<|{S*ctPogchV<4lhlHo@37jvo>JqVcuSx-h=ajWp$Q7&D+okOA!wS-wMHFkn3a3ne#=(FCj= z{s38G8pTKest64%95L9Ws(yFB%J;Ji>V9(9XEp6S8!GW|Y>!~LIka{pd>#yMUF23Y z$YrWEZF%ui^U|Ec=CFDHDOm)~-=7lrhjw6=5wdl@C^APN8c~5WE{iFV>p_Fl`Aw~# zUss8A%EB|H|1J1ZJ7( z8eEH2Ro^c7Gki_;a<=uYew-Dw7!a_UrydKPg?j0bEUgWiT+O{J_vN$8i!V&hSa<)L z)+{daI(&Z0%UAViVS%@dYIOEC$ZSfJJV!i+BNzWp3*fhAS}fqb zV)p0cw)8j22 z;C*YE++%t-#Vw3?RoN&$>FlM`^7)IuHL7+F{c`*GqfWt_L+{fTvpP8?Rcl+z?hL+A z@l`t3)0d|yN^0YC0iH!aY1i|yHeoCoCaB7UoZZBh`#Cb1+gpZa*Dx)4+$SD9%wL~+ z=caR-1jsaTwga9NrN2)UCW!Uh&M+jzH+QhlodWi=rx%mIP9AA5xRxLfy>D)ei&X*y zpXP}DQ>k`eiwjK#2lm(Wtd!W-I(}UyxwiDzrP_$@n4VqFhu58J7gwz7T+qM$Y|4bB za#~3CX0?C!HFKYLb%3|*^I-0e6FbFr{g7~-b-nOYEt}I*JxnEedZLdXC=QBp4Rho4 zsL*zODS7&O26sn0r8i?ug7$)_^qANShR$0c5PZE zNX1?-1QKPNvWcZcHX`bg34*rd(%^R^u0rDyYEKp0QlaWdO25$bzt^p z)&G1Yf3v)Hr3#A~YG|_FXcijJen0Sf#_;*m<3jZX2Q`didQ>mgye)fI!9!i*7YM6d z(Eoj@f^YmfdnaFY`xHID^p&S88nyQCSk?0&>Cd}k?yKeq@Ro4;S-d9Z;!`#%!zt^T z-;u3mM~<0)j@$hjZPQSFl`Z96w{@!ZM!Du5bk49@^5vng7TAnf0|dF`_vu0XOAX_W zYidx+jwYLLXWxzf<`*-$Nhu5+dl`&>)Zo~sGYWaj8yh;mzi){76S2SmorK}xdBD15 z{iFq#cSx32FJ5AH#IUBvW$GmR+U+cEA0Nwiw@=GRK6p^wlRJNU_&||e?L=gN>aFGu z`nkg-aIMm_bT4CnBhf~G3pSd+8)_R0>t$cL^B(@TUQG^_IEdGb=kM4(rtgbc!?|(I zB`;6cmOTnDSIOv{VTdoR(oVq4V@^4N)Lv2W;T13$g*DmPxylLIMt3WEwybOOda*uv zbDn45-(5ns{Y~JYifd~#cio5wo0=c6>6H3(rWW=+95PSNmiHW_*YMiy^I)5KGRLTv zT8xdF&SF6crLH`8qrvGO|HRbK6h(lL*FIXhZY@mXduiD9 z-(LeKk{Xi=>N4MSeVgXO`FR(uh6WybCkr3g8*?bFl4`Wd^dcFpG9LBvO9OTyUQq?lWoR&O{Dw2the_`|b!p2Q5 z3{)p)?qCzUCN&JWb9;Qfv|c`q|tJw;QkL0Zm21Gatevxg43I zg_yP+)1;I;-q6N0yBshT?Ox)ssb0NH^FYaYamC3__TKF~3#9_e+4|P#SdCI;5Fj_M)r;l;T@grWK~2 zIBWzs(LszeG0kKFez8RM5ONHJ%9}qDH3QZGO)g8m%RmL$vy4)oEXaZwL13?#?vwcx zqRxOg(~kAuXj${Z&-?d;raABOmXR^_`~BQ5<$Is&j1T8a?7hZvKg6J=surWs(Efy5 zbQJh7NuHl9GMs9;EzhuU@)v}Q%U(!2YB79y+qGA3j$J|(a~P8~WxPE4M!;<88WioCfW&MK!_L_7x=sy!hhM4N2nYA?jJxUNSJrn0Wa z+zlioX+4&1l*?u6^-C6XyC67@O0lX864IrE233~t?(D?Ik8)%?9J;DSAv+L5*#946 zZTg`H70Qx431|*EdK^nESTv;r-hQXxS+O+P674VXaKXrFcVlTkr&EMKJjQZ$ywg$1 ze*JN^k9y6jq+FkKiT%|s!9kz;&P-z6x=J!&#v(h{B{op)#=l55iwKdNm@t5uuGTID)MY2eCQf;L916v@XHA-j+UkjY4geeQ6KW z7kJ1Ycl~UA^4-_OLZ~0&V9XfBGSXC!m9KWc(C+z(HhjN12XfLcpl=*oY1xqyf2&|+ ztW0y*i|@zJ-rH^I2sDCB%ki)mFRrzGQ=Pql#kqFQBuNg3c}c9Z3i1s2=Q9=x&Q(YrKe20>E$5AB>_SvDO*+5lY#@LQF|?#h(Qv;%OeQU%a59AR`eCje>=d zjnKBrEwJ$zOhOen8nB22l{@AVARSQNh`+#1H#hj;T)2>q`oFLSKEuHKH55Ift?f;{iuM|YX#@!bzFwPKSi-0Vpa|89@<3L8^@$}Ux<$pc z?&Dc2wDo}tE-v=yEx<ZX%#cwh@2}mUjkWCi*>+ zdJb>-c<0d25IuDk>YZ)@l36~X!b&$eRG%l^^4x`$?#AEr5Fa{>P$0Mk0j!HvA;qU= zD#^-*QzC%)%T_GC`eFyhu(L#uq)`S4cfP0}2Cs0&+`q=n><ORZd2RpQ~3VF3>YV7$s&9Gg1^kEH03rQI-a19bq}h%H%3w2bR`_;RkDVMipku!Kf7 zaB_tNJDF^`>OWHSh_&MnPQlJXgDgSq=Z@aqP*imFIWd}hK<#VCF`k1}72d&) zKcJQpauT4rVCj~URi^l)t+3h=7w6NbPo)TX>ECK8RwH57J@+#Ew_1uboxJ^#VEG7d zD~S{$0o*ff(k1ZQnmc2$Pwm~ke8xQ1lt@lP>D6DEgNF}mpADapPINkQA~vRG_|p(Y zseah>uumglEsN#fA1@+LPEGR%hhV(T66L=qh3}{npxK5^nLuE7UD89OWw@}hrh_6t z3i67A%0WXBBLvyijg6E7`1~43PU3A^`ZkSL{QBABSboW{KqwT`@i@R}4Q!@2%ge(S z#td5(gz5_7a)c~vlgbT6f6gL4`mV{4SG8g?LBC;kh5z(~G4nO%b}dV}ET<#qG(vr_ ztHLi1yDGla`8%m^$zENsXa4W>3Cw<0g9YA2<6q`h=3r^1HiWZYeJq7CLn4H2bGVsw zr5jECJpG5X`ASNGw=1~|y!u?_%8L(u{!zbO=Jbr>FfY63=sc=8H-$HGn>))xzch3C z0P}q*^SL&hq@lJ>8;S7L3}u&_NsR=)=$}W#K8T){NMet}%rt~S`rs7Q15%ce-L5!} zBc;q~ky93M6~9MjTOlDEtmPh&2}99j%biOje!Gnn9b2qA<@}wFV9yTSKvo|Ob@s*r z$DuCsgqZpd&57sK2TtB)UN_^wK>k|(=;VPwOP#+)D^?D44$!POc}{P_vIe52;}KC6 z4=+WP3w4WpK~Ns0umRHu>uGTuzdzRa0UjVs{>?`04$BFeD!inhLlTL=M+`Q)XeufN#H*q9*G;cDt$ zs$TL=FA%+Z$HnL!@+TQ7k<9C@6U$2+em;?P)TwBD9*~J_I6DRJ=CabGuRSoP3C-L0 zJL&21?mgM~sAEAi&VXD}#i#lPZ6hBA1`pyKKn)~2S#j$j24`@_NbAd;d|NX!*kg2A z^Xv_-zVrfqNX^}ki|xHpG|@Z zTu`eZn@Tk~BnPQ|e4;Eflto7^Ivml~wP5w$`>ff{{`ez5ZqASE{K!k`NU|(^d^SNP z!k;rZ`{93-+xUp*$c48xpn=V&3s3M5idOroB!X5_qMFleFh5d;~c=TsRO@8 zc4mRr2ys9s_ZxqL=Wq2wrzdTBVsZR=*?>3S(ZyL-sq|ujI{*=?A+{oV5|sVV(yO zd`0@CaMYQpNKC>oDcTz>a+C(aCQGE`2x$zoH1hnm!c$8!Y7dd!o<_Pd2z$igZ5efx zzlT~BNtledyvuG%^;~Zn&`G`2ytZk&Z~3)|?sbZy@>{^P9II)kDC=-$>FA>OQ+^zx zt^!2R0bw1Aetd)w7LEo({X_h82{KT?4;Y5agQr9q5`g65d%n~AO1n~Exr>XRD<~+4 zvn$s%Nqjb7dQ-zhw|vy$&1ZSb4>qx)JUuhfs>xNsCl_yAcQfR6OmKd4sL8YtPLR~> zc9z