Skip to content

Commit 5f061b6

Browse files
committed
Update
1 parent a61f947 commit 5f061b6

2 files changed

Lines changed: 222 additions & 6 deletions

File tree

src/FileFormats/NL/NL.jl

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ mutable struct Model <: MOI.ModelLike
146146
MOI.Utilities.UniversalFallback{MOI.Utilities.Model{Float64}},
147147
}
148148
use_nlp_block::Bool
149+
complementarity_constraints::Vector{Vector{Int}}
149150

150151
function Model(; use_nlp_block::Bool = true)
151152
return new(
@@ -160,6 +161,7 @@ mutable struct Model <: MOI.ModelLike
160161
MOI.VariableIndex[],
161162
nothing,
162163
use_nlp_block,
164+
Vector{Int}[],
163165
)
164166
end
165167
end
@@ -185,6 +187,7 @@ function MOI.empty!(model::Model)
185187
end
186188
empty!(model.order)
187189
model.model = nothing
190+
empty!(model.complementarity_constraints)
188191
return
189192
end
190193

@@ -222,6 +225,21 @@ function MOI.supports_constraint(
222225
return true
223226
end
224227

228+
function MOI.supports_constraint(
229+
::Model,
230+
::Type{F},
231+
::Type{MOI.Complements},
232+
) where {
233+
F<:Union{
234+
MOI.VectorOfVariables,
235+
MOI.VectorAffineFunction{Float64},
236+
MOI.VectorQuadraticFunction{Float64},
237+
MOI.VectorNonlinearFunction,
238+
},
239+
}
240+
return true
241+
end
242+
225243
MOI.supports(::Model, ::MOI.ObjectiveSense) = true
226244
MOI.supports(::Model, ::MOI.ObjectiveFunction{<:_SCALAR_FUNCTIONS}) = true
227245

@@ -493,6 +511,48 @@ function _process_constraint(
493511
return
494512
end
495513

514+
_to_x(f) = convert(MOI.VariableIndex, f)
515+
516+
function _to_x(f::MOI.ScalarNonlinearFunction)
517+
# Hacky way to ensure that f is a standalone variable
518+
@assert f isa MOI.ScalarNonlinearFunction
519+
@assert f.head == :+ && length(f.args) == 1
520+
@assert f.args[1] isa MOI.VariableIndex
521+
return return f.args[1]
522+
end
523+
524+
function _process_constraint(
525+
dest::Model,
526+
model,
527+
::Type{F},
528+
::Type{S},
529+
mapping,
530+
) where {F,S<:MOI.Complements}
531+
ci_src = MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
532+
for ci in ci_src
533+
f_vec = MOI.get(model, MOI.ConstraintFunction(), ci)
534+
f_scalars = MOI.Utilities.scalarize(f_vec)
535+
n = div(MOI.output_dimension(f_vec), 2)
536+
rows = Int[]
537+
for i in 1:n
538+
fi, xi = f_scalars[i], _to_x(f_scalars[i+n])
539+
con = _NLConstraint(Float64(xi.value), Inf, 5, _NLExpr(fi))
540+
if con.expr.is_linear
541+
push!(dest.h, con)
542+
push!(rows, -length(dest.h))
543+
else
544+
push!(dest.g, con)
545+
push!(rows, length(dest.g))
546+
end
547+
end
548+
push!(dest.complementarity_constraints, rows)
549+
mapping[ci] =
550+
MOI.ConstraintIndex{F,S}(length(dest.complementarity_constraints))
551+
end
552+
MOI.Utilities.pass_attributes(dest, model, mapping, ci_src)
553+
return
554+
end
555+
496556
function _str(x::Float64)
497557
if isinteger(x) && (typemin(Int) <= x <= typemax(Int))
498558
return string(round(Int, x))
@@ -571,13 +631,19 @@ function Base.write(io::IO, model::Model)
571631
# Line 3: nonlinear constraints, objectives
572632
# Notes:
573633
# * We assume there is always one objective, even if it is just `min 0`.
574-
# * `Writing .nl Files` lies! There are four extra integers here
634+
# * `Writing .nl Files` lies: there are four extra integers here
575635
# * Number of linear complementarity constraints
576636
# * Number of nonlinear complementarity constraints
577637
# * nd: I have no idea
578638
# * nzlb: I have no idea
579639
n_nlcon = length(model.g)
580-
println(io, " ", n_nlcon, " 1 0 0 0 0")
640+
ccon_lin = sum(c.opcode == 5 for c in model.h; init = 0)
641+
ccon_nl = sum(c.opcode == 5 for c in model.g; init = 0)
642+
if ccon_lin + ccon_nl > 0
643+
println(io, " ", n_nlcon, " 1 ", ccon_lin, " ", ccon_nl, " 0 0")
644+
else
645+
println(io, " ", n_nlcon, " ", 1)
646+
end
581647

582648
# Line 4: network constraints: nonlinear, linear
583649
# Notes:
@@ -699,9 +765,15 @@ function Base.write(io::IO, model::Model)
699765
println(io, " ", _str(g.lower))
700766
elseif g.opcode == 3
701767
println(io)
702-
else
703-
@assert g.opcode == 4
768+
elseif g.opcode == 4
704769
println(io, " ", _str(g.lower))
770+
else
771+
@assert g.opcode == 5
772+
@assert !isfinite(g.upper)
773+
x = MOI.VariableIndex(g.lower)
774+
v = model.x[x]
775+
k = (-Inf < v.lower) + 2 * (v.upper < Inf)
776+
println(io, " ", k, " ", v.order + 1)
705777
end
706778
end
707779
# Linear constraints
@@ -715,9 +787,15 @@ function Base.write(io::IO, model::Model)
715787
println(io, " ", _str(h.lower))
716788
elseif h.opcode == 3
717789
println(io)
718-
else
719-
@assert h.opcode == 4
790+
elseif h.opcode == 4
720791
println(io, " ", _str(h.lower))
792+
else
793+
@assert h.opcode == 5
794+
@assert !isfinite(h.upper)
795+
x = MOI.VariableIndex(h.lower)
796+
v = model.x[x]
797+
k = (-Inf < v.lower) + 2 * (v.upper < Inf)
798+
println(io, " ", k, " ", v.order + 1)
721799
end
722800
end
723801
end

test/FileFormats/NL/NL.jl

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,144 @@ function test_unsupported_objectives()
13941394
return
13951395
end
13961396

1397+
function test_write_complements_VectorOfVariables()
1398+
for (set, k, b) in (
1399+
(MOI.GreaterThan(0.0), 1, "2 0"),
1400+
(MOI.LessThan(1.0), 2, "1 1"),
1401+
(MOI.EqualTo(1.0), 3, "4 1"),
1402+
(MOI.Interval(0.0, 1.0), 3, "0 0 1"),
1403+
)
1404+
src = MOI.Utilities.Model{Float64}()
1405+
x = MOI.add_variable(src)
1406+
y, _ = MOI.add_constrained_variable(src, MOI.Interval(0.0, 1.0))
1407+
MOI.add_constraint(src, x, set)
1408+
MOI.add_constraint(
1409+
src,
1410+
MOI.Utilities.vectorize([y, x]),
1411+
MOI.Complements(2),
1412+
)
1413+
dest = NL.Model()
1414+
MOI.copy_to(dest, src)
1415+
@test sprint(write, dest) == """
1416+
g3 1 1 0
1417+
2 1 1 0 0 0
1418+
0 1 1 0 0 0
1419+
0 0
1420+
0 0 0
1421+
0 0 0 1
1422+
0 0 0 0 0
1423+
1 0
1424+
0 0
1425+
0 0 0 0 0
1426+
C0
1427+
n0
1428+
O0 0
1429+
n0
1430+
x2
1431+
0 0
1432+
1 0
1433+
r
1434+
5 $k 1
1435+
b
1436+
$b
1437+
0 0 1
1438+
k1
1439+
0
1440+
J0 1
1441+
1 1
1442+
"""
1443+
end
1444+
return
1445+
end
1446+
1447+
function test_write_complements_VectorAffineFunction()
1448+
for (set, k, b) in (
1449+
(MOI.GreaterThan(0.0), 1, "2 0"),
1450+
(MOI.LessThan(1.0), 2, "1 1"),
1451+
(MOI.EqualTo(1.0), 3, "4 1"),
1452+
(MOI.Interval(0.0, 1.0), 3, "0 0 1"),
1453+
)
1454+
src = MOI.Utilities.Model{Float64}()
1455+
x = MOI.add_variable(src)
1456+
MOI.add_constraint(src, x, set)
1457+
MOI.add_constraint(
1458+
src,
1459+
MOI.Utilities.vectorize([1.0 - x, x]),
1460+
MOI.Complements(2),
1461+
)
1462+
dest = NL.Model()
1463+
MOI.copy_to(dest, src)
1464+
@test sprint(write, dest) == """
1465+
g3 1 1 0
1466+
1 1 1 0 0 0
1467+
0 1 1 0 0 0
1468+
0 0
1469+
0 0 0
1470+
0 0 0 1
1471+
0 0 0 0 0
1472+
1 0
1473+
0 0
1474+
0 0 0 0 0
1475+
C0
1476+
n1
1477+
O0 0
1478+
n0
1479+
x1
1480+
0 0
1481+
r
1482+
5 $k 1
1483+
b
1484+
$b
1485+
k0
1486+
J0 1
1487+
0 -1
1488+
"""
1489+
end
1490+
return
1491+
end
1492+
1493+
function test_write_complements_VectorNonlinearFunction()
1494+
src = MOI.Utilities.Model{Float64}()
1495+
x, _ = MOI.add_constrained_variable(src, MOI.Interval(0.0, 1.0))
1496+
MOI.add_constraint(
1497+
src,
1498+
MOI.VectorNonlinearFunction([
1499+
MOI.ScalarNonlinearFunction(:sin, Any[x]),
1500+
MOI.ScalarNonlinearFunction(:+, Any[x]),
1501+
]),
1502+
MOI.Complements(2),
1503+
)
1504+
dest = NL.Model()
1505+
MOI.copy_to(dest, src)
1506+
@test sprint(write, dest) == """
1507+
g3 1 1 0
1508+
1 1 1 0 0 0
1509+
1 1 0 1 0 0
1510+
0 0
1511+
1 0 0
1512+
0 0 0 1
1513+
0 0 0 0 0
1514+
1 0
1515+
0 0
1516+
0 0 0 0 0
1517+
C0
1518+
o41
1519+
v0
1520+
O0 0
1521+
n0
1522+
x1
1523+
0 0
1524+
r
1525+
5 3 1
1526+
b
1527+
0 0 1
1528+
k0
1529+
J0 1
1530+
0 0
1531+
"""
1532+
return
1533+
end
1534+
13971535
end
13981536

13991537
TestNLModel.runtests()

0 commit comments

Comments
 (0)