Skip to content

Commit 439789f

Browse files
committed
move from Nonconvex
1 parent 4b6c59a commit 439789f

File tree

3 files changed

+284
-5
lines changed

3 files changed

+284
-5
lines changed

Project.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,22 @@ uuid = "4296f080-e499-44ba-a02c-ae40015c44d5"
33
authors = ["Mohamed Tarek <[email protected]> and contributors"]
44
version = "0.1.0"
55

6+
[deps]
7+
ADNLPModels = "54578032-b7ea-4c30-94aa-7cbd1cce6c9a"
8+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
9+
NLPModelsModifiers = "e01155f1-5c6f-4375-a9d8-616dd036575f"
10+
NonconvexCore = "035190e5-69f1-488f-aaab-becca2889735"
11+
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
12+
Percival = "01435c0c-c90d-11e9-3788-63660f8fbccc"
13+
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
14+
615
[compat]
16+
ADNLPModels = "0.1, 0.2, 0.3"
17+
NLPModelsModifiers = "0.1, 0.2, 0.4"
18+
NonconvexCore = "0.1.4"
19+
Parameters = "0.12"
20+
Percival = "0.3.1"
21+
Reexport = "1"
722
julia = "1"
823

924
[extras]

src/NonconvexPercival.jl

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,168 @@
11
module NonconvexPercival
22

3-
# Write your package code here.
3+
export PercivalAlg, PercivalOptions, AugLag, AugLagOptions
4+
5+
import Percival, NLPModelsModifiers, ADNLPModels
6+
using Reexport, Parameters
7+
@reexport using NonconvexCore
8+
using NonconvexCore: @params, VecModel, AbstractOptimizer
9+
using NonconvexCore: AbstractResult, CountingFunction, getnconstraints
10+
import NonconvexCore: Workspace, reset!, optimize!
11+
12+
struct PercivalAlg <: AbstractOptimizer end
13+
const AugLag = PercivalAlg
14+
15+
@params struct PercivalOptions
16+
nt::NamedTuple
17+
end
18+
function PercivalOptions(; first_order = true, memory = 5, kwargs...)
19+
return PercivalOptions(
20+
(;first_order = first_order,
21+
memory = memory, inity = ones,
22+
kwargs...),
23+
)
24+
end
25+
const AugLagOptions = PercivalOptions
26+
27+
@params mutable struct PercivalWorkspace <: Workspace
28+
model::VecModel
29+
problem::Percival.NLPModels.AbstractNLPModel
30+
x0::AbstractVector
31+
options::PercivalOptions
32+
counter::Base.RefValue{Int}
33+
end
34+
function PercivalWorkspace(
35+
model::VecModel, x0::AbstractVector = getinit(model);
36+
options = PercivalOptions(), kwargs...,
37+
)
38+
problem, counter = get_percival_problem(model, copy(x0))
39+
return PercivalWorkspace(model, problem, copy(x0), options, counter)
40+
end
41+
@params struct PercivalResult <: AbstractResult
42+
minimizer
43+
minimum
44+
problem
45+
result
46+
fcalls
47+
end
48+
const AugLagWorkspace = PercivalWorkspace
49+
50+
function optimize!(workspace::PercivalWorkspace)
51+
@unpack problem, options, x0, counter = workspace
52+
counter[] = 0
53+
m = getnconstraints(workspace.model)
54+
problem.meta.x0 .= x0
55+
result = _percival(problem; options.nt...,
56+
inity = options.nt.inity(eltype(x0), m))
57+
result.solution = result.solution[1:length(x0)]
58+
return PercivalResult(
59+
copy(result.solution), result.objective, problem, result, counter[],
60+
)
61+
end
62+
63+
function _percival(nlp;
64+
T = eltype(nlp.meta.x0),
65+
μ::Real = convert(T, 10),
66+
max_iter::Int = 1000, max_time::Real = convert(T, Inf),
67+
max_eval::Int = 100000, atol::Real = convert(T, 1e-6),
68+
rtol::Real = convert(T, 1e-6), ctol::Real = convert(T, 1e-6),
69+
first_order = true, memory = 5,
70+
subsolver_logger::Percival.AbstractLogger = Percival.NullLogger(),
71+
inity = nothing, max_cgiter = 100, subsolver_max_eval = 200, kwargs...,
72+
)
73+
modifier = m -> begin
74+
op = NLPModelsModifiers.LinearOperators.LBFGSOperator(T, m.meta.nvar; mem = memory)
75+
return NLPModelsModifiers.LBFGSModel(m.meta, m, op)
76+
end
77+
_kwargs = (
78+
max_iter = max_iter, max_time = max_time,
79+
max_eval = max_eval, atol = atol, rtol = rtol,
80+
subsolver_logger = subsolver_logger,
81+
subproblem_modifier = first_order ? modifier : identity,
82+
subsolver_max_eval = subsolver_max_eval,
83+
subsolver_kwargs = Dict(:max_cgiter => max_cgiter),
84+
)
85+
if Percival.unconstrained(nlp) || Percival.bound_constrained(nlp)
86+
return Percival.percival(
87+
Val(:tron), nlp; _kwargs...,
88+
)
89+
elseif Percival.equality_constrained(nlp)
90+
return Percival.percival(
91+
Val(:equ), nlp; inity = inity, _kwargs...,
92+
)
93+
else # has inequalities
94+
return Percival.percival(
95+
Val(:ineq), nlp; inity = inity, _kwargs...,
96+
)
97+
end
98+
end
99+
100+
function Workspace(model::VecModel, optimizer::PercivalAlg, args...; kwargs...,)
101+
return PercivalWorkspace(model, args...; kwargs...)
102+
end
103+
104+
function reset!(w::AugLagWorkspace, x0 = nothing)
105+
w.counter[] = 0
106+
if x0 !== nothing
107+
w.x0 .= x0
108+
end
109+
return w
110+
end
111+
112+
function get_percival_problem(model::VecModel, x0::AbstractVector)
113+
eq = if length(model.eq_constraints.fs) == 0
114+
nothing
115+
else
116+
model.eq_constraints
117+
end
118+
ineq = if length(model.ineq_constraints.fs) == 0
119+
nothing
120+
else
121+
model.ineq_constraints
122+
end
123+
obj = CountingFunction(getobjective(model))
124+
return get_percival_problem(
125+
obj,
126+
ineq,
127+
eq,
128+
x0,
129+
getmin(model),
130+
getmax(model),
131+
), obj.counter
132+
end
133+
function get_percival_problem(obj, ineq_constr, eq_constr, x0, xlb, xub)
134+
T = eltype(x0)
135+
nvars = length(x0)
136+
if ineq_constr !== nothing
137+
ineqval = ineq_constr(x0)
138+
ineq_nconstr = length(ineqval)
139+
else
140+
ineqval = T[]
141+
ineq_nconstr = 0
142+
end
143+
if eq_constr !== nothing
144+
eqval = eq_constr(x0)
145+
eq_nconstr = length(eqval)
146+
else
147+
eqval = T[]
148+
eq_nconstr = 0
149+
end
150+
c = x -> begin
151+
if ineq_constr !== nothing
152+
v1 = ineq_constr(x)
153+
else
154+
v1 = eltype(x)[]
155+
end
156+
if eq_constr !== nothing
157+
v2 = eq_constr(x)
158+
else
159+
v2 = eltype(x)[]
160+
end
161+
return [v1; v2]
162+
end
163+
lcon = [fill(convert(T, -Inf), ineq_nconstr); zeros(T, eq_nconstr)]
164+
ucon = zeros(T, ineq_nconstr + eq_nconstr)
165+
return ADNLPModels.ADNLPModel(obj, x0, xlb, xub, c, lcon, ucon, adbackend = ADNLPModels.ZygoteAD())
166+
end
4167

5168
end

test/runtests.jl

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,107 @@
1-
using NonconvexPercival
2-
using Test
1+
using NonconvexPercival, LinearAlgebra, Test
32

4-
@testset "NonconvexPercival.jl" begin
5-
# Write your tests here.
3+
f(x::AbstractVector) = sqrt(x[2])
4+
g(x::AbstractVector, a, b) = (a*x[1] + b)^3 - x[2]
5+
6+
alg = AugLag()
7+
8+
@testset "Simple constraints" begin
9+
m = Model(f)
10+
addvar!(m, [0.0, 0.0], [10.0, 10.0])
11+
add_ineq_constraint!(m, x -> g(x, 2, 0))
12+
add_ineq_constraint!(m, x -> g(x, -1, 1))
13+
for first_order in (true, false)
14+
options = AugLagOptions(first_order = first_order)
15+
r = optimize(m, alg, [1.234, 2.345], options = options)
16+
@test abs(r.minimum - sqrt(8/27)) < 1e-6
17+
@test norm(r.minimizer - [1/3, 8/27]) < 1e-6
18+
end
19+
end
20+
21+
@testset "Equality constraints" begin
22+
m = Model(f)
23+
addvar!(m, [0.0, 0.0], [10.0, 10.0])
24+
add_ineq_constraint!(m, x -> g(x, 2, 0))
25+
add_ineq_constraint!(m, x -> g(x, -1, 1))
26+
add_eq_constraint!(m, x -> sum(x) - 1/3 - 8/27)
27+
for first_order in (true, false)
28+
options = AugLagOptions(first_order = first_order)
29+
r = optimize(m, alg, [1.234, 2.345], options = options)
30+
@test abs(r.minimum - sqrt(8/27)) < 1e-6
31+
@test norm(r.minimizer - [1/3, 8/27]) < 1e-6
32+
end
33+
end
34+
35+
@testset "Equality constraints BigFloat" begin
36+
T = BigFloat
37+
m = Model(f)
38+
addvar!(m, T.([0.0, 0.0]), T.([10.0, 10.0]))
39+
add_ineq_constraint!(m, x -> g(x, T(2), T(0)))
40+
add_ineq_constraint!(m, x -> g(x, T(-1), T(1)))
41+
add_eq_constraint!(m, x -> sum(x) - T(1/3) - T(8/27))
42+
for first_order in (true, false)
43+
options = AugLagOptions(first_order = first_order)
44+
r = optimize(m, alg, T.([1.234, 2.345]), options = options)
45+
@test abs(r.minimum - sqrt(T(8/27))) < T(1e-6)
46+
@test norm(r.minimizer - T.([1/3, 8/27])) < T(1e-6)
47+
@test r.minimum isa T
48+
@test eltype(r.minimizer) <: T
49+
end
50+
end
51+
52+
@testset "Block constraints" begin
53+
m = Model(f)
54+
addvar!(m, [0.0, 0.0], [10.0, 10.0])
55+
add_ineq_constraint!(m, FunctionWrapper(x -> [g(x, 2, 0), g(x, -1, 1)], 2))
56+
57+
for first_order in (true, false)
58+
options = AugLagOptions(first_order = first_order)
59+
r = optimize(m, alg, [1.234, 2.345], options = options)
60+
@test abs(r.minimum - sqrt(8/27)) < 1e-6
61+
@test norm(r.minimizer - [1/3, 8/27]) < 1e-6
62+
end
63+
end
64+
65+
@testset "Infinite bounds" begin
66+
@testset "Infinite upper bound" begin
67+
m = Model(f)
68+
addvar!(m, [0.0, 0.0], [Inf, Inf])
69+
add_ineq_constraint!(m, x -> g(x, 2, 0))
70+
add_ineq_constraint!(m, x -> g(x, -1, 1))
71+
72+
for first_order in (true, false)
73+
options = AugLagOptions(first_order = first_order)
74+
r = optimize(m, alg, [1.234, 2.345], options = options)
75+
@test abs(r.minimum - sqrt(8/27)) < 1e-6
76+
@test norm(r.minimizer - [1/3, 8/27]) < 1e-6
77+
end
78+
end
79+
#=
80+
@testset "Infinite lower bound" begin
81+
m = Model(f)
82+
addvar!(m, [-Inf, -Inf], [10, 10])
83+
add_ineq_constraint!(m, x -> g(x, 2, 0))
84+
add_ineq_constraint!(m, x -> g(x, -1, 1))
85+
86+
for first_order in (true, false)
87+
options = AugLagOptions(first_order = first_order)
88+
r = optimize(m, alg, [1.234, 2.345], options = options)
89+
@test abs(r.minimum - sqrt(8/27)) < 1e-6
90+
@test norm(r.minimizer - [1/3, 8/27]) < 1e-6
91+
end
92+
end
93+
@testset "Infinite upper and lower bound" begin
94+
m = Model(f)
95+
addvar!(m, [-Inf, -Inf], [Inf, Inf])
96+
add_ineq_constraint!(m, x -> g(x, 2, 0))
97+
add_ineq_constraint!(m, x -> g(x, -1, 1))
98+
99+
for first_order in (true, false)
100+
options = AugLagOptions(first_order = first_order)
101+
r = optimize(m, alg, [1.234, 2.345], options = options)
102+
@test abs(r.minimum - sqrt(8/27)) < 1e-6
103+
@test norm(r.minimizer - [1/3, 8/27]) < 1e-6
104+
end
105+
end
106+
=#
6107
end

0 commit comments

Comments
 (0)