Skip to content

Commit 4db6d66

Browse files
committed
Add a structure LinearModel
1 parent e467f84 commit 4db6d66

File tree

4 files changed

+243
-30
lines changed

4 files changed

+243
-30
lines changed

ext/QuadraticModelsQPSReaderExt.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,19 @@ function QuadraticModels.QuadraticModel(qps::QPSReader.QPSData, x0 = zeros(qps.n
2121
)
2222
end
2323

24+
function QuadraticModels.LinearModel(qps::QPSReader.QPSData, x0 = zeros(qps.nvar))
25+
QuadraticModels.LinearModel(
26+
qps.c,
27+
Arows = qps.arows,
28+
Acols = qps.acols,
29+
Avals = qps.avals,
30+
lcon = qps.lcon,
31+
ucon = qps.ucon,
32+
lvar = qps.lvar,
33+
uvar = qps.uvar,
34+
c0 = qps.c0,
35+
x0 = x0,
36+
)
37+
end
38+
2439
end

src/QuadraticModels.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ import NLPModelsModifiers: SlackModel, slack_meta
2727

2828
import Base.convert
2929

30-
export AbstractQuadraticModel, QuadraticModel, presolve, postsolve, postsolve!, QMSolution
30+
export AbstractQuadraticModel, LinearModel, QuadraticModel, presolve, postsolve, postsolve!, QMSolution
3131

3232
include("linalg_utils.jl")
3333
include("qpmodel.jl")
34+
include("lpmodel.jl")
3435
include("presolve/presolve.jl")
3536

3637
end # module

src/lpmodel.jl

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
"""
2+
lp = LinearModel(c; Arows = Arows, Acols = Acols, Avals = Avals,
3+
lcon = lcon, ucon = ucon, lvar = lvar, uvar = uvar, c0=c0, sortcols = false)
4+
5+
lp = LinearModel(c; A = A, lcon = lcon, ucon = ucon, lvar = lvar, uvar = uvar, c0 = c0)
6+
7+
Create a Linear model ``min ~c^T x + c_0`` with optional bounds
8+
`lvar ≦ x ≦ uvar` and optional linear constraints `lcon ≦ Ax ≦ ucon`.
9+
10+
With the first constructor, if `sortcols = true`, then `Acols` is sorted in ascending order
11+
(`Arows` and `Avals` are then sorted accordingly).
12+
13+
You can also use [`QPSReader.jl`](https://github.com/JuliaSmoothOptimizers/QPSReader.jl) to
14+
create a Linear model from a QPS file:
15+
16+
using QPSReader
17+
qps = readqps("QAFIRO.SIF")
18+
lp = LinearModel(qps)
19+
"""
20+
mutable struct LinearModel{T, S, M1, M2} <: AbstractQuadraticModel{T, S}
21+
meta::NLPModelMeta{T, S}
22+
counters::Counters
23+
data::QPData{T, S, M1, M2}
24+
end
25+
26+
function LinearModel(
27+
c::S;
28+
Arows::AbstractVector{<:Integer} = Int[],
29+
Acols::AbstractVector{<:Integer} = Int[],
30+
Avals::S = S(undef, 0),
31+
lcon::S = S(undef, 0),
32+
ucon::S = S(undef, 0),
33+
lvar::S = fill!(S(undef, length(c)), eltype(c)(-Inf)),
34+
uvar::S = fill!(S(undef, length(c)), eltype(c)(Inf)),
35+
c0::T = zero(eltype(c)),
36+
sortcols::Bool = false,
37+
kwargs...,
38+
) where {T, S}
39+
@assert all(lvar .≤ uvar)
40+
@assert all(lcon .≤ ucon)
41+
nnzh = 0
42+
nnzj = length(Avals)
43+
if !(nnzj == length(Arows) == length(Acols))
44+
error("The length of Arows, Acols and Avals must be the same")
45+
end
46+
ncon = length(lcon)
47+
if ncon != length(ucon)
48+
error("The length of lcon and ucon must be the same")
49+
end
50+
nvar = length(c)
51+
if !(nvar == length(lvar) == length(uvar))
52+
error("The length of c, lvar and uvar must be the same")
53+
end
54+
if sortcols
55+
pA = sortperm(Acols)
56+
permute!(Arows, pA)
57+
permute!(Acols, pA)
58+
permute!(Avals, pA)
59+
end
60+
LinearModel(
61+
NLPModelMeta{T, S}(
62+
length(c),
63+
lvar = lvar,
64+
uvar = uvar,
65+
ncon = ncon,
66+
lcon = lcon,
67+
ucon = ucon,
68+
nnzj = nnzj,
69+
lin_nnzj = nnzj,
70+
nln_nnzj = 0,
71+
nnzh = nnzh,
72+
lin = 1:ncon,
73+
islp = true;
74+
kwargs...,
75+
),
76+
Counters(),
77+
QPData(
78+
c0,
79+
c,
80+
SparseMatrixCOO(0, nvar, similar(Arows, 0), similar(Acols, 0), similar(Avals, 0)),
81+
SparseMatrixCOO(ncon, nvar, Arows, Acols, Avals),
82+
),
83+
)
84+
end
85+
86+
function LinearModel(
87+
c::S;
88+
A::Union{AbstractMatrix{T}, AbstractLinearOperator{T}} = SparseMatrixCOO(0, length(c), Int[], Int[], T[]),
89+
lcon::S = S(undef, 0),
90+
ucon::S = S(undef, 0),
91+
lvar::S = fill!(S(undef, length(c)), T(-Inf)),
92+
uvar::S = fill!(S(undef, length(c)), T(Inf)),
93+
c0::T = zero(T),
94+
kwargs...,
95+
) where {T, S}
96+
@assert all(lvar .≤ uvar)
97+
@assert all(lcon .≤ ucon)
98+
ncon, nvar = size(A)
99+
nnzh = 0
100+
nnzj = nnz(A)
101+
H = similar_empty_matrix(A, length(c))
102+
data = QPData(c0, c, H, A)
103+
104+
LinearModel(
105+
NLPModelMeta{T, S}(
106+
nvar,
107+
lvar = lvar,
108+
uvar = uvar,
109+
ncon = ncon,
110+
lcon = lcon,
111+
ucon = ucon,
112+
nnzj = nnzj,
113+
lin_nnzj = nnzj,
114+
nln_nnzj = 0,
115+
nnzh = nnzh,
116+
lin = 1:ncon,
117+
islp = (ncon == 0);
118+
kwargs...,
119+
),
120+
Counters(),
121+
data,
122+
)
123+
end
124+
125+
"""
126+
LinearModel(nlp, x)
127+
128+
Creates a linear Taylor model of `nlp` around `x`.
129+
"""
130+
function LinearModel(model::AbstractNLPModel{T, S}, x::AbstractVector; kwargs...) where {T, S}
131+
nvar = model.meta.nvar
132+
ncon = model.meta.ncon
133+
c0 = obj(model, x)
134+
g = grad(model, x)
135+
if model.meta.ncon > 0
136+
c = cons(model, x)
137+
Arows, Acols = jac_structure(model)
138+
Avals = jac_coord(model, x)
139+
LinearModel(
140+
g,
141+
c0 = c0,
142+
Arows = Arows,
143+
Acols = Acols,
144+
Avals = Avals,
145+
lcon = model.meta.lcon .- c,
146+
ucon = model.meta.ucon .- c,
147+
lvar = model.meta.lvar .- x,
148+
uvar = model.meta.uvar .- x,
149+
x0 = fill!(S(undef, model.meta.nvar), zero(T)),
150+
)
151+
else
152+
LinearModel(
153+
g,
154+
c0 = c0,
155+
lvar = model.meta.lvar .- x,
156+
uvar = model.meta.uvar .- x,
157+
x0 = fill!(S(undef, model.meta.nvar), zero(T)),
158+
)
159+
end
160+
end
161+
162+
function NLPModels.objgrad!(qp::LinearModel, x::AbstractVector, g::AbstractVector)
163+
NLPModels.increment!(qp, :neval_obj)
164+
NLPModels.increment!(qp, :neval_grad)
165+
f = qp.data.c0 + dot(qp.data.c, x)
166+
g .+= qp.data.c
167+
return f, g
168+
end
169+
170+
function NLPModels.obj(qp::LinearModel, x::AbstractVector)
171+
NLPModels.increment!(qp, :neval_obj)
172+
return qp.data.c0 + dot(qp.data.c, x)
173+
end
174+
175+
function NLPModels.grad!(qp::LinearModel, x::AbstractVector, g::AbstractVector)
176+
NLPModels.increment!(qp, :neval_grad)
177+
g .= qp.data.c
178+
return g
179+
end
180+
181+
function NLPModels.hess_structure!(
182+
lp::LinearModel,
183+
rows::AbstractVector{<:Integer},
184+
cols::AbstractVector{<:Integer},
185+
)
186+
return rows, cols
187+
end
188+
189+
function NLPModels.hess_coord!(
190+
lp::LinearModel,
191+
x::AbstractVector,
192+
vals::AbstractVector;
193+
obj_weight::Real = one(eltype(x)),
194+
)
195+
NLPModels.increment!(lp, :neval_hess)
196+
fill!(vals, zero(eltype(x)))
197+
return vals
198+
end
199+
200+
function NLPModels.hprod!(
201+
lp::LinearModel,
202+
x::AbstractVector,
203+
v::AbstractVector,
204+
Hv::AbstractVector;
205+
obj_weight::Real = one(eltype(x)),
206+
)
207+
NLPModels.increment!(lp, :neval_hprod)
208+
fill!(Hv, zero(eltype(x)))
209+
return Hv
210+
end

src/qpmodel.jl

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mutable struct QPData{
1313
A::M2
1414
end
1515

16-
@inline QPData(c0, c, H, A) = QPData(c0, c, similar(c), H, A)
16+
QPData(c0, c, H, A) = QPData(c0, c, similar(c), H, A)
1717
isdense(data::QPData{T, S, M1, M2}) where {T, S, M1, M2} = M1 <: DenseMatrix || M2 <: DenseMatrix
1818

1919
function Base.convert(
@@ -33,9 +33,9 @@ abstract type AbstractQuadraticModel{T, S} <: AbstractNLPModel{T, S} end
3333

3434
"""
3535
qp = QuadraticModel(c, Hrows, Hcols, Hvals; Arows = Arows, Acols = Acols, Avals = Avals,
36-
lcon = lcon, ucon = ucon, lvar = lvar, uvar = uvar, sortcols = false)
36+
lcon = lcon, ucon = ucon, lvar = lvar, uvar = uvar, c0 = c0, sortcols = false)
3737
38-
qp = QuadraticModel(c, H; A = A, lcon = lcon, ucon = ucon, lvar = lvar, uvar = uvar)
38+
qp = QuadraticModel(c, H; A = A, lcon = lcon, ucon = ucon, lvar = lvar, uvar = uvar, c0 = c0)
3939
4040
Create a Quadratic model ``min ~\\tfrac{1}{2} x^T H x + c^T x + c_0`` with optional bounds
4141
`lvar ≦ x ≦ uvar` and optional linear constraints `lcon ≦ Ax ≦ ucon`.
@@ -137,7 +137,7 @@ function QuadraticModel(
137137
nln_nnzj = 0,
138138
nnzh = nnzh,
139139
lin = 1:ncon,
140-
islp = (ncon == 0);
140+
islp = (nnzh == 0);
141141
kwargs...,
142142
),
143143
Counters(),
@@ -184,7 +184,7 @@ function QuadraticModel(
184184
nvar,
185185
lvar = lvar,
186186
uvar = uvar,
187-
ncon = size(A, 1),
187+
ncon = ncon,
188188
lcon = lcon,
189189
ucon = ucon,
190190
nnzj = nnzj,
@@ -247,22 +247,22 @@ end
247247

248248
linobj(qp::AbstractQuadraticModel, args...) = qp.data.c
249249

250-
function NLPModels.objgrad!(qp::AbstractQuadraticModel, x::AbstractVector, g::AbstractVector)
250+
function NLPModels.objgrad!(qp::QuadraticModel, x::AbstractVector, g::AbstractVector)
251251
NLPModels.increment!(qp, :neval_obj)
252252
NLPModels.increment!(qp, :neval_grad)
253253
mul!(g, Symmetric(qp.data.H, :L), x)
254254
f = qp.data.c0 + dot(qp.data.c, x) + dot(g, x) / 2
255-
@. g .+= qp.data.c
255+
g .+= qp.data.c
256256
return f, g
257257
end
258258

259-
function NLPModels.obj(qp::AbstractQuadraticModel{T, S}, x::AbstractVector) where {T, S}
259+
function NLPModels.obj(qp::QuadraticModel, x::AbstractVector)
260260
NLPModels.increment!(qp, :neval_obj)
261261
mul!(qp.data.v, Symmetric(qp.data.H, :L), x)
262262
return qp.data.c0 + dot(qp.data.c, x) + dot(qp.data.v, x) / 2
263263
end
264264

265-
function NLPModels.grad!(qp::AbstractQuadraticModel, x::AbstractVector, g::AbstractVector)
265+
function NLPModels.grad!(qp::QuadraticModel, x::AbstractVector, g::AbstractVector)
266266
NLPModels.increment!(qp, :neval_grad)
267267
mul!(g, Symmetric(qp.data.H, :L), x)
268268
g .+= qp.data.c
@@ -363,7 +363,7 @@ function NLPModels.hess_coord!(
363363
end
364364

365365
NLPModels.hess_coord!(
366-
qp::QuadraticModel,
366+
qp::AbstractQuadraticModel,
367367
x::AbstractVector,
368368
y::AbstractVector,
369369
vals::AbstractVector;
@@ -392,7 +392,7 @@ function NLPModels.jac_lin_structure!(
392392
end
393393

394394
function NLPModels.jac_lin_structure!(
395-
qp::QuadraticModel{T, S, M1, M2},
395+
qp::AbstractQuadraticModel{T, S, M1, M2},
396396
rows::AbstractVector{<:Integer},
397397
cols::AbstractVector{<:Integer},
398398
) where {T, S, M1, M2 <: Matrix}
@@ -409,7 +409,7 @@ function NLPModels.jac_lin_structure!(
409409
end
410410

411411
function NLPModels.jac_lin_coord!(
412-
qp::QuadraticModel{T, S, M1, M2},
412+
qp::AbstractQuadraticModel{T, S, M1, M2},
413413
x::AbstractVector,
414414
vals::AbstractVector,
415415
) where {T, S, M1, M2 <: SparseMatrixCOO}
@@ -421,7 +421,7 @@ function NLPModels.jac_lin_coord!(
421421
end
422422

423423
function NLPModels.jac_lin_coord!(
424-
qp::QuadraticModel{T, S, M1, M2},
424+
qp::AbstractQuadraticModel{T, S, M1, M2},
425425
x::AbstractVector,
426426
vals::AbstractVector,
427427
) where {T, S, M1, M2 <: SparseMatrixCSC}
@@ -433,7 +433,7 @@ function NLPModels.jac_lin_coord!(
433433
end
434434

435435
function NLPModels.jac_lin_coord!(
436-
qp::QuadraticModel{T, S, M1, M2},
436+
qp::AbstractQuadraticModel{T, S, M1, M2},
437437
x::AbstractVector,
438438
vals::AbstractVector,
439439
) where {T, S, M1, M2 <: Matrix}
@@ -451,7 +451,7 @@ function NLPModels.jac_lin_coord!(
451451
end
452452

453453
function NLPModels.jac_lin(
454-
qp::QuadraticModel{T, S, M1, M2},
454+
qp::AbstractQuadraticModel{T, S, M1, M2},
455455
x::AbstractVector,
456456
) where {T, S, M1 <: AbstractLinearOperator, M2 <: AbstractLinearOperator}
457457
@lencheck qp.meta.nvar x
@@ -468,7 +468,7 @@ function NLPModels.cons_lin!(qp::AbstractQuadraticModel, x::AbstractVector, c::A
468468
end
469469

470470
function NLPModels.hprod!(
471-
qp::AbstractQuadraticModel,
471+
qp::QuadraticModel,
472472
x::AbstractVector,
473473
v::AbstractVector,
474474
Hv::AbstractVector;
@@ -504,19 +504,6 @@ function NLPModels.jprod_lin!(
504504
return Av
505505
end
506506

507-
function NLPModels.jtprod!(
508-
qp::AbstractQuadraticModel,
509-
x::AbstractVector,
510-
v::AbstractVector,
511-
Atv::AbstractVector,
512-
)
513-
@lencheck qp.meta.nvar x Atv
514-
@lencheck qp.meta.ncon v
515-
NLPModels.increment!(qp, :neval_jtprod)
516-
mul!(Atv, transpose(qp.data.A), v)
517-
return Atv
518-
end
519-
520507
function NLPModels.jtprod_lin!(
521508
qp::AbstractQuadraticModel,
522509
x::AbstractVector,

0 commit comments

Comments
 (0)