Skip to content

Commit 30f1c3b

Browse files
authored
Add options variable_bounds_analysis and constraint_bounds_analysis in NLPModelMeta (#543)
1 parent 506a5d4 commit 30f1c3b

5 files changed

Lines changed: 153 additions & 86 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ Attribute | Type | Notes
118118
`minimize` | `Bool` | true if `optimize == minimize`
119119
`islp` | `Bool` | true if the problem is a linear program
120120
`name` | `String` | problem name
121+
`variable_bounds_analysis` | `Bool` | true if the partition of variables into fixed, lower-bounded, upper-bounded, range-bounded, free, and trivially infeasible sets is computed
122+
`constraint_bounds_analysis`| `Bool` | true if the partition of constraints into equality, lower-bounded, upper-bounded, range-bounded, free, and trivially infeasible sets is computed
121123
`sparse_jacobian` | `Bool` | true if the Jacobian of the constraints is sparse
122124
`sparse_hessian` | `Bool` | true if the Hessian of the Lagrangian is sparse
123125
`grad_available` | `Bool` | true if the gradient of the objective is available

docs/src/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ Attribute | Type | Notes
113113
`minimize` | `Bool` | true if `optimize == minimize`
114114
`islp` | `Bool` | true if the problem is a linear program
115115
`name` | `String` | problem name
116+
`variable_bounds_analysis` | `Bool` | true if the partition of variables into fixed, lower-bounded, upper-bounded, range-bounded, free, and trivially infeasible sets is computed
117+
`constraint_bounds_analysis`| `Bool` | true if the partition of constraints into equality, lower-bounded, upper-bounded, range-bounded, free, and trivially infeasible sets is computed
116118
`sparse_jacobian` | `Bool` | true if the Jacobian of the constraints is sparse
117119
`sparse_hessian` | `Bool` | true if the Hessian of the Lagrangian is sparse
118120
`grad_available` | `Bool` | true if the gradient of the objective is available

src/nlp/meta.jl

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ The following keyword arguments are accepted:
5151
- `minimize`: true if optimize == minimize
5252
- `islp`: true if the problem is a linear program
5353
- `name`: problem name
54+
- `variable_bounds_analysis`: whether to compute the partition of variables into fixed, lower-bounded, upper-bounded, range-bounded, free, and trivially infeasible sets
55+
- `constraint_bounds_analysis`: whether to compute the partition of constraints into equality, lower-bounded, upper-bounded, range-bounded, free, and trivially infeasible sets
5456
- `sparse_jacobian`: indicates whether the Jacobian of the constraints is sparse
5557
- `sparse_hessian`: indicates whether the Hessian of the Lagrangian is sparse
5658
- `grad_available`: indicates whether the gradient of the objective is available
@@ -77,6 +79,9 @@ The following keyword arguments are accepted:
7779
- `nlin`: number of linear constraints
7880
- `nnln`: number of nonlinear general constraints
7981
- `nln`: indices of nonlinear constraints
82+
83+
The attributes `ifix`, `ilow`, `iupp`, `irng`, `ifree`, and `iinf` are not computed if `variable_bounds_analysis` is set to `false`.
84+
The attributes `jfix`, `jlow`, `jupp`, `jrng`, `jfree`, and `jinf` are not computed if `constraint_bounds_analysis` is set to `false`.
8085
"""
8186
struct NLPModelMeta{T, S} <: AbstractNLPModelMeta{T, S}
8287
nvar::Int
@@ -123,6 +128,9 @@ struct NLPModelMeta{T, S} <: AbstractNLPModelMeta{T, S}
123128
islp::Bool
124129
name::String
125130

131+
variable_bounds_analysis::Bool
132+
constraint_bounds_analysis::Bool
133+
126134
sparse_jacobian::Bool
127135
sparse_hessian::Bool
128136

@@ -155,6 +163,8 @@ function NLPModelMeta{T, S}(
155163
minimize::Bool = true,
156164
islp::Bool = false,
157165
name = "Generic",
166+
variable_bounds_analysis::Bool = true,
167+
constraint_bounds_analysis::Bool = true,
158168
sparse_jacobian::Bool = true,
159169
sparse_hessian::Bool = true,
160170
grad_available::Bool = true,
@@ -173,14 +183,23 @@ function NLPModelMeta{T, S}(
173183
@rangecheck 1 ncon lin
174184
@assert nnzj == lin_nnzj + nln_nnzj
175185

176-
ifix = findall(lvar .== uvar)
177-
ilow = findall((lvar .> T(-Inf)) .& (uvar .== T(Inf)))
178-
iupp = findall((lvar .== T(-Inf)) .& (uvar .< T(Inf)))
179-
irng = findall((lvar .> T(-Inf)) .& (uvar .< T(Inf)) .& (lvar .< uvar))
180-
ifree = findall((lvar .== T(-Inf)) .& (uvar .== T(Inf)))
181-
iinf = findall(lvar .> uvar)
186+
if variable_bounds_analysis
187+
ifix = findall(lvar .== uvar)
188+
ilow = findall((lvar .> T(-Inf)) .& (uvar .== T(Inf)))
189+
iupp = findall((lvar .== T(-Inf)) .& (uvar .< T(Inf)))
190+
irng = findall((lvar .> T(-Inf)) .& (uvar .< T(Inf)) .& (lvar .< uvar))
191+
ifree = findall((lvar .== T(-Inf)) .& (uvar .== T(Inf)))
192+
iinf = findall(lvar .> uvar)
193+
else
194+
ifix = Int[]
195+
ilow = Int[]
196+
iupp = Int[]
197+
irng = Int[]
198+
ifree = Int[]
199+
iinf = Int[]
200+
end
182201

183-
if ncon > 0
202+
if (ncon > 0) && constraint_bounds_analysis
184203
jfix = findall(lcon .== ucon)
185204
jlow = findall((lcon .> T(-Inf)) .& (ucon .== T(Inf)))
186205
jupp = findall((lcon .== T(-Inf)) .& (ucon .< T(Inf)))
@@ -236,6 +255,8 @@ function NLPModelMeta{T, S}(
236255
minimize,
237256
islp,
238257
name,
258+
variable_bounds_analysis,
259+
constraint_bounds_analysis,
239260
sparse_jacobian,
240261
sparse_hessian,
241262
grad_available,
@@ -272,8 +293,10 @@ function NLPModelMeta(
272293
minimize::Bool = meta.minimize,
273294
islp::Bool = meta.islp,
274295
name = meta.name,
275-
sparse_jacobian::Bool = true,
276-
sparse_hessian::Bool = true,
296+
variable_bounds_analysis::Bool = meta.variable_bounds_analysis,
297+
constraint_bounds_analysis::Bool = meta.constraint_bounds_analysis,
298+
sparse_jacobian::Bool = meta.sparse_jacobian,
299+
sparse_hessian::Bool = meta.sparse_hessian,
277300
grad_available::Bool = meta.grad_available,
278301
jac_available::Bool = meta.jac_available,
279302
hess_available::Bool = meta.hess_available,
@@ -302,6 +325,8 @@ function NLPModelMeta(
302325
minimize = minimize,
303326
islp = islp,
304327
name = name,
328+
variable_bounds_analysis = variable_bounds_analysis,
329+
constraint_bounds_analysis = constraint_bounds_analysis,
305330
sparse_jacobian = sparse_jacobian,
306331
sparse_hessian = sparse_hessian,
307332
grad_available = grad_available,

src/nlp/tools.jl

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,37 @@ end
2222
2323
Returns whether the problem has bounds on the variables.
2424
"""
25-
has_bounds(meta::AbstractNLPModelMeta) = length(meta.ifree) < meta.nvar
25+
function has_bounds(meta::AbstractNLPModelMeta)
26+
if meta.variable_bounds_analysis
27+
return length(meta.ifree) < meta.nvar
28+
else
29+
return !all(lv -> isinf(lv), meta.lvar) || !all(uv -> isinf(uv), meta.uvar)
30+
end
31+
end
2632

2733
"""
2834
bound_constrained(nlp)
2935
bound_constrained(meta)
3036
3137
Returns whether the problem has bounds on the variables and no other constraints.
3238
"""
33-
bound_constrained(meta::AbstractNLPModelMeta) = meta.ncon == 0 && has_bounds(meta)
39+
bound_constrained(meta::AbstractNLPModelMeta) = (meta.ncon == 0) && has_bounds(meta)
3440

3541
"""
3642
unconstrained(nlp)
3743
unconstrained(meta)
3844
3945
Returns whether the problem in unconstrained.
4046
"""
41-
unconstrained(meta::AbstractNLPModelMeta) = meta.ncon == 0 && !has_bounds(meta)
47+
unconstrained(meta::AbstractNLPModelMeta) = (meta.ncon == 0) && !has_bounds(meta)
4248

4349
"""
4450
linearly_constrained(nlp)
4551
linearly_constrained(meta)
4652
4753
Returns whether the problem's constraints are known to be all linear.
4854
"""
49-
linearly_constrained(meta::AbstractNLPModelMeta) = meta.nlin == meta.ncon > 0
55+
linearly_constrained(meta::AbstractNLPModelMeta) = (meta.ncon > 0) && (meta.nlin == meta.ncon)
5056

5157
"""
5258
equality_constrained(nlp)
@@ -55,7 +61,13 @@ linearly_constrained(meta::AbstractNLPModelMeta) = meta.nlin == meta.ncon > 0
5561
Returns whether the problem's constraints are all equalities.
5662
Unconstrained problems return false.
5763
"""
58-
equality_constrained(meta::AbstractNLPModelMeta) = length(meta.jfix) == meta.ncon > 0
64+
function equality_constrained(meta::AbstractNLPModelMeta)
65+
if meta.constraint_bounds_analysis
66+
return (meta.ncon > 0) && (length(meta.jfix) == meta.ncon)
67+
else
68+
return (meta.ncon > 0) && all(x -> x[1] == x[2], zip(meta.lcon, meta.ucon))
69+
end
70+
end
5971

6072
"""
6173
inequality_constrained(nlp)
@@ -64,23 +76,41 @@ equality_constrained(meta::AbstractNLPModelMeta) = length(meta.jfix) == meta.nco
6476
Returns whether the problem's constraints are all inequalities.
6577
Unconstrained problems return true.
6678
"""
67-
inequality_constrained(meta::AbstractNLPModelMeta) = meta.ncon > 0 && length(meta.jfix) == 0
79+
function inequality_constrained(meta::AbstractNLPModelMeta)
80+
if meta.constraint_bounds_analysis
81+
return (meta.ncon > 0) && (length(meta.jfix) == 0)
82+
else
83+
return (meta.ncon > 0) && all(x -> x[1] != x[2], zip(meta.lcon, meta.ucon))
84+
end
85+
end
6886

6987
"""
7088
has_equalities(nlp)
7189
7290
Returns whether the problem has constraints and at least one of them is an equality.
7391
Unconstrained problems return false.
7492
"""
75-
has_equalities(meta::AbstractNLPModelMeta) = meta.ncon length(meta.jfix) > 0
93+
function has_equalities(meta::AbstractNLPModelMeta)
94+
if meta.constraint_bounds_analysis
95+
return (meta.ncon > 0) && (length(meta.jfix) > 0)
96+
else
97+
return (meta.ncon > 0) && !all(x -> x[1] != x[2], zip(meta.lcon, meta.ucon))
98+
end
99+
end
76100

77101
"""
78102
has_inequalities(nlp)
79103
80104
Returns whether the problem has constraints and at least one of them is an inequality.
81105
Unconstrained problems return false.
82106
"""
83-
has_inequalities(meta::AbstractNLPModelMeta) = meta.ncon > 0 && meta.ncon > length(meta.jfix)
107+
function has_inequalities(meta::AbstractNLPModelMeta)
108+
if meta.constraint_bounds_analysis
109+
return (meta.ncon > 0) && (meta.ncon > length(meta.jfix))
110+
else
111+
return (meta.ncon > 0) && !all(x -> x[1] == x[2], zip(meta.lcon, meta.ucon))
112+
end
113+
end
84114

85115
for meth in [
86116
:has_bounds,

test/nlp/tools.jl

Lines changed: 77 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,80 @@
11
@testset "Problem type functions" begin
2-
foo_list = [
3-
has_bounds,
4-
bound_constrained,
5-
unconstrained,
6-
linearly_constrained,
7-
equality_constrained,
8-
inequality_constrained,
9-
has_equalities,
10-
has_inequalities,
11-
]
12-
meta_list = [
13-
NLPModelMeta(2),
14-
NLPModelMeta(2, lvar = zeros(2), uvar = ones(2)),
15-
NLPModelMeta(2, ncon = 1, lcon = [0.0], ucon = [0.0]),
16-
NLPModelMeta(2, ncon = 1, lcon = [0.0], ucon = [1.0]),
17-
NLPModelMeta(2, ncon = 1, lcon = [0.0], ucon = [Inf]),
18-
NLPModelMeta(2, ncon = 1, lcon = [-Inf], ucon = [0.0]),
19-
NLPModelMeta(2, ncon = 1, lcon = [0.0], ucon = [1.0], lin = [1]),
20-
NLPModelMeta(2, ncon = 2, lcon = [0.0, 0.0], ucon = [1.0, 1.0], lin = [1]),
21-
NLPModelMeta(2, ncon = 2, lcon = [0.0, 0.0], ucon = [1.0, 0.0], lin = [1]),
22-
NLPModelMeta(2, lvar = zeros(2), uvar = ones(2), ncon = 1, lcon = [0.0], ucon = [0.0]),
23-
NLPModelMeta(2, lvar = zeros(2), uvar = ones(2), ncon = 1, lcon = [0.0], ucon = [1.0]),
24-
NLPModelMeta(2, lvar = zeros(2), uvar = ones(2), ncon = 1, lcon = [0.0], ucon = [Inf]),
25-
NLPModelMeta(2, lvar = zeros(2), uvar = ones(2), ncon = 1, lcon = [-Inf], ucon = [0.0]),
26-
NLPModelMeta(
27-
2,
28-
lvar = zeros(2),
29-
uvar = ones(2),
30-
ncon = 1,
31-
lcon = [0.0],
32-
ucon = [1.0],
33-
lin = [1],
34-
),
35-
NLPModelMeta(
36-
2,
37-
lvar = zeros(2),
38-
uvar = ones(2),
39-
ncon = 2,
40-
lcon = [0.0, 0.0],
41-
ucon = [1.0, 1.0],
42-
lin = [1],
43-
),
44-
NLPModelMeta(
45-
2,
46-
lvar = zeros(2),
47-
uvar = ones(2),
48-
ncon = 2,
49-
lcon = [0.0, 0.0],
50-
ucon = [1.0, 0.0],
51-
lin = [1],
52-
),
53-
]
54-
results = Bool[
55-
0 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1
56-
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
57-
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
58-
0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0
59-
0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0
60-
0 0 0 1 1 1 1 1 0 0 1 1 1 1 1 0
61-
0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 1
62-
0 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1
63-
]
64-
for (i, f) in enumerate(foo_list), (j, meta) in enumerate(meta_list)
65-
@test f(meta) == results[i, j]
66-
@test f(DummyModel(meta)) == results[i, j]
67-
end
68-
for f in fieldnames(NLPModelMeta), (j, meta) in enumerate(meta_list)
69-
@test eval(Meta.parse("get_" * string(f)))(meta) == getproperty(meta, f)
70-
@test eval(Meta.parse("get_" * string(f)))(DummyModel(meta)) == getproperty(meta, f)
2+
@testset "Analysis = $bool" for bool in (false, true)
3+
foo_list = [
4+
has_bounds,
5+
bound_constrained,
6+
unconstrained,
7+
linearly_constrained,
8+
equality_constrained,
9+
inequality_constrained,
10+
has_equalities,
11+
has_inequalities,
12+
]
13+
meta_list = [
14+
NLPModelMeta(2, variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
15+
NLPModelMeta(2, lvar = zeros(2), uvar = ones(2), variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
16+
NLPModelMeta(2, ncon = 1, lcon = [0.0], ucon = [0.0], variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
17+
NLPModelMeta(2, ncon = 1, lcon = [0.0], ucon = [1.0], variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
18+
NLPModelMeta(2, ncon = 1, lcon = [0.0], ucon = [Inf], variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
19+
NLPModelMeta(2, ncon = 1, lcon = [-Inf], ucon = [0.0], variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
20+
NLPModelMeta(2, ncon = 1, lcon = [0.0], ucon = [1.0], lin = [1], variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
21+
NLPModelMeta(2, ncon = 2, lcon = [0.0, 0.0], ucon = [1.0, 1.0], lin = [1], variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
22+
NLPModelMeta(2, ncon = 2, lcon = [0.0, 0.0], ucon = [1.0, 0.0], lin = [1], variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
23+
NLPModelMeta(2, lvar = zeros(2), uvar = ones(2), ncon = 1, lcon = [0.0], ucon = [0.0], variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
24+
NLPModelMeta(2, lvar = zeros(2), uvar = ones(2), ncon = 1, lcon = [0.0], ucon = [1.0], variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
25+
NLPModelMeta(2, lvar = zeros(2), uvar = ones(2), ncon = 1, lcon = [0.0], ucon = [Inf], variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
26+
NLPModelMeta(2, lvar = zeros(2), uvar = ones(2), ncon = 1, lcon = [-Inf], ucon = [0.0], variable_bounds_analysis=bool, constraint_bounds_analysis=bool),
27+
NLPModelMeta(
28+
2,
29+
lvar = zeros(2),
30+
uvar = ones(2),
31+
ncon = 1,
32+
lcon = [0.0],
33+
ucon = [1.0],
34+
lin = [1],
35+
variable_bounds_analysis=bool,
36+
constraint_bounds_analysis=bool,
37+
),
38+
NLPModelMeta(
39+
2,
40+
lvar = zeros(2),
41+
uvar = ones(2),
42+
ncon = 2,
43+
lcon = [0.0, 0.0],
44+
ucon = [1.0, 1.0],
45+
lin = [1],
46+
variable_bounds_analysis=bool,
47+
constraint_bounds_analysis=bool,
48+
),
49+
NLPModelMeta(
50+
2,
51+
lvar = zeros(2),
52+
uvar = ones(2),
53+
ncon = 2,
54+
lcon = [0.0, 0.0],
55+
ucon = [1.0, 0.0],
56+
lin = [1],
57+
variable_bounds_analysis=bool,
58+
constraint_bounds_analysis=bool,
59+
),
60+
]
61+
results = Bool[
62+
0 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1
63+
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
64+
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
65+
0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0
66+
0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0
67+
0 0 0 1 1 1 1 1 0 0 1 1 1 1 1 0
68+
0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 1
69+
0 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1
70+
]
71+
for (i, f) in enumerate(foo_list), (j, meta) in enumerate(meta_list)
72+
@test f(meta) == results[i, j]
73+
@test f(DummyModel(meta)) == results[i, j]
74+
end
75+
for f in fieldnames(NLPModelMeta), (j, meta) in enumerate(meta_list)
76+
@test eval(Meta.parse("get_" * string(f)))(meta) == getproperty(meta, f)
77+
@test eval(Meta.parse("get_" * string(f)))(DummyModel(meta)) == getproperty(meta, f)
78+
end
7179
end
7280
end

0 commit comments

Comments
 (0)