Skip to content

Commit 48f0b2f

Browse files
jClugstorclaude
andcommitted
Fix sublibrary QA findings (JET, Aqua, syntax)
Address the JET/Aqua static-analysis failures across sublibraries: JET "possibly undefined local" (conditional definition / closure boxing): - GCMAES: define the gradient closure `g` unconditionally (the QA pass's `local g` + conditional assignment is what JET flagged). - NLopt: drop the theta-memoization and write constraints/Jacobian into the preallocated caches in place, so no captured variable is reassigned/boxed. - OptimizationBase (DISparseExt): hoist `cons_oop`/`lagrangian` out of the `if f.cons === nothing` else-branch so they are defined on all paths. JET "no matching method ... ::Nothing": - PRIMA: `get_solve_func` now errors in an else branch, so its return type (and `optfunc`) no longer includes Nothing. - NOMAD: merge the `bb`/`bbcons` definition and the NomadProblem construction into a single if/else so the callback isn't seen as possibly-undefined. Real bugs surfaced by JET: - SciPy: `pytypeof` -> `pytype` (pytypeof does not exist in PythonCall; the fallback branch would have errored). - SimpleOptimization: guard `open_brkt_ind` against `nothing` before subtracting, in the AD-package-not-loaded error path. Aqua / syntax: - Manopt: remove the stray second `;` in the `Aqua.test_all` call (syntax error). - QuadDIRECT: add `Test` compat (Aqua check_extras) and raise SciMLBase floor to 2.122.1 to match OptimizationBase. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 05b7d92 commit 48f0b2f

9 files changed

Lines changed: 48 additions & 58 deletions

File tree

lib/OptimizationBase/src/OptimizationDISparseExt.jl

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -142,25 +142,25 @@ function instantiate_function(
142142
cons = let f = f, p = p
143143
(res, θ, p_call = p) -> f.cons(res, θ, p_call)
144144
end
145+
end
145146

146-
function cons_oop(x)
147-
_res = zeros(eltype(x), num_cons)
148-
f.cons(_res, x, p)
149-
return _res
150-
end
147+
function cons_oop(x)
148+
_res = zeros(eltype(x), num_cons)
149+
f.cons(_res, x, p)
150+
return _res
151+
end
151152

152-
function cons_oop(x, i)
153-
_res = zeros(eltype(x), num_cons)
154-
f.cons(_res, x, p)
155-
return _res[i]
156-
end
153+
function cons_oop(x, i)
154+
_res = zeros(eltype(x), num_cons)
155+
f.cons(_res, x, p)
156+
return _res[i]
157+
end
157158

158-
function lagrangian(θ, σ, λ, p)
159-
if eltype(θ) <: SparseConnectivityTracer.AbstractTracer || !iszero(θ)
160-
return σ * f.f(θ, p) + dot(λ, cons_oop(θ))
161-
else
162-
return dot(λ, cons_oop(θ))
163-
end
159+
function lagrangian(θ, σ, λ, p)
160+
if eltype(θ) <: SparseConnectivityTracer.AbstractTracer || !iszero(θ)
161+
return σ * f.f(θ, p) + dot(λ, cons_oop(θ))
162+
else
163+
return dot(λ, cons_oop(θ))
164164
end
165165
end
166166

@@ -452,10 +452,10 @@ function instantiate_function(
452452
cons = let f = f, p = p
453453
(x, p_call = p) -> f.cons(x, p_call)
454454
end
455+
end
455456

456-
function lagrangian(θ, σ, λ, p)
457-
return σ * f.f(θ, p) + dot(λ, f.cons(θ, p))
458-
end
457+
function lagrangian(θ, σ, λ, p)
458+
return σ * f.f(θ, p) + dot(λ, f.cons(θ, p))
459459
end
460460

461461
cons_jac_prototype = f.cons_jac_prototype

lib/OptimizationGCMAES/src/OptimizationGCMAES.jl

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,9 @@ function SciMLBase.__solve(cache::OptimizationCache{O}) where {O <: GCMAESOpt}
7272
return x[1]
7373
end
7474

75-
local g
76-
if !isnothing(cache.f.grad)
77-
g = function (θ)
78-
cache.f.grad(G, θ)
79-
return G
80-
end
75+
g = function (θ)
76+
cache.f.grad(G, θ)
77+
return G
8178
end
8279

8380
maxiters = OptimizationBase._check_and_convert_maxiters(cache.solver_args.maxiters)

lib/OptimizationManopt/test/qa/qa.jl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ using Test
99
# Manifolds is declared because the curvature analysis path may pull it in,
1010
# but no symbol from it is currently used in src — ignore it for now.
1111
Aqua.test_all(
12-
1312
OptimizationManopt;
14-
undefined_exports = (; broken = true)
15-
;
13+
undefined_exports = (; broken = true),
1614
stale_deps = (; ignore = [:Manifolds])
1715
)
1816
end

lib/OptimizationNLopt/src/OptimizationNLopt.jl

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,9 @@ function SciMLBase.__solve(cache::OptimizationCache{O}) where {
196196
eqinds = map((y) -> y[1] == y[2], zip(cache.lcons, cache.ucons))
197197
ineqinds = map((y) -> y[1] != y[2], zip(cache.lcons, cache.ucons))
198198
cons_cache = zeros(eltype(cache.u0), sum(eqinds) + sum(ineqinds))
199-
thetacache = rand(size(cache.u0))
200-
Jthetacache = rand(size(cache.u0))
201199
Jcache = zeros(eltype(cache.u0), sum(ineqinds) + sum(eqinds), length(cache.u0))
202200
evalcons = function (θ, ineqoreq)
203-
if thetacache != θ
204-
cache.f.cons(cons_cache, θ)
205-
thetacache = copy(θ)
206-
end
201+
cache.f.cons(cons_cache, θ)
207202
if ineqoreq == :eq
208203
return @view(cons_cache[eqinds])
209204
else
@@ -212,11 +207,7 @@ function SciMLBase.__solve(cache::OptimizationCache{O}) where {
212207
end
213208

214209
evalconj = function (θ, ineqoreq)
215-
if Jthetacache != θ
216-
cache.f.cons_j(Jcache, θ)
217-
Jthetacache = copy(θ)
218-
end
219-
210+
cache.f.cons_j(Jcache, θ)
220211
if ineqoreq == :eq
221212
return @view(Jcache[eqinds, :])'
222213
else

lib/OptimizationNOMAD/src/OptimizationNOMAD.jl

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,23 @@ function SciMLBase.__solve(
7575
end
7676

7777
bb = nothing
78-
bbcons = nothing
78+
bounds = (;)
79+
if !isnothing(prob.lb)
80+
bounds = (; bounds..., lower_bound = prob.lb)
81+
end
82+
83+
if !isnothing(prob.ub)
84+
bounds = (; bounds..., upper_bound = prob.ub)
85+
end
86+
7987
if prob.f.cons === nothing
8088
bb = function (x)
8189
l = _loss(x)
8290
success = !isnan(l) && !isinf(l)
8391
count_eval = true
8492
return (success, count_eval, [l])
8593
end
94+
opt_setup = NOMAD.NomadProblem(length(prob.u0), 1, ["OBJ"], bb; bounds...)
8695
else
8796
eqinds = findall(i -> prob.lcons[i] == prob.ucons[i], 1:length(prob.ucons))
8897
bbcons = function (x)
@@ -97,20 +106,6 @@ function SciMLBase.__solve(
97106
count_eval = true
98107
return (success, count_eval, vcat(l, c))
99108
end
100-
end
101-
102-
bounds = (;)
103-
if !isnothing(prob.lb)
104-
bounds = (; bounds..., lower_bound = prob.lb)
105-
end
106-
107-
if !isnothing(prob.ub)
108-
bounds = (; bounds..., upper_bound = prob.ub)
109-
end
110-
111-
if prob.f.cons === nothing
112-
opt_setup = NOMAD.NomadProblem(length(prob.u0), 1, ["OBJ"], bb; bounds...)
113-
else
114109
opt_setup = NOMAD.NomadProblem(
115110
length(prob.u0), 1 + length(prob.ucons),
116111
vcat("OBJ", fill(strcnsmethod(cons_method), length(prob.ucons))),

lib/OptimizationPRIMA/src/OptimizationPRIMA.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ function get_solve_func(opt::PRIMASolvers)
8080
return PRIMA.lincoa
8181
elseif opt isa COBYLA
8282
return PRIMA.cobyla
83+
else
84+
error("Unknown PRIMA solver type: $(typeof(opt))")
8385
end
8486
end
8587

lib/OptimizationQuadDIRECT/Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ Pkg = "1"
1919
julia = "1.10"
2020
OptimizationBase = "5"
2121
QuadDIRECT = "0.1.2"
22-
SciMLBase = "2.58, 3"
22+
SciMLBase = "2.122.1, 3"
2323
SafeTestsets = "0.1"
2424
Reexport = "1.2"
25+
Test = "1.10"
2526

2627
[targets]
2728
test = ["Pkg", "Test", "SafeTestsets"]

lib/OptimizationSciPy/src/OptimizationSciPy.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function safe_get_message(result)
2929
if pyisinstance(msg, pybuiltins.list) || pyisinstance(msg, pybuiltins.tuple)
3030
return join(pyconvert(Vector{String}, msg), ", ")
3131
end
32-
return string(pytypeof(msg))
32+
return string(pytype(msg))
3333
end
3434

3535
# Squash any kind of numeric object down to a Julia Float64.

lib/SimpleOptimization/src/SimpleOptimization.jl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,15 @@ function instantiate_gradient(f, adtype::ADTypes.AbstractADType)
155155
strtind = isnothing(_strtind) ? 5 : _strtind + 5
156156
open_nrmlbrkt_ind = findfirst('(', adtypestr)
157157
open_squigllybrkt_ind = findfirst('{', adtypestr)
158-
open_brkt_ind = isnothing(open_squigllybrkt_ind) ? open_nrmlbrkt_ind :
158+
open_brkt_ind = if isnothing(open_squigllybrkt_ind)
159+
open_nrmlbrkt_ind
160+
elseif isnothing(open_nrmlbrkt_ind)
161+
open_squigllybrkt_ind
162+
else
159163
min(open_nrmlbrkt_ind, open_squigllybrkt_ind)
160-
adpkg = adtypestr[strtind:(open_brkt_ind - 1)]
164+
end
165+
lastidx = isnothing(open_brkt_ind) ? lastindex(adtypestr) : (open_brkt_ind - 1)
166+
adpkg = adtypestr[strtind:lastidx]
161167
throw(ArgumentError("The passed automatic differentiation backend choice is not available. Please load the corresponding AD package $adpkg."))
162168
end
163169

0 commit comments

Comments
 (0)