From fd2d47259f6d7adea5bdb007f46eb2f3a430eac0 Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Tue, 1 Sep 2026 10:10:15 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20finish=20the=20coherence=20pass=20?= =?UTF-8?q?=E2=80=94=20guided=20tour=20line=20width=20+=20NonlinearSolve.s?= =?UTF-8?q?olve?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase E4 part 2 (#918) deferred getting-started/guided-tour.md to E5, and E5 (#920) made it execute without applying the coherence fixes. Close the gap at the Literate source: - wrap the six over-75 lines in `@example` blocks (constraint!, the two println, plot!, trim two long comments); - `solve(prob; …)` -> `NonlinearSolve.solve(prob; …)`, the last unqualified shooting call site (F25). Also the two files touched after the wrap pass: - examples/logo.md — four lines at 76-81 (circle, Axis, two padded comments), all brought within 75; - modelling/abstract-syntax.md — one 77-char comment trimmed. Verified: Literate regen of guided-tour.md has 0 lines over 75 in executable blocks; logo.md example blocks parse clean. Co-Authored-By: Claude Sonnet 5 --- docs/src-literate/guided-tour.jl | 28 +++++++++++++++++++-------- docs/src/examples/logo.md | 12 ++++++++---- docs/src/modelling/abstract-syntax.md | 2 +- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/docs/src-literate/guided-tour.jl b/docs/src-literate/guided-tour.jl index 9b00f9a7b..948dddd18 100644 --- a/docs/src-literate/guided-tour.jl +++ b/docs/src-literate/guided-tour.jl @@ -127,7 +127,10 @@ function boundary_energy!(b, x0_, xf_, v) b[4] = xf_[2] - xf[2] return nothing end -constraint!(pre, :boundary; f=boundary_energy!, lb=zeros(4), ub=zeros(4), label=:endpoint) +constraint!( + pre, :boundary; + f=boundary_energy!, lb=zeros(4), ub=zeros(4), label=:endpoint, +) lagrange_energy(t, x, u, v) = 0.5 * u[1]^2 objective!(pre, :min; lagrange=lagrange_energy) @@ -145,7 +148,7 @@ definition(ocp) # the macro records the full DSL expression #- -has_abstract_definition(ocp_func) # false: functional API stores no abstract definition +has_abstract_definition(ocp_func) # false: no symbolic definition #md # !!! warning "Two things to keep in mind" #md # - In the functional API, callbacks are **always vector-valued**: even when the control is scalar, one writes `u[1]` — not `u` — inside `f_energy!` or `lagrange_energy`. @@ -274,8 +277,14 @@ using MadNLP sol_ipopt = solve(goddard; grid_size=250, display=false) sol_madnlp = solve(goddard, :madnlp; grid_size=250, display=false) -println("Ipopt : r(tf) = ", objective(sol_ipopt), ", ", iterations(sol_ipopt), " iters") -println("MadNLP : r(tf) = ", objective(sol_madnlp), ", ", iterations(sol_madnlp), " iters") +println( + "Ipopt : r(tf) = ", objective(sol_ipopt), + ", ", iterations(sol_ipopt), " iters", +) +println( + "MadNLP : r(tf) = ", objective(sol_madnlp), + ", ", iterations(sol_madnlp), " iters", +) # The available methods and their options can be inspected with `methods()` and `describe(:collocation)`; we will not dwell on them here. @@ -283,7 +292,7 @@ println("MadNLP : r(tf) = ", objective(sol_madnlp), ", ", iterations(sol_madnlp) # # A solution can be passed **directly** as the initial guess of another solve — it is interpolated onto the new grid. This makes discrete continuation trivial and ties back to the initialisation above. On this nonlinear problem it genuinely **pays**: we compare reaching a fine grid of 1000 two ways — a **cold start** (solve `grid_size=1000` directly) versus a **cascade** (solve `grid_size=50` first, then `grid_size=1000` warm-started with that solution). -## solutions computed once, reused for iteration counts and the overlay plot +## computed once, reused for iteration counts and the overlay plot sol_cold = solve(goddard; grid_size=1000, display=false) ## warm cascade: grid 50 first, then grid 1000 initialised from it @@ -306,7 +315,7 @@ plot!(plt, s1000; label="1000") # How much better is the optimal solution compared to a naive strategy? We simulate **full thrust until fuel depletion, then coast to apogee** — a bang-bang profile with no optimisation, just two ODE integrations with callbacks. -using OrdinaryDiffEqTsit5 # ODE solver (callbacks for the bang-bang simulation) +using OrdinaryDiffEqTsit5 # ODE solver (bang-bang simulation callbacks) ## Phase 1: u = 1, stop when m = mf (fuel depleted) bang1!(dx, x, p, t) = (dx[:] = F0(x) + F1(x)) @@ -356,7 +365,10 @@ t_bang = [sol_bang1.t; sol_bang2.t] r_bang = [sol_bang1[1, :]; sol_bang2[1, :]] plt_bang = plot(sol_cold; label="optimal", linewidth=2, color=1) -plot!(plt_bang[1], t_bang, r_bang; label="bang-bang", linestyle=:dash, linewidth=2, color=2) +plot!( + plt_bang[1], t_bang, r_bang; + label="bang-bang", linestyle=:dash, linewidth=2, color=2, +) plot(plt_bang[1]; legend=:bottomright, xlabel="time", ylabel="altitude") #src ============================================================================ @@ -456,7 +468,7 @@ p_of_t = costate(direct_sol) # costate as a function of time p0_guess = p_of_t(t0) # initial costate from the direct method prob = NonlinearProblem(nle!, p0_guess) -shooting_sol = solve(prob; show_trace=Val(true)) +shooting_sol = NonlinearSolve.solve(prob; show_trace=Val(true)) p0_sol = shooting_sol.u println("costate p0 = ", p0_sol) diff --git a/docs/src/examples/logo.md b/docs/src/examples/logo.md index 4c478a5f6..b23b306e8 100644 --- a/docs/src/examples/logo.md +++ b/docs/src/examples/logo.md @@ -117,7 +117,8 @@ orbit to the outer one. xt = state(sol) arc = [Point2f(xt(t)[1], xt(t)[2]) for t in range(0, tf; length = 500)] -circle(r) = [Point2f(r * cos(a), r * sin(a)) for a in range(0, 2π; length = 300)] +circle(r) = + [Point2f(r * cos(a), r * sin(a)) for a in range(0, 2π; length = 300)] fig = Figure(size = (460, 460)) ax = Axis(fig[1, 1]; aspect = DataAspect()) @@ -141,18 +142,21 @@ orbit in Julia blue and the central body, and the logo is done. using Colors rot(ψ) = [cos(ψ) -sin(ψ); sin(ψ) cos(ψ)] -jl = Colors.JULIA_LOGO_COLORS # (red, green, blue, purple) +jl = Colors.JULIA_LOGO_COLORS # (red, green, blue, purple) # departure points placed as the Julia-logo dots — green on top arms = [(7π / 6, jl.red), (π / 2, jl.green), (11π / 6, jl.purple)] fig = Figure(size = (600, 600), backgroundcolor = :transparent) -ax = Axis(fig[1, 1]; aspect = DataAspect(), backgroundcolor = :transparent) +ax = Axis( + fig[1, 1]; + aspect = DataAspect(), backgroundcolor = :transparent, +) hidedecorations!(ax) hidespines!(ax) limits!(ax, -1.32rf, 1.32rf, -1.32rf, 1.32rf) -poly!(ax, circle(rf); color = :white) # white disk behind everything +poly!(ax, circle(rf); color = :white) # white disk behind everything for (ψ, c) in arms P = [Point2f(rot(ψ) * p) for p in arc] diff --git a/docs/src/modelling/abstract-syntax.md b/docs/src/modelling/abstract-syntax.md index 003337ece..183f051d1 100644 --- a/docs/src/modelling/abstract-syntax.md +++ b/docs/src/modelling/abstract-syntax.md @@ -452,7 +452,7 @@ nothing # hide u ∈ R, control -1 ≤ v ≤ 1 x₁(0) == -1 - x₂(0) - v == 0 # OK: the boundary constraint may involve the variable + x₂(0) - v == 0 # OK: this side may involve the variable x(1) == [0, 0] ẋ(t) == [x₂(t), u(t)] ∫(0.5u(t)^2) → min