This repository has been archived by the owner on Apr 21, 2022. It is now read-only.
/
MathOptFormat.jl
192 lines (176 loc) · 6.61 KB
/
MathOptFormat.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
module MathOptFormat
import MathOptInterface
const MOI = MathOptInterface
import CodecZlib
include("CBF/CBF.jl")
include("LP/LP.jl")
include("MOF/MOF.jl")
include("MPS/MPS.jl")
"""
create_unique_names(
model::MOI.ModelLike; warn::Bool = false,
replacements::Vector{Pair{Char, Char}} = Pair{Char, Char}[]
)
Rename variables in `model` to ensure that all variables and constraints have
a unique name. In addition, loop through `replacements` pairs `old => new` and
replace all `old` `Char`s with `new`.
If `warn`, print a warning if a variable or constraint is renamed.
"""
function create_unique_names(
model::MOI.ModelLike; warn::Bool = false,
replacements::Vector{Pair{Char, Char}} = Pair{Char, Char}[]
)
create_unique_variable_names(model, warn, replacements)
create_unique_constraint_names(model, warn, replacements)
return
end
function _replace(s::String, replacements::Vector{Pair{Char, Char}})
for replacement in replacements
s = replace(s, replacement)
end
return s
end
function create_unique_constraint_names(
model::MOI.ModelLike, warn::Bool, replacements::Vector{Pair{Char, Char}}
)
original_names = Set{String}()
for (F, S) in MOI.get(model, MOI.ListOfConstraints())
for index in MOI.get(model, MOI.ListOfConstraintIndices{F, S}())
name = MOI.get(model, MOI.ConstraintName(), index)
push!(original_names, name)
end
end
added_names = Set{String}()
for (F, S) in MOI.get(model, MOI.ListOfConstraints())
for index in MOI.get(model, MOI.ListOfConstraintIndices{F, S}())
original_name = MOI.get(model, MOI.ConstraintName(), index)
new_name = _replace(
original_name != "" ? original_name : "c$(index.value)",
replacements
)
if new_name in added_names
# We found a duplicate name! We could just append a string like "_",
# but we're going to be clever and loop through the integers to name
# them appropriately. Thus, if we have three constraints named c,
# we'll end up with variables named c, c_1, and c_2.
i = 1
tmp_name = string(new_name, "_", i)
while tmp_name in added_names || tmp_name in original_names
i += 1
tmp_name = string(new_name, "_", i)
end
new_name = tmp_name
end
push!(added_names, new_name)
if new_name != original_name
if warn
if original_name == ""
@warn("Blank name detected for constraint $(index). " *
"Renamed to $(new_name).")
else
@warn("Duplicate name $(original_name) detected for " *
"constraint $(index). Renamed to $(new_name).")
end
end
MOI.set(model, MOI.ConstraintName(), index, new_name)
end
end
end
end
function create_unique_variable_names(
model::MOI.ModelLike, warn::Bool, replacements::Vector{Pair{Char, Char}}
)
variables = MOI.get(model, MOI.ListOfVariableIndices())
# This is a list of all of the names currently in the model. We're going to
# use this to make sure we don't rename a variable to a name that already
# exists.
original_names = Set{String}([
MOI.get(model, MOI.VariableName(), index) for index in variables])
# This set of going to store all of the names in the model so that we don't
# add duplicates.
added_names = Set{String}()
for index in variables
original_name = MOI.get(model, MOI.VariableName(), index)
new_name = _replace(
original_name != "" ? original_name : "x$(index.value)",
replacements
)
if new_name in added_names
# We found a duplicate name! We could just append a string like "_",
# but we're going to be clever and loop through the integers to name
# them appropriately. Thus, if we have three variables named x,
# we'll end up with variables named x, x_1, and x_2.
i = 1
tmp_name = string(new_name, "_", i)
while tmp_name in added_names || tmp_name in original_names
i += 1
tmp_name = string(new_name, "_", i)
end
new_name = tmp_name
end
push!(added_names, new_name)
if new_name != original_name
if warn
if original_name == ""
@warn("Blank name detected for variable $(index). Renamed to " *
"$(new_name).")
else
@warn("Duplicate name $(original_name) detected for variable " *
"$(index). Renamed to $(new_name).")
end
end
MOI.set(model, MOI.VariableName(), index, new_name)
end
end
end
function gzip_open(f::Function, filename::String, mode::String)
if endswith(filename, ".gz")
if mode == "r"
open(CodecZlib.GzipDecompressorStream, filename, mode) do io
f(io)
end
elseif mode == "w"
open(CodecZlib.GzipCompressorStream, filename, mode) do io
f(io)
end
else
throw(ArgumentError("Mode must be \"r\" or \"w\""))
end
else
return open(f, filename, mode)
end
end
const MATH_OPT_FORMATS = Union{
CBF.InnerModel, LP.InnerModel, MOF.Model, MPS.InnerModel
}
function MOI.write_to_file(model::MATH_OPT_FORMATS, filename::String)
gzip_open(filename, "w") do io
MOI.write_to_file(model, io)
end
end
function MOI.read_from_file(model::MATH_OPT_FORMATS, filename::String)
gzip_open(filename, "r") do io
MOI.read_from_file(model, io)
end
end
"""
read_from_file(filename::String)
Create a MOI model by reading `filename`. Type of the returned model depends on
the extension of `filename`.
"""
function read_from_file(filename::String)
model = if endswith(filename, ".mof.json.gz") || endswith(filename, ".mof.json")
MOF.Model()
elseif endswith(filename, ".cbf.gz") || endswith(filename, ".cbf")
CBF.Model()
elseif endswith(filename, ".mps.gz") || endswith(filename, ".mps")
MPS.Model()
elseif endswith(filename, ".lp.gz") || endswith(filename, ".lp")
LP.Model()
else
error("File-type of $(filename) not supported by MathOptFormat.jl.")
end
MOI.read_from_file(model, filename)
return model
end
end