Skip to content

Commit 6e92639

Browse files
RenanODabelsiqueira
authored andcommitted
Add missing methods and tests (#7)
* add missing methods * add tests * drop julia 0.7 support * add NLPModelsIpopt to dependencies * fix indentation * fix indentation mistakes * small corrections * fix mistake in objective value test * now using NLPs Counter and changes how functions are overwritten * Order jac and hess functions * separate imports in test and fix indentation * put imports in alphabetical order
1 parent c55e641 commit 6e92639

File tree

5 files changed

+106
-53
lines changed

5 files changed

+106
-53
lines changed

.appveyor.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
environment:
22
matrix:
3-
- julia_version: 0.7
43
- julia_version: 1
4+
- julia_version: 1.1
5+
- julia_version: 1.2
56
- julia_version: nightly
67

78
platform:

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ FastClosures = "9aa1b823-49e4-5ca5-8b0f-3971ec8bab6a"
88
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
99
LinearOperators = "5c8ed15e-5a4c-59e4-a42b-c7e8811fb125"
1010
NLPModels = "a4795742-8479-5a88-8948-cc11e1c8c1a6"
11+
NLPModelsIpopt = "f4238b75-b362-5c4c-b852-0801c9a21d71"
1112
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
1213
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1314
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

src/QuadraticModels.jl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
module QuadraticModels
22

3-
using FastClosures
4-
using LinearOperators
5-
using NLPModels
6-
using Requires
3+
# stdlib
4+
using LinearAlgebra, SparseArrays
75

8-
using LinearAlgebra
9-
using SparseArrays
6+
# our packages
7+
using LinearOperators, NLPModels
8+
9+
# auxiliary packages
10+
using FastClosures, Requires
1011

1112
import NLPModels:
12-
objgrad, objgrad!, obj,grad, grad!,
13+
objgrad, objgrad!, obj, grad, grad!,
1314
hess_coord, hess, hess_op, hprod,
1415
cons, cons!,
1516
jac_coord, jac, jac_op, jprod, jtprod

src/qpmodel.jl

Lines changed: 71 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,27 @@
1-
mutable struct QPData
2-
c0::Float64 # constant term in objective
3-
c::AbstractVector{Float64} # linear term
4-
H::AbstractMatrix{Float64} # quadratic term
5-
opH::AbstractLinearOperator # assumed with preallocation!
6-
A::AbstractMatrix{Float64} # constraint matrix
7-
end
1+
# override some NLPModels functions
2+
export jac_structure!, hess_structure!, jac_coord!, hess_coord!
83

9-
mutable struct QPCounters
10-
neval_hprod::Int # number of products with H
11-
neval_jprod::Int # number of products with A
12-
neval_jtprod::Int # number of products with A'
13-
QPCounters() = new(0, 0, 0)
4+
mutable struct QPData
5+
c0 :: Float64 # constant term in objective
6+
c :: AbstractVector{Float64} # linear term
7+
H :: AbstractMatrix{Float64} # quadratic term
8+
opH :: AbstractLinearOperator # assumed with preallocation!
9+
A :: AbstractMatrix{Float64} # constraint matrix
1410
end
1511

1612
abstract type AbstractQuadraticModel <: AbstractNLPModel end
1713

1814
mutable struct QuadraticModel <: AbstractQuadraticModel
19-
meta::NLPModelMeta
20-
counters::QPCounters
21-
data::QPData
22-
23-
function QuadraticModel(c::AbstractVector{Float64}, H::AbstractMatrix{Float64},
24-
opH::AbstractLinearOperator,
25-
A::AbstractMatrix{Float64},
26-
lcon::AbstractVector{Float64}, ucon::AbstractVector{Float64},
27-
lvar::AbstractVector{Float64}, uvar::AbstractVector{Float64};
28-
c0::Float64=0.0, kwargs...)
15+
meta :: NLPModelMeta
16+
counters :: Counters
17+
data :: QPData
18+
19+
function QuadraticModel(c :: AbstractVector{Float64}, H :: AbstractMatrix{Float64},
20+
opH :: AbstractLinearOperator,
21+
A :: AbstractMatrix{Float64},
22+
lcon :: AbstractVector{Float64}, ucon :: AbstractVector{Float64},
23+
lvar :: AbstractVector{Float64}, uvar :: AbstractVector{Float64};
24+
c0 :: Float64=0.0, kwargs...)
2925
ncon, nvar = size(A)
3026
nnzh = issparse(H) ? nnz(H) : (nvar * (nvar + 1) / 2)
3127
nnzj = issparse(A) ? nnz(A) : (nvar * ncon)
@@ -35,12 +31,12 @@ mutable struct QuadraticModel <: AbstractQuadraticModel
3531
nnzj=nnzj,
3632
nnzh=nnzh,
3733
lin=1:ncon, nln=Int[], islp=(ncon == 0); kwargs...),
38-
QPCounters(),
34+
Counters(),
3935
QPData(c0, c, H, opH, A))
4036
end
4137
end
4238

43-
function QuadraticModel(model::AbstractNLPModel)
39+
function QuadraticModel(model :: AbstractNLPModel)
4440
nvar = model.meta.nvar
4541
ncon = model.meta.ncon
4642
z = zeros(nvar)
@@ -54,76 +50,108 @@ end
5450

5551
linobj(qp::AbstractQuadraticModel, args...) = qp.data.c
5652

57-
function objgrad(qp::AbstractQuadraticModel, x::AbstractVector)
53+
function objgrad(qp :: AbstractQuadraticModel, x :: AbstractVector)
5854
g = Vector{eltype(x)}(length(x))
5955
objgrad!(qp, x, g)
6056
end
6157

62-
function objgrad!(qp::AbstractQuadraticModel, x::AbstractVector, g::AbstractVector)
58+
function objgrad!(qp :: AbstractQuadraticModel, x :: AbstractVector, g :: AbstractVector)
6359
v = qp.data.opH * x
6460
@. g = qp.data.c + v
6561
f = qp.data.c0 + dot(qp.data.c, x) + 0.5 * dot(v, x)
6662
qp.counters.neval_hprod += 1
6763
(f, g)
6864
end
6965

70-
function obj(qp::AbstractQuadraticModel, x::AbstractVector)
66+
function obj(qp :: AbstractQuadraticModel, x :: AbstractVector)
7167
v = qp.data.opH * x
7268
f = qp.data.c0 + dot(qp.data.c, x) + 0.5 * dot(v, x)
7369
qp.counters.neval_hprod += 1
7470
f
7571
end
7672

77-
function grad(qp::AbstractQuadraticModel, x::AbstractVector)
73+
function grad(qp :: AbstractQuadraticModel, x :: AbstractVector)
7874
g = Vector{eltype(x)}(undef, qp.meta.nvar)
7975
grad!(qp, x, g)
8076
end
8177

82-
function grad!(qp::AbstractQuadraticModel, x::AbstractVector, g::AbstractVector)
78+
function grad!(qp :: AbstractQuadraticModel, x :: AbstractVector, g :: AbstractVector)
8379
v = qp.data.opH * x
8480
@. g = qp.data.c + v
8581
qp.counters.neval_hprod += 1
8682
g
8783
end
8884

89-
hess_coord(qp::AbstractQuadraticModel, ::AbstractVector; kwargs...) = findnz(qp.data.H)
85+
hess(qp :: AbstractQuadraticModel, ::AbstractVector; kwargs...) = qp.data.H
86+
87+
hess_op(qp :: AbstractQuadraticModel, ::AbstractVector; kwargs...) = qp.data.opH
88+
89+
hess_coord(qp :: AbstractQuadraticModel, ::AbstractVector; kwargs...) = findnz(qp.data.H)
90+
91+
"""
92+
Return the structure of the Lagrangian Hessian in sparse coordinate format in place.
93+
"""
94+
function NLPModels.hess_structure!(qp :: QuadraticModel, rows :: Vector{<: Integer}, cols :: Vector{<: Integer}; kwargs...)
95+
rows .= qp.data.H.rowval
96+
cols .= findnz(qp.data.H)[2]
97+
end
98+
99+
"""
100+
Evaluate the Lagrangian Hessian at `x` in sparse coordinate format. Only the lower triangle is returned.
101+
"""
102+
function NLPModels.hess_coord!(qp :: QuadraticModel, :: AbstractVector, rows :: AbstractVector{<: Integer},
103+
cols :: AbstractVector{<: Integer}, vals :: Vector{<: AbstractFloat}; kwargs...)
104+
vals .= findnz(qp.data.H)[3]
105+
end
90106

91-
hess(qp::AbstractQuadraticModel, ::AbstractVector; kwargs...) = qp.data.H
107+
jac(qp :: AbstractQuadraticModel, ::AbstractVector; kwargs...) = qp.data.A
92108

93-
hess_op(qp::AbstractQuadraticModel, ::AbstractVector; kwargs...) = qp.data.opH
109+
jac_op(qp :: AbstractQuadraticModel, ::AbstractVector; kwargs...) = LinearOperator(qp.data.A)
94110

95-
function cons(qp::AbstractQuadraticModel, x::AbstractVector)
111+
jac_coord(qp :: AbstractQuadraticModel, ::AbstractVector; kwargs...) = findnz(qp.data.A)
112+
113+
"""
114+
Return the structure of the constraints Jacobian in sparse coordinate format in place.
115+
"""
116+
function NLPModels.jac_structure!(qp :: QuadraticModel, rows :: Vector{<: Integer}, cols :: Vector{<: Integer}; kwargs...)
117+
rows .= qp.data.A.rowval
118+
cols .= findnz(qp.data.A)[2]
119+
end
120+
121+
"""
122+
Return the structure of the constraints Jacobian in sparse coordinate format in place.
123+
"""
124+
function NLPModels.jac_coord!(qp :: QuadraticModel, x :: AbstractVector, rows :: Vector{<: Integer},
125+
cols :: Vector{<: Integer}, vals :: Vector{<: AbstractFloat}; kwargs...)
126+
vals .= findnz(qp.data.A)[3]
127+
end
128+
129+
function cons(qp::AbstractQuadraticModel, x :: AbstractVector)
96130
c = Vector{eltype(x)}(undef, qp.meta.ncon)
97131
cons!(qp, x, c)
98132
end
99133

100-
function cons!(qp::AbstractQuadraticModel, x::AbstractVector, c::AbstractVector)
134+
function cons!(qp :: AbstractQuadraticModel, x :: AbstractVector, c :: AbstractVector)
101135
mul!(c, qp.data.A, x)
102136
qp.counters.neval_jprod += 1
103137
c
104138
end
105139

106-
jac_coord(qp::AbstractQuadraticModel, ::AbstractVector; kwargs...) = findnz(qp.data.A)
107-
108-
jac(qp::AbstractQuadraticModel, ::AbstractVector; kwargs...) = qp.data.A
109-
110-
jac_op(qp::AbstractQuadraticModel, ::AbstractVector; kwargs...) = LinearOperator(qp.data.A)
111-
112-
function hprod(qp::AbstractQuadraticModel, ::AbstractVector; kwargs...)
140+
function hprod(qp :: AbstractQuadraticModel, ::AbstractVector; kwargs...)
113141
@closure v -> begin
114142
qp.counters.neval_hprod += 1
115143
qp.data.opH * v
116144
end
117145
end
118146

119-
function jprod(qp::AbstractQuadraticModel, ::AbstractVector; kwargs...)
147+
function jprod(qp :: AbstractQuadraticModel, ::AbstractVector; kwargs...)
120148
@closure v -> begin
121149
qp.counters.neval_jprod += 1
122150
qp.data.A * v
123151
end
124152
end
125153

126-
function jtprod(qp::AbstractQuadraticModel, ::AbstractVector; kwargs...)
154+
function jtprod(qp :: AbstractQuadraticModel, ::AbstractVector; kwargs...)
127155
@closure v -> begin
128156
qp.counters.neval_jtprod += 1
129157
qp.data.A' * v

test/runtests.jl

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1-
using QuadraticModels
1+
# stdlib
2+
using SparseArrays, Test
23

3-
true
4+
# our packages
5+
using LinearOperators, NLPModels, NLPModelsIpopt, QuadraticModels
6+
7+
@testset "QuadraticModelsTests" begin
8+
c0 = 0.0
9+
c = [1.5; -2.0]
10+
Q = sparse([1; 2; 2], [1; 1; 2], [8.0; 2.0; 10.0])
11+
A = sparse([1; 2; 1; 2], [1; 1; 2; 2], [2.0; -1.0; 1.0; 2.0])
12+
lcon = [2.0; -Inf]
13+
ucon = [Inf; 6.0]
14+
uvar = [20; Inf]
15+
lvar = [0.0; 0.0]
16+
objective = 4.3718750e+00
17+
18+
qp = QuadraticModel(c, Q, opHermitian(Q), A, lcon, ucon, lvar, uvar, c0 = c0)
19+
output = ipopt(qp, print_level = 0)
20+
21+
@test output.dual_feas < 1e-6
22+
@test output.primal_feas < 1e-6
23+
@test abs(output.objective - objective) < 1e-6
24+
25+
end # testset

0 commit comments

Comments
 (0)