Skip to content

Commit 6988d69

Browse files
authored
Merge pull request #2688 from jump-dev/od/use-nlp-block
[FileFormats.MOF] make use_nlp_block=false the default if SNF in model
2 parents 7a0dd03 + 1f3cd21 commit 6988d69

File tree

4 files changed

+65
-13
lines changed

4 files changed

+65
-13
lines changed

src/FileFormats/MOF/MOF.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,14 @@ struct Options
112112
print_compact::Bool
113113
warn::Bool
114114
differentiation_backend::MOI.Nonlinear.AbstractAutomaticDifferentiation
115-
use_nlp_block::Bool
115+
use_nlp_block::Union{Bool,Nothing}
116116
end
117117

118118
function get_options(m::Model)
119119
return get(
120120
m.model.ext,
121121
:MOF_OPTIONS,
122-
Options(false, false, MOI.Nonlinear.SparseReverseMode(), true),
122+
Options(false, false, MOI.Nonlinear.SparseReverseMode(), nothing),
123123
)
124124
end
125125

@@ -144,7 +144,7 @@ function Model(;
144144
print_compact::Bool = false,
145145
warn::Bool = false,
146146
differentiation_backend::MOI.Nonlinear.AbstractAutomaticDifferentiation = MOI.Nonlinear.SparseReverseMode(),
147-
use_nlp_block::Bool = true,
147+
use_nlp_block::Union{Bool,Nothing} = nothing,
148148
)
149149
model = MOI.Utilities.UniversalFallback(InnerModel{Float64}())
150150
model.model.ext[:MOF_OPTIONS] =

src/FileFormats/MOF/read.jl

+9-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,15 @@ function Base.read!(io::IO, model::Model)
2727
read_objective(model, object, name_map)
2828
read_constraints(model, object, name_map)
2929
options = get_options(model)
30-
if options.use_nlp_block
30+
# We should convert to NLPBlock if...
31+
# | options.use_nlp_block
32+
# | true false nothing
33+
# object["has_scalar_nonlinear"] = false | 1 0 1
34+
# = true | 1 0 0
35+
if something(
36+
options.use_nlp_block,
37+
!get(object, "has_scalar_nonlinear", false),
38+
)
3139
_convert_to_nlpblock(model)
3240
end
3341
return

src/FileFormats/MOF/write.jl

+12-5
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ function Base.write(io::IO, model::Model)
1515
variables, constraints = NamedTuple[], NamedTuple[]
1616
name_map = _write_variables(variables, model)
1717
objective = _write_nlpblock(constraints, model, name_map)
18+
has_scalar_nonlinear = false
1819
if objective === nothing
19-
objective = _write_objective(model, name_map)
20+
objective, has_scalar_nonlinear = _write_objective(model, name_map)
2021
end
21-
_write_constraints(constraints, model, name_map)
22+
has_scalar_nonlinear |= _write_constraints(constraints, model, name_map)
2223
object = (;
2324
name = "MathOptFormat Model",
2425
version = (
@@ -29,6 +30,9 @@ function Base.write(io::IO, model::Model)
2930
objective = objective,
3031
constraints = constraints,
3132
)
33+
if has_scalar_nonlinear
34+
object = (; has_scalar_nonlinear = true, object...)
35+
end
3236
JSON3.write(io, object)
3337
return
3438
end
@@ -122,27 +126,30 @@ function _write_objective(
122126
)
123127
sense = MOI.get(model, MOI.ObjectiveSense())
124128
if sense == MOI.FEASIBILITY_SENSE
125-
return (; :sense => moi_to_object(sense))
129+
return (; :sense => moi_to_object(sense)), false
126130
end
127131
F = MOI.get(model, MOI.ObjectiveFunctionType())
128132
objective_function = MOI.get(model, MOI.ObjectiveFunction{F}())
129-
return (;
133+
object = (;
130134
:sense => moi_to_object(sense),
131135
:function => moi_to_object(objective_function, name_map),
132136
)
137+
return object, (F == MOI.ScalarNonlinearFunction)
133138
end
134139

135140
function _write_constraints(
136141
constraints::Vector{NamedTuple},
137142
model::Model,
138143
name_map::Dict{MOI.VariableIndex,String},
139144
)
145+
has_scalar_nonlinear = false
140146
for (F, S) in MOI.get(model, MOI.ListOfConstraintTypesPresent())
147+
has_scalar_nonlinear |= (F == MOI.ScalarNonlinearFunction)
141148
for index in MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
142149
push!(constraints, moi_to_object(index, model, name_map))
143150
end
144151
end
145-
return
152+
return has_scalar_nonlinear
146153
end
147154

148155
"""

test/FileFormats/MOF/MOF.jl

+41-4
Original file line numberDiff line numberDiff line change
@@ -767,8 +767,7 @@ variables: x
767767
minobjective: ScalarNonlinearFunction(exp(x))
768768
""",
769769
["x"],
770-
String[];
771-
use_nlp_block = false,
770+
String[],
772771
)
773772
end
774773

@@ -779,8 +778,7 @@ variables: x
779778
c1: ScalarNonlinearFunction(exp(x)^2) <= 1.0
780779
""",
781780
["x"],
782-
["c1"];
783-
use_nlp_block = false,
781+
["c1"],
784782
)
785783
end
786784

@@ -1553,6 +1551,45 @@ function test_write_NLPBlock_no_objective()
15531551
return
15541552
end
15551553

1554+
function test_use_nlp_block()
1555+
model = MOF.Model()
1556+
x = MOI.add_variable(model)
1557+
MOI.set(model, MOI.VariableName(), x, "x")
1558+
f = MOI.ScalarNonlinearFunction(:log, Any[x])
1559+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
1560+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
1561+
io = IOBuffer()
1562+
write(io, model)
1563+
seekstart(io)
1564+
object = JSON3.read(io, Dict{String,Any})
1565+
@test object["has_scalar_nonlinear"] == true
1566+
@test JSONSchema.validate(SCHEMA, object) === nothing
1567+
# Test (; use_nlp_block = nothing)
1568+
seekstart(io)
1569+
model = MOF.Model()
1570+
read!(io, model)
1571+
@test MOI.get(model, MOI.NLPBlock()) === nothing
1572+
@test MOI.get(model, MOI.ObjectiveFunctionType()) ==
1573+
MOI.ScalarNonlinearFunction
1574+
# Test (; use_nlp_block = false)
1575+
seekstart(io)
1576+
model = MOF.Model(; use_nlp_block = false)
1577+
read!(io, model)
1578+
@test MOI.get(model, MOI.NLPBlock()) === nothing
1579+
@test MOI.get(model, MOI.ObjectiveFunctionType()) ==
1580+
MOI.ScalarNonlinearFunction
1581+
# Test (; use_nlp_block = true)
1582+
seekstart(io)
1583+
model = MOF.Model(; use_nlp_block = true)
1584+
read!(io, model)
1585+
block = MOI.get(model, MOI.NLPBlock())
1586+
@test block isa MOI.NLPBlockData
1587+
@test block.has_objective == true
1588+
@test MOI.get(model, MOI.ObjectiveFunctionType()) ==
1589+
MOI.ScalarAffineFunction{Float64}
1590+
return
1591+
end
1592+
15561593
end
15571594

15581595
TestMOF.runtests()

0 commit comments

Comments
 (0)