Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matbesancon/667a1089f5114e29bc804fe8614a96eb to your computer and use it in GitHub Desktop.
Save matbesancon/667a1089f5114e29bc804fe8614a96eb to your computer and use it in GitHub Desktop.
Bilevel AUX Reader
"""
Takes the model loaded from the MPS file and the line of the `.aux` file.
"""
function build_from_mibp(m::JuMP.Model, aux_file::Vector{String}; complete=false)
l1 = split(aux_file[1])
@assert length(l1) == 2
nl = parse(Int, l1[2])
nu = num_variables(m) - nl
l2 = split(aux_file[2])
@assert length(l2) == 2
ml = parse(Int, l2[2])
mu = num_constraints(m, AffExpr, MOI.GreaterThan{Float64}) - ml
curr_line_idx = 3
lower_var = Int[]
while true
curr_line = split(aux_file[curr_line_idx])
if curr_line[1] != "LC"
break
end
vname = "C" * pad_string(parse(Int, curr_line[2]))
vidx = variable_by_name(m, vname).index.value
push!(lower_var, vidx)
curr_line_idx += 1
end
@info "Finished LC phase: $curr_line_idx $curr_line"
upper_var = filter(1:nl+nu) do v
v ∉ lower_var
end
@assert length(upper_var) == nu
@assert length(lower_var) == nl
lower_cons = BitSet()
b = Float64[]
A = spzeros(ml, nu)
B = spzeros(ml, nl)
curr_row = 1
while true
curr_line = split(aux_file[curr_line_idx])
if curr_line[1] != "LR"
break
end
cname = "R" * pad_string(parse(Int, curr_line[2]))
cons = constraint_by_name(m, cname, AffExpr, MOI.GreaterThan{Float64})
cons.index isa MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},MOI.GreaterThan{Float64}} || error("unexpected type $cidx.index")
cfunc = MOI.get(m, MOI.ConstraintFunction(), cons)
cset = MOI.get(m, MOI.ConstraintSet(), cons)
@assert cfunc.constant ≈ 0
push!(lower_cons, cons.index.value)
push!(b, -cset.lower)
for term in cfunc.terms
vidx = findfirst(lower_var) do idx
idx == term.variable_index.value
end
if vidx !== nothing # found in lower variable
B[curr_row, vidx] = -term.coefficient
else # find in upper variable
vidx = findfirst(upper_var) do idx
idx == term.variable_index.value
end
vidx === nothing && error("index not found for term $term")
A[curr_row, vidx] = -term.coefficient
end
end
curr_line_idx += 1
curr_row += 1
end
@assert length(lower_cons) == ml
@info "Finished LR phase: $curr_line_idx"
# build upper-level constraints
q = Float64[]
G = spzeros(mu, nu)
H = spzeros(mu, nl)
curr_row = 1
for cons in all_constraints(m, AffExpr, MOI.GreaterThan{Float64})
if cons.index.value ∉ lower_cons
@info "upper found"
cfunc = MOI.get(m, MOI.ConstraintFunction(), cons)
cset = MOI.get(m, MOI.ConstraintSet(), cons)
push!(q, -cset.lower)
for term in cfunc.terms
vidx = findfirst(lower_var) do idx
idx == term.variable_index.value
end
if vidx !== nothing # found in lower variable
H[curr_row, vidx] = -term.coefficient
else # find in upper variable
vidx = findfirst(upper_var) do idx
idx == term.variable_index.value
end
vidx === nothing && error("index not found for term $term")
G[curr_row, vidx] = -term.coefficient
end
end
curr_row += 1
end
end
# fetching objective
nobj = 0
d = Float64[]
while true
curr_line = split(aux_file[curr_line_idx])
if curr_line[1] != "LO"
break
end
push!(d,
parse(Float64, curr_line[2])
)
curr_line_idx += 1
nobj += 1
end
@info "Finished LO phase: $curr_line_idx"
curr_line = split(aux_file[curr_line_idx])
@assert nobj == nl
curr_line[1] == "OS" || error("$curr_line_idx: $curr_line")
if curr_line[2] == "-1"
d .*= -1
end
# upper-level objective
cx = Float64[]
for vidx in upper_var
for (varref, coeff) in objective_function(m).terms
if varref.index.value == vidx
push!(cx, coeff)
end
end
end
cy = Float64[]
for vidx in lower_var
for (varref, coeff) in objective_function(m).terms
if varref.index.value == vidx
push!(cy, coeff)
end
end
end
if objective_sense(m) == MOI.MAX_SENSE
cx .*= -1
cy .*= -1
end
if mu > 0
return [(G, H, A, B, cx, cy, d, q, b)]
end
# else, split constraints
dv = ml ÷ 3
Ga = A[1:dv,:]
Aa = A[dv+1:end,:]
Ha = B[1:dv,:]
Ba = B[dv+1:end,:]
qa = b[1:dv]
ba = b[dv+1:end]
Gb = A[end-dv+1:end,:]
Ab = A[1:end-dv,:]
Hb = B[end-dv+1:end,:]
Bb = B[1:end-dv,:]
qb = b[end-dv+1:end]
bb = b[1:end-dv]
if !complete
return [(Ga, Ha, Aa, Ba, cx, cy, d, qa, ba), (Gb, Hb, Ab, Bb, cx, cy, d, qb, bb)]
end
Gc = Aa
Ac = Ga
Hc = Ba
Bc = Ha
qc = ba
bc = qa
Gd = Ab
Ad = Gb
Hd = Bb
Bd = Hb
qd = bb
bd = qb
return [(Ga, Ha, Aa, Ba, cx, cy, d, qa, ba), (Gb, Hb, Ab, Bb, cx, cy, d, qb, bb), (Gc, Hc, Ac, Bc, cx, cy, d, qc, bc), (Gd, Hd, Ad, Bd, cx, cy, d, qd, bd)]
end
# example
const data_path = joinpath(@__DIR__, "../data/notInterdiction/MIPS/RAND_BILEVEL")
const all_files = Filesystem.readdir(data_path)
for file in all_files
global results
global result_times
if !endswith(file, "mps")
continue
end
file_base = split(file, '.')[1]
m = JuMP.read_from_file(joinpath(data_path, file))
list_cons_types = [
(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}),
(MOI.SingleVariable, MOI.Interval{Float64}),
(MOI.SingleVariable, MOI.Integer),
]
all(zip(list_cons_types, MOI.get(m, MOI.ListOfConstraints()))) do (c1, c2)
c1 == c2
end || error("hello")
aux_file = open(joinpath(data_path, file_base * ".aux")) do f
readlines(f)
end
prob_instance = build_from_mibp(m, aux_file, complete=true)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment