joaquimg / BilevelJuMP.jl
Showing 11 of 36 files from the diff.

@@ -10,43 +10,37 @@
Loading
10 10
const LT = MOI.LessThan
11 11
const ET = MOI.EqualTo
12 12
13 -
const SCALAR_SETS = Union{
14 -
    MOI.GreaterThan{Float64},
15 -
    MOI.LessThan{Float64},
16 -
    MOI.EqualTo{Float64},
17 -
}
13 +
const SCALAR_SETS =
14 +
    Union{MOI.GreaterThan{Float64},MOI.LessThan{Float64},MOI.EqualTo{Float64}}
18 15
19 -
const VECTOR_SETS = Union{
20 -
    MOI.SecondOrderCone,
21 -
    MOI.RotatedSecondOrderCone,
22 -
}
16 +
const VECTOR_SETS = Union{MOI.SecondOrderCone,MOI.RotatedSecondOrderCone}
23 17
24 18
# dual variable info
25 -
mutable struct BilevelConstraintInfo{T<:Union{Float64, Vector{Float64}}}
19 +
mutable struct BilevelConstraintInfo{T<:Union{Float64,Vector{Float64}}}
26 20
    level::Level
27 21
    start::T
28 22
    upper::T
29 23
    lower::T
30 24
    function BilevelConstraintInfo{Float64}(level)
31 -
        new(level, NaN, NaN, NaN)
25 +
        return new(level, NaN, NaN, NaN)
32 26
    end
33 27
    function BilevelConstraintInfo{Vector{Float64}}(level, N::Integer)
34 -
        new(level, fill(NaN, N), fill(NaN, N), fill(NaN, N))
28 +
        return new(level, fill(NaN, N), fill(NaN, N), fill(NaN, N))
35 29
    end
36 30
end
37 31
38 -
mutable struct BilevelVariableInfo{T<:Union{Float64, Vector{Float64}}}
32 +
mutable struct BilevelVariableInfo{T<:Union{Float64,Vector{Float64}}}
39 33
    level::Level
40 34
    upper::T
41 35
    lower::T
42 36
    function BilevelVariableInfo(level)
43 -
        new{Float64}(level, NaN, NaN)
37 +
        return new{Float64}(level, NaN, NaN)
44 38
    end
45 39
    function BilevelVariableInfo(level, N::Integer)
46 -
        new{Vector{Float64}}(level, fill(NaN, N), fill(NaN, N))
40 +
        return new{Vector{Float64}}(level, fill(NaN, N), fill(NaN, N))
47 41
    end
48 42
end
49 -
function BilevelVariableInfo(_info::BilevelConstraintInfo{T}) where T
43 +
function BilevelVariableInfo(_info::BilevelConstraintInfo{T}) where {T}
50 44
    if isa(_info.upper, Number)
51 45
        info = BilevelVariableInfo(DUAL_OF_LOWER)
52 46
        info.lower = _info.lower
@@ -60,20 +54,19 @@
Loading
60 54
end
61 55
62 56
struct Complement#{M1 <: MOI.ModelLike, M2 <: MOI.ModelLike, F, S}
63 -
    is_vec
57 +
    is_vec::Any
64 58
    # primal::M1
65 -
    constraint
66 -
    func_w_cte#::F
67 -
    set_w_zero#::S
59 +
    constraint::Any
60 +
    func_w_cte::Any#::F
61 +
    set_w_zero::Any#::S
68 62
    # dual::M2
69 -
    variable#::VI
63 +
    variable::Any#::VI
70 64
    # var_set#::S2
71 65
end
72 66
73 67
abstract type AbstractBilevelSolverMode{T} end
74 68
75 -
mutable struct NoMode{T} <: AbstractBilevelSolverMode{T}
76 -
end
69 +
mutable struct NoMode{T} <: AbstractBilevelSolverMode{T} end
77 70
78 71
mutable struct SOS1Mode{T} <: AbstractBilevelSolverMode{T}
79 72
    function SOS1Mode()
@@ -83,7 +76,7 @@
Loading
83 76
84 77
mutable struct ComplementMode{T} <: AbstractBilevelSolverMode{T}
85 78
    with_slack::Bool
86 -
    function ComplementMode(;with_slack = false)
79 +
    function ComplementMode(; with_slack = false)
87 80
        return new{Float64}(with_slack)
88 81
    end
89 82
end
@@ -92,12 +85,12 @@
Loading
92 85
    epsilon::T
93 86
    with_slack::Bool
94 87
    aggregation_group::Int # only useful in mixed mode
95 -
    function_cache::Union{Nothing, MOI.AbstractScalarFunction}
88 +
    function_cache::Union{Nothing,MOI.AbstractScalarFunction}
96 89
    function ProductMode(
97 -
        eps::T=zero(Float64);
90 +
        eps::T = zero(Float64);
98 91
        with_slack::Bool = false,
99 -
        aggregation_group = nothing
100 -
    ) where T<:Float64 # Real
92 +
        aggregation_group = nothing,
93 +
    ) where {T<:Float64} # Real
101 94
        @assert aggregation_group === nothing || aggregation_group >= 1
102 95
        # nothing means individualized
103 96
        # positive integers point to their numbers
@@ -106,7 +99,7 @@
Loading
106 99
            with_slack,
107 100
            aggregation_group === nothing ? 0 : aggregation_group,
108 101
            nothing,
109 -
            )
102 +
        )
110 103
    end
111 104
end
112 105
@@ -121,17 +114,17 @@
Loading
121 114
122 115
struct ComplementBoundCache
123 116
    # internal usage
124 -
    upper::Dict{VI, BilevelVariableInfo}
125 -
    lower::Dict{VI, BilevelVariableInfo}
126 -
    ldual::Dict{CI, BilevelConstraintInfo}
117 +
    upper::Dict{VI,BilevelVariableInfo}
118 +
    lower::Dict{VI,BilevelVariableInfo}
119 +
    ldual::Dict{CI,BilevelConstraintInfo}
127 120
    # full map
128 -
    map::Dict{VI, BilevelVariableInfo}
121 +
    map::Dict{VI,BilevelVariableInfo}
129 122
    function ComplementBoundCache()
130 123
        return new(
131 -
            Dict{VI, BilevelVariableInfo}(),
132 -
            Dict{VI, BilevelVariableInfo}(),
133 -
            Dict{CI, BilevelConstraintInfo}(),
134 -
            Dict{VI, BilevelVariableInfo}(),
124 +
            Dict{VI,BilevelVariableInfo}(),
125 +
            Dict{VI,BilevelVariableInfo}(),
126 +
            Dict{CI,BilevelConstraintInfo}(),
127 +
            Dict{VI,BilevelVariableInfo}(),
135 128
        )
136 129
    end
137 130
end
@@ -141,30 +134,33 @@
Loading
141 134
    primal_big_M::Float64
142 135
    dual_big_M::Float64
143 136
    cache::ComplementBoundCache
144 -
    function FortunyAmatMcCarlMode(;with_slack = false,
145 -
        primal_big_M = Inf, dual_big_M = Inf)
137 +
    function FortunyAmatMcCarlMode(;
138 +
        with_slack = false,
139 +
        primal_big_M = Inf,
140 +
        dual_big_M = Inf,
141 +
    )
146 142
        return new{Float64}(
147 143
            with_slack,
148 144
            primal_big_M,
149 145
            dual_big_M,
150 -
            ComplementBoundCache()
146 +
            ComplementBoundCache(),
151 147
        )
152 148
    end
153 149
end
154 150
155 151
mutable struct MixedMode{T} <: AbstractBilevelSolverMode{T}
156 152
    default::AbstractBilevelSolverMode{T}
157 -
    constraint_mode_map_c::Dict{CI, AbstractBilevelSolverMode{T}}
158 -
    constraint_mode_map_v::Dict{VI, AbstractBilevelSolverMode{T}}
153 +
    constraint_mode_map_c::Dict{CI,AbstractBilevelSolverMode{T}}
154 +
    constraint_mode_map_v::Dict{VI,AbstractBilevelSolverMode{T}}
159 155
    cache::ComplementBoundCache
160 156
    function_cache::Dict{Int,MOI.AbstractScalarFunction}
161 -
    function MixedMode(;default = SOS1Mode())
157 +
    function MixedMode(; default = SOS1Mode())
162 158
        return new{Float64}(
163 159
            default,
164 -
            Dict{CI, AbstractBilevelSolverMode{Float64}}(),
165 -
            Dict{VI, AbstractBilevelSolverMode{Float64}}(),
160 +
            Dict{CI,AbstractBilevelSolverMode{Float64}}(),
161 +
            Dict{VI,AbstractBilevelSolverMode{Float64}}(),
166 162
            ComplementBoundCache(),
167 -
            Dict{Int,MOI.AbstractScalarFunction}()
163 +
            Dict{Int,MOI.AbstractScalarFunction}(),
168 164
        )
169 165
    end
170 166
end
@@ -201,32 +197,62 @@
Loading
201 197
    return nothing
202 198
end
203 199
204 -
function build_full_map!(mode,
205 -
    upper_idxmap, lower_idxmap, lower_dual_idxmap, lower_primal_dual_map)
200 +
function build_full_map!(
201 +
    mode,
202 +
    upper_idxmap,
203 +
    lower_idxmap,
204 +
    lower_dual_idxmap,
205 +
    lower_primal_dual_map,
206 +
)
206 207
    return nothing
207 208
end
208 -
function build_full_map!(mode::FortunyAmatMcCarlMode,
209 -
        upper_idxmap, lower_idxmap, lower_dual_idxmap, lower_primal_dual_map)
210 -
    _build_bound_map!(mode.cache,
211 -
        upper_idxmap, lower_idxmap, lower_dual_idxmap, lower_primal_dual_map)
209 +
function build_full_map!(
210 +
    mode::FortunyAmatMcCarlMode,
211 +
    upper_idxmap,
212 +
    lower_idxmap,
213 +
    lower_dual_idxmap,
214 +
    lower_primal_dual_map,
215 +
)
216 +
    _build_bound_map!(
217 +
        mode.cache,
218 +
        upper_idxmap,
219 +
        lower_idxmap,
220 +
        lower_dual_idxmap,
221 +
        lower_primal_dual_map,
222 +
    )
212 223
    return nothing
213 224
end
214 -
function build_full_map!(mode::MixedMode,
215 -
    upper_idxmap, lower_idxmap, lower_dual_idxmap, lower_primal_dual_map)
216 -
_build_bound_map!(mode.cache,
217 -
    upper_idxmap, lower_idxmap, lower_dual_idxmap, lower_primal_dual_map)
218 -
return nothing
225 +
function build_full_map!(
226 +
    mode::MixedMode,
227 +
    upper_idxmap,
228 +
    lower_idxmap,
229 +
    lower_dual_idxmap,
230 +
    lower_primal_dual_map,
231 +
)
232 +
    _build_bound_map!(
233 +
        mode.cache,
234 +
        upper_idxmap,
235 +
        lower_idxmap,
236 +
        lower_dual_idxmap,
237 +
        lower_primal_dual_map,
238 +
    )
239 +
    return nothing
219 240
end
220 -
function _build_bound_map!(mode::ComplementBoundCache,
221 -
    upper_idxmap, lower_idxmap, lower_dual_idxmap, lower_primal_dual_map)
241 +
function _build_bound_map!(
242 +
    mode::ComplementBoundCache,
243 +
    upper_idxmap,
244 +
    lower_idxmap,
245 +
    lower_dual_idxmap,
246 +
    lower_primal_dual_map,
247 +
)
222 248
    empty!(mode.map)
223 -
    for (k,v) in mode.upper
249 +
    for (k, v) in mode.upper
224 250
        mode.map[upper_idxmap[k]] = v
225 251
    end
226 -
    for (k,v) in mode.lower
252 +
    for (k, v) in mode.lower
227 253
        mode.map[lower_idxmap[k]] = v
228 254
    end
229 -
    for (k,v) in mode.ldual
255 +
    for (k, v) in mode.ldual
230 256
        vec = lower_primal_dual_map.primal_con_dual_var[k]#[1] # TODO check this scalar
231 257
        for var in vec
232 258
            mode.map[lower_dual_idxmap[var]] = BilevelVariableInfo(v)
@@ -238,22 +264,30 @@
Loading
238 264
mutable struct StrongDualityMode{T} <: AbstractBilevelSolverMode{T}
239 265
    inequality::Bool
240 266
    epsilon::T
241 -
    function StrongDualityMode(eps::T=zero(Float64); inequality = true) where T
267 +
    function StrongDualityMode(
268 +
        eps::T = zero(Float64);
269 +
        inequality = true,
270 +
    ) where {T}
242 271
        return new{T}(inequality, eps)
243 272
    end
244 273
end
245 274
246 -
ignore_dual_objective(::AbstractBilevelSolverMode{T}) where T = true
247 -
ignore_dual_objective(::StrongDualityMode{T}) where T = false
275 +
ignore_dual_objective(::AbstractBilevelSolverMode{T}) where {T} = true
276 +
ignore_dual_objective(::StrongDualityMode{T}) where {T} = false
248 277
249 -
function accept_vector_set(mode::AbstractBilevelSolverMode{T}, con::Complement) where T
278 +
function accept_vector_set(
279 +
    mode::AbstractBilevelSolverMode{T},
280 +
    con::Complement,
281 +
) where {T}
250 282
    if con.is_vec
251 -
        error("Set $(typeof(con.set_w_zero)) is not accepted when solution method is $(typeof(mode))")
283 +
        error(
284 +
            "Set $(typeof(con.set_w_zero)) is not accepted when solution method is $(typeof(mode))",
285 +
        )
252 286
    end
253 287
    return nothing
254 288
end
255 -
accept_vector_set(::ProductMode{T}, ::Complement) where T = nothing
256 -
accept_vector_set(::MixedMode{T}, ::Complement) where T = nothing
289 +
accept_vector_set(::ProductMode{T}, ::Complement) where {T} = nothing
290 +
accept_vector_set(::MixedMode{T}, ::Complement) where {T} = nothing
257 291
258 292
function get_canonical_complements(primal_model, primal_dual_map)
259 293
    map = primal_dual_map.primal_con_dual_var
@@ -264,8 +298,11 @@
Loading
264 298
    end
265 299
    return out
266 300
end
267 -
function get_canonical_complement(primal_model, map,
268 -
    ci::CI{F,S}) where {F, S<:VECTOR_SETS}
301 +
function get_canonical_complement(
302 +
    primal_model,
303 +
    map,
304 +
    ci::CI{F,S},
305 +
) where {F,S<:VECTOR_SETS}
269 306
    T = Float64
270 307
    func = MOI.copy(MOI.get(primal_model, MOI.ConstraintFunction(), ci))::F
271 308
    set = MOI.copy(MOI.get(primal_model, MOI.ConstraintSet(), ci))::S
@@ -279,12 +316,16 @@
Loading
279 316
    con = Complement(true, ci, func, set_with_zero(set), map[ci])
280 317
    return con
281 318
end
282 -
function get_canonical_complement(primal_model, map,
283 -
    ci::CI{F,S}) where {F, S<:SCALAR_SETS}
319 +
function get_canonical_complement(
320 +
    primal_model,
321 +
    map,
322 +
    ci::CI{F,S},
323 +
) where {F,S<:SCALAR_SETS}
284 324
    T = Float64
285 325
    func = MOI.copy(MOI.get(primal_model, MOI.ConstraintFunction(), ci))::F
286 326
    set = MOI.copy(MOI.get(primal_model, MOI.ConstraintSet(), ci))::S
287 -
    constant = Dualization.set_dot(1, set, T) *
327 +
    constant =
328 +
        Dualization.set_dot(1, set, T) *
288 329
        Dualization.get_scalar_term(primal_model, ci)[]
289 330
    if F == MOI.VariableIndex
290 331
        func = MOIU.operate(+, T, func, constant)
@@ -311,19 +352,23 @@
Loading
311 352
    mode,
312 353
    upper_var_to_lower_ctr::Dict{VI,CI} = Dict{VI,CI}();
313 354
    copy_names::Bool = false,
314 -
    pass_start::Bool = false
315 -
    )
355 +
    pass_start::Bool = false,
356 +
)
316 357
317 358
    # Start with an empty problem
318 359
    moi_mode = MOIU.AUTOMATIC
319 -
    m = MOIU.CachingOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}()), moi_mode)
360 +
    m = MOIU.CachingOptimizer(
361 +
        MOIU.UniversalFallback(MOIU.Model{Float64}()),
362 +
        moi_mode,
363 +
    )
320 364
321 365
    #=
322 366
        Create Lower DUAL level model
323 367
    =#
324 368
    # dualize the second level
325 -
    dual_problem = Dualization.dualize(lower,
326 -
        dual_names = DualNames("dual_","dual_"),
369 +
    dual_problem = Dualization.dualize(
370 +
        lower;
371 +
        dual_names = DualNames("dual_", "dual_"),
327 372
        variable_parameters = upper_variables,
328 373
        ignore_objective = ignore_dual_objective(mode),
329 374
        consider_constrained_variables = false,
@@ -354,7 +399,8 @@
Loading
354 399
        # get primal obj
355 400
        type_primal_obj = MOI.get(lower, MOI.ObjectiveFunctionType())
356 401
        @assert type_primal_obj !== nothing
357 -
        lower_primal_obj = MOI.get(lower, MOI.ObjectiveFunction{type_primal_obj}())
402 +
        lower_primal_obj =
403 +
            MOI.get(lower, MOI.ObjectiveFunction{type_primal_obj}())
358 404
        # deepcopy and delete dual obj
359 405
        # MOI.set(lower, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction(MOI.ScalarAffineTerm{Float64}[], 0.0))
360 406
    end
@@ -366,7 +412,7 @@
Loading
366 412
    end
367 413
368 414
    # append the second level primal
369 -
    append_to(m, lower, lower_idxmap, allow_single_bounds = true)
415 +
    append_to(m, lower, lower_idxmap; allow_single_bounds = true)
370 416
    if copy_names
371 417
        pass_names(m, lower, lower_idxmap)
372 418
    end
@@ -380,7 +426,8 @@
Loading
380 426
        # get dual obj
381 427
        tp_dual_obj = MOI.get(lower_dual, MOI.ObjectiveFunctionType())
382 428
        @assert tp_dual_obj !== nothing
383 -
        lower_dual_obj = MOI.get(lower_dual, MOI.ObjectiveFunction{tp_dual_obj}())
429 +
        lower_dual_obj =
430 +
            MOI.get(lower_dual, MOI.ObjectiveFunction{tp_dual_obj}())
384 431
        # delete dual obj
385 432
        # MOI.set(lower_dual, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction(MOI.ScalarAffineTerm{Float64}[], 0.0))
386 433
    end
@@ -389,12 +436,16 @@
Loading
389 436
    lower_dual_idxmap = MOIU.IndexMap()
390 437
    # 1) for QP's there are dual variable that are linked to:
391 438
    # 1.1) primal variables
392 -
    for (lower_primal_var_key, lower_dual_quad_slack_val) in lower_primal_dual_map.primal_var_dual_quad_slack
393 -
        lower_dual_idxmap[lower_dual_quad_slack_val] = lower_idxmap[lower_primal_var_key]
439 +
    for (lower_primal_var_key, lower_dual_quad_slack_val) in
440 +
        lower_primal_dual_map.primal_var_dual_quad_slack
441 +
        lower_dual_idxmap[lower_dual_quad_slack_val] =
442 +
            lower_idxmap[lower_primal_var_key]
394 443
    end
395 444
    # 1.2) and to upper level variable which are lower level parameters
396 -
    for (lower_primal_param_key, lower_dual_param_val) in lower_primal_dual_map.primal_parameter
397 -
        lower_dual_idxmap[lower_dual_param_val] = lower_idxmap[lower_primal_param_key]
445 +
    for (lower_primal_param_key, lower_dual_param_val) in
446 +
        lower_primal_dual_map.primal_parameter
447 +
        lower_dual_idxmap[lower_dual_param_val] =
448 +
            lower_idxmap[lower_primal_param_key]
398 449
    end
399 450
    # 2) Dual variables might appear in the upper level
400 451
    for (upper_var, lower_con) in upper_var_to_lower_ctr
@@ -413,8 +464,13 @@
Loading
413 464
    =#
414 465
415 466
    # build map bound map for FortunyAmatMcCarlMode
416 -
    build_full_map!(mode,
417 -
        upper_idxmap, lower_idxmap, lower_dual_idxmap, lower_primal_dual_map)
467 +
    build_full_map!(
468 +
        mode,
469 +
        upper_idxmap,
470 +
        lower_idxmap,
471 +
        lower_dual_idxmap,
472 +
        lower_primal_dual_map,
473 +
    )
418 474
419 475
    if ignore_dual_objective(mode)
420 476
        # complementary slackness
@@ -422,25 +478,48 @@
Loading
422 478
        for comp in comps
423 479
            if !is_equality(comp.set_w_zero)
424 480
                accept_vector_set(mode, comp)
425 -
                add_complement(mode, m, comp,
426 -
                    lower_idxmap, lower_dual_idxmap, copy_names, pass_start)
481 +
                add_complement(
482 +
                    mode,
483 +
                    m,
484 +
                    comp,
485 +
                    lower_idxmap,
486 +
                    lower_dual_idxmap,
487 +
                    copy_names,
488 +
                    pass_start,
489 +
                )
427 490
            else
428 491
                # feasible equality constraints always satisfy complementarity
429 492
            end
430 493
        end
431 494
    else # strong duality
432 -
        add_strong_duality(mode, m, lower_primal_obj, lower_dual_obj, lower_idxmap, lower_dual_idxmap)
495 +
        add_strong_duality(
496 +
            mode,
497 +
            m,
498 +
            lower_primal_obj,
499 +
            lower_dual_obj,
500 +
            lower_idxmap,
501 +
            lower_dual_idxmap,
502 +
        )
433 503
    end
434 504
    add_aggregate_constraints(m, mode, copy_names)
435 505
436 -
    return m, upper_idxmap, lower_idxmap, lower_primal_dual_map, lower_dual_idxmap
506 +
    return m,
507 +
    upper_idxmap,
508 +
    lower_idxmap,
509 +
    lower_primal_dual_map,
510 +
    lower_dual_idxmap
437 511
end
438 512
439 -
function add_strong_duality(mode::StrongDualityMode{T}, m, primal_obj, dual_obj,
440 -
    idxmap_primal, idxmap_dual) where T
441 -
513 +
function add_strong_duality(
514 +
    mode::StrongDualityMode{T},
515 +
    m,
516 +
    primal_obj,
517 +
    dual_obj,
518 +
    idxmap_primal,
519 +
    idxmap_dual,
520 +
) where {T}
442 521
    primal = MOIU.map_indices.(Ref(idxmap_primal), primal_obj)
443 -
    dual   = MOIU.map_indices.(Ref(idxmap_dual), dual_obj)
522 +
    dual = MOIU.map_indices.(Ref(idxmap_dual), dual_obj)
444 523
445 524
    func = MOIU.operate(-, T, primal, dual)
446 525
@@ -450,11 +529,16 @@
Loading
450 529
        return CI[c]
451 530
    else
452 531
        func_up = MOIU.operate(-, T, func, mode.epsilon)
453 -
        c_up = MOIU.normalize_and_add_constraint(m, func_up, MOI.LessThan(zero(T)))
532 +
        c_up =
533 +
            MOIU.normalize_and_add_constraint(m, func_up, MOI.LessThan(zero(T)))
454 534
        MOI.set(m, MOI.ConstraintName(), c_up, "lower_strong_duality_up")
455 535
456 536
        func_lo = MOIU.operate(+, T, func, mode.epsilon)
457 -
        c_lo = MOIU.normalize_and_add_constraint(m, func_lo, MOI.GreaterThan(zero(T)))
537 +
        c_lo = MOIU.normalize_and_add_constraint(
538 +
            m,
539 +
            func_lo,
540 +
            MOI.GreaterThan(zero(T)),
541 +
        )
458 542
        MOI.set(m, MOI.ConstraintName(), c_lo, "lower_strong_duality_lo")
459 543
460 544
        return CI[c_up, c_lo]
@@ -464,18 +548,22 @@
Loading
464 548
add_aggregate_constraints(m, mode, copy_names) = nothing
465 549
466 550
function _add_aggregate_constraints(
467 -
    m, func::F, eps, idx, copy_names
468 -
) where F<:MOI.ScalarQuadraticFunction{T} where T
551 +
    m,
552 +
    func::F,
553 +
    eps,
554 +
    idx,
555 +
    copy_names,
556 +
) where {F<:MOI.ScalarQuadraticFunction{T}} where {T}
469 557
    prod_f1 = MOIU.operate(-, T, func, eps)
470 -
    c1 = MOIU.normalize_and_add_constraint(m,
471 -
        prod_f1,
472 -
        MOI.LessThan{T}(0.0))
558 +
    c1 = MOIU.normalize_and_add_constraint(m, prod_f1, MOI.LessThan{T}(0.0))
473 559
    # appush!(out_ctr, c1)
474 560
    if true # comp.is_vec
475 561
        prod_f2 = MOIU.operate(+, T, func, eps)
476 -
        c2 = MOIU.normalize_and_add_constraint(m,
562 +
        c2 = MOIU.normalize_and_add_constraint(
563 +
            m,
477 564
            prod_f2,
478 -
            MOI.GreaterThan{T}(0.0))
565 +
            MOI.GreaterThan{T}(0.0),
566 +
        )
479 567
        # appush!(out_ctr, c2)
480 568
    end
481 569
    if copy_names
@@ -491,18 +579,23 @@
Loading
491 579
        return nothing
492 580
    end
493 581
    _add_aggregate_constraints(
494 -
        m, mode.function_cache, mode.epsilon, 0, copy_names)
582 +
        m,
583 +
        mode.function_cache,
584 +
        mode.epsilon,
585 +
        0,
586 +
        copy_names,
587 +
    )
495 588
    return nothing
496 589
end
497 -
function add_aggregate_constraints(m, mode::MixedMode{T}, copy_names) where T
590 +
function add_aggregate_constraints(m, mode::MixedMode{T}, copy_names) where {T}
498 591
    if isempty(mode.function_cache)
499 592
        return nothing
500 593
    end
501 -
    eps = Dict{Int, T}()
594 +
    eps = Dict{Int,T}()
502 595
    for list in (
503 596
        mode.constraint_mode_map_c,
504 597
        mode.constraint_mode_map_v,
505 -
        Dict(nothing=>mode.default)
598 +
        Dict(nothing => mode.default),
506 599
    )
507 600
        for md in values(list)
508 601
            val, idx = _get_eps_idx(md)
@@ -514,13 +607,12 @@
Loading
514 607
        end
515 608
    end
516 609
    for (idx, func) in mode.function_cache
517 -
        _add_aggregate_constraints(
518 -
            m, func, eps[idx], idx, copy_names)
610 +
        _add_aggregate_constraints(m, func, eps[idx], idx, copy_names)
519 611
    end
520 612
    return nothing
521 613
end
522 614
523 -
function _get_eps_idx(mode::ProductMode{T}) where T
615 +
function _get_eps_idx(mode::ProductMode{T}) where {T}
524 616
    idx = mode.aggregation_group
525 617
    eps = mode.epsilon
526 618
    return eps, idx
@@ -529,22 +621,43 @@
Loading
529 621
    return 0.0, 0
530 622
end
531 623
532 -
function add_complement(mode::MixedMode{T}, m, comp::Complement,
533 -
        idxmap_primal, idxmap_dual, copy_names::Bool, pass_start::Bool) where T
624 +
function add_complement(
625 +
    mode::MixedMode{T},
626 +
    m,
627 +
    comp::Complement,
628 +
    idxmap_primal,
629 +
    idxmap_dual,
630 +
    copy_names::Bool,
631 +
    pass_start::Bool,
632 +
) where {T}
534 633
    _mode = get_mode(mode, comp.constraint, idxmap_primal)
535 634
    accept_vector_set(_mode, comp)
536 -
    ret = add_complement(_mode, m, comp,
537 -
        idxmap_primal, idxmap_dual, copy_names, pass_start)
635 +
    ret = add_complement(
636 +
        _mode,
637 +
        m,
638 +
        comp,
639 +
        idxmap_primal,
640 +
        idxmap_dual,
641 +
        copy_names,
642 +
        pass_start,
643 +
    )
538 644
    add_function_to_cache(mode, _mode)
539 645
    return ret
540 646
end
541 -
add_function_to_cache(mode::MixedMode{T}, _mode) where T = nothing
542 -
function add_function_to_cache(mode::MixedMode{T}, _mode::ProductMode{T}) where T
647 +
add_function_to_cache(mode::MixedMode{T}, _mode) where {T} = nothing
648 +
function add_function_to_cache(
649 +
    mode::MixedMode{T},
650 +
    _mode::ProductMode{T},
651 +
) where {T}
543 652
    idx = _mode.aggregation_group
544 653
    if _mode.function_cache !== nothing && idx > 0
545 654
        if haskey(mode.function_cache, idx)
546 -
            mode.function_cache[idx] = MOIU.operate(+, T,
547 -
                mode.function_cache[idx], _mode.function_cache)
655 +
            mode.function_cache[idx] = MOIU.operate(
656 +
                +,
657 +
                T,
658 +
                mode.function_cache[idx],
659 +
                _mode.function_cache,
660 +
            )
548 661
        else
549 662
            mode.function_cache[idx] = _mode.function_cache
550 663
        end
@@ -553,10 +666,14 @@
Loading
553 666
    return nothing
554 667
end
555 668
556 -
function get_mode(mode::MixedMode{T}, ci::CI{F,S}, map) where {
669 +
function get_mode(
670 +
    mode::MixedMode{T},
671 +
    ci::CI{F,S},
672 +
    map,
673 +
) where {
557 674
    T,
558 675
    F<:MOI.VariableIndex,
559 -
    S<:Union{MOI.EqualTo{T}, MOI.LessThan{T}, MOI.GreaterThan{T}}
676 +
    S<:Union{MOI.EqualTo{T},MOI.LessThan{T},MOI.GreaterThan{T}},
560 677
}
561 678
    # key = map[VI(ci.value)]
562 679
    key = VI(ci.value)
@@ -575,8 +692,15 @@
Loading
575 692
    end
576 693
end
577 694
578 -
function add_complement(mode::ComplementMode{T}, m, comp::Complement,
579 -
    idxmap_primal, idxmap_dual, copy_names, pass_start::Bool) where T
695 +
function add_complement(
696 +
    mode::ComplementMode{T},
697 +
    m,
698 +
    comp::Complement,
699 +
    idxmap_primal,
700 +
    idxmap_dual,
701 +
    copy_names,
702 +
    pass_start::Bool,
703 +
) where {T}
580 704
    f = comp.func_w_cte
581 705
    s = comp.set_w_zero
582 706
    v = comp.variable
@@ -592,19 +716,24 @@
Loading
592 716
    if with_slack
593 717
        slack, slack_in_set = MOI.add_constrained_variable(m, s)
594 718
        new_f = MOIU.operate(-, T, f_dest, slack)
595 -
        equality = MOIU.normalize_and_add_constraint(m, new_f, MOI.EqualTo(zero(T)))
719 +
        equality =
720 +
            MOIU.normalize_and_add_constraint(m, new_f, MOI.EqualTo(zero(T)))
596 721
597 722
        if pass_start
598 723
            val = MOIU.eval_variables(
599 -
                x-> nothing_to_nan(MOI.get(m, MOI.VariablePrimalStart(), x)), f_dest)
724 +
                x -> nothing_to_nan(MOI.get(m, MOI.VariablePrimalStart(), x)),
725 +
                f_dest,
726 +
            )
600 727
            if !isnan(val)
601 728
                MOI.set(m, MOI.VariablePrimalStart(), slack, val)
602 729
            end
603 730
        end
604 731
605 -
        c = MOI.add_constraint(m, 
732 +
        c = MOI.add_constraint(
733 +
            m,
606 734
            MOI.VectorOfVariables([slack, dual]),
607 -
            MOI.Complements(1))
735 +
            MOI.Complements(1),
736 +
        )
608 737
        if copy_names
609 738
            nm = MOI.get(m, MOI.VariableName(), dual)
610 739
            MOI.set(m, MOI.VariableName(), slack, "slk_($(nm))")
@@ -620,9 +749,7 @@
Loading
620 749
    else
621 750
        new_f = MOIU.operate(vcat, T, f_dest, dual)
622 751
623 -
        c = MOI.add_constraint(m, 
624 -
            new_f,
625 -
            MOI.Complements(1))
752 +
        c = MOI.add_constraint(m, new_f, MOI.Complements(1))
626 753
627 754
        if copy_names
628 755
            nm = MOI.get(m, MOI.VariableName(), dual)
@@ -635,8 +762,15 @@
Loading
635 762
    return out_var, out_ctr
636 763
end
637 764
638 -
function add_complement(mode::SOS1Mode{T}, m, comp::Complement,
639 -
    idxmap_primal, idxmap_dual, copy_names::Bool, pass_start::Bool) where T
765 +
function add_complement(
766 +
    mode::SOS1Mode{T},
767 +
    m,
768 +
    comp::Complement,
769 +
    idxmap_primal,
770 +
    idxmap_dual,
771 +
    copy_names::Bool,
772 +
    pass_start::Bool,
773 +
) where {T}
640 774
    f = comp.func_w_cte
641 775
    s = comp.set_w_zero
642 776
    v = comp.variable
@@ -651,9 +785,11 @@
Loading
651 785
    equality = MOIU.normalize_and_add_constraint(m, new_f, MOI.EqualTo(zero(T)))
652 786
653 787
    dual = idxmap_dual[v]
654 -
    c1 = MOI.add_constraint(m, 
788 +
    c1 = MOI.add_constraint(
789 +
        m,
655 790
        MOI.VectorOfVariables([slack, dual]),
656 -
        MOI.SOS1([1.0, 2.0]))
791 +
        MOI.SOS1([1.0, 2.0]),
792 +
    )
657 793
658 794
    if copy_names
659 795
        nm = MOI.get(m, MOI.VariableName(), dual)
@@ -667,7 +803,7 @@
Loading
667 803
end
668 804
669 805
is_equality(set::S) where {S<:MOI.AbstractSet} = false
670 -
is_equality(set::MOI.EqualTo{T}) where T = true
806 +
is_equality(set::MOI.EqualTo{T}) where {T} = true
671 807
is_equality(set::MOI.Zeros) = true
672 808
673 809
only_variable_functions(v::MOI.VariableIndex) = v
@@ -684,8 +820,15 @@
Loading
684 820
    end
685 821
    return nothing
686 822
end
687 -
function add_complement(mode::ProductMode{T}, m, comp::Complement,
688 -
    idxmap_primal, idxmap_dual, copy_names::Bool, pass_start::Bool) where T
823 +
function add_complement(
824 +
    mode::ProductMode{T},
825 +
    m,
826 +
    comp::Complement,
827 +
    idxmap_primal,
828 +
    idxmap_dual,
829 +
    copy_names::Bool,
830 +
    pass_start::Bool,
831 +
) where {T}
689 832
    f = comp.func_w_cte
690 833
    s = comp.set_w_zero
691 834
    v = comp.variable
@@ -696,9 +839,9 @@
Loading
696 839
    eps = mode.epsilon
697 840
    with_slack = mode.with_slack
698 841
699 -
    f_dest = MOIU.map_indices(x->idxmap_primal[x], f)
842 +
    f_dest = MOIU.map_indices(x -> idxmap_primal[x], f)
700 843
701 -
    dual = comp.is_vec ? map(x->idxmap_dual[x], v) : idxmap_dual[v]
844 +
    dual = comp.is_vec ? map(x -> idxmap_dual[x], v) : idxmap_dual[v]
702 845
703 846
    if with_slack
704 847
        slack, slack_in_set = if comp.is_vec
@@ -708,12 +851,25 @@
Loading
708 851
        end
709 852
        new_f = MOIU.operate(-, T, f_dest, only_variable_functions(slack))
710 853
        if comp.is_vec
711 -
            equality = MOIU.normalize_and_add_constraint(m, new_f, MOI.Zeros(length(slack)))
854 +
            equality = MOIU.normalize_and_add_constraint(
855 +
                m,
856 +
                new_f,
857 +
                MOI.Zeros(length(slack)),
858 +
            )
712 859
        else
713 -
            equality = MOIU.normalize_and_add_constraint(m, new_f, MOI.EqualTo(zero(T)))
860 +
            equality = MOIU.normalize_and_add_constraint(
861 +
                m,
862 +
                new_f,
863 +
                MOI.EqualTo(zero(T)),
864 +
            )
714 865
        end
715 866
716 -
        prod_f = MOIU.operate(dot, T, only_variable_functions(slack), only_variable_functions(dual))
867 +
        prod_f = MOIU.operate(
868 +
            dot,
869 +
            T,
870 +
            only_variable_functions(slack),
871 +
            only_variable_functions(dual),
872 +
        )
717 873
718 874
        appush!(out_var, slack)
719 875
        appush!(out_ctr, slack_in_set)
@@ -721,15 +877,19 @@
Loading
721 877
722 878
        if mode.aggregation_group == 0
723 879
            prod_f1 = MOIU.operate(-, T, prod_f, eps)
724 -
            c1 = MOIU.normalize_and_add_constraint(m,
880 +
            c1 = MOIU.normalize_and_add_constraint(
881 +
                m,
725 882
                prod_f1,
726 -
                MOI.LessThan{Float64}(0.0))
883 +
                MOI.LessThan{Float64}(0.0),
884 +
            )
727 885
            appush!(out_ctr, c1)
728 886
            if comp.is_vec
729 887
                prod_f2 = MOIU.operate(+, T, prod_f, eps)
730 -
                c2 = MOIU.normalize_and_add_constraint(m,
888 +
                c2 = MOIU.normalize_and_add_constraint(
889 +
                    m,
731 890
                    prod_f2,
732 -
                    MOI.GreaterThan{Float64}(0.0))
891 +
                    MOI.GreaterThan{Float64}(0.0),
892 +
                )
733 893
                appush!(out_ctr, c2)
734 894
            end
735 895
        else
@@ -738,7 +898,9 @@
Loading
738 898
739 899
        if pass_start
740 900
            val = MOIU.eval_variables(
741 -
                x-> nothing_to_nan(MOI.get(m, MOI.VariablePrimalStart(), x)), f_dest)
901 +
                x -> nothing_to_nan(MOI.get(m, MOI.VariablePrimalStart(), x)),
902 +
                f_dest,
903 +
            )
742 904
            if comp.is_vec
743 905
                for i in eachindex(val)
744 906
                    if !isnan(val[i])
@@ -760,7 +922,12 @@
Loading
760 922
            if mode.aggregation_group == 0
761 923
                MOI.set(m, MOI.ConstraintName(), c1, "compl_prodWslk_($(nm))")
762 924
                if comp.is_vec
763 -
                    MOI.set(m, MOI.ConstraintName(), c2, "compl_prodWslk2_($(nm))")
925 +
                    MOI.set(
926 +
                        m,
927 +
                        MOI.ConstraintName(),
928 +
                        c2,
929 +
                        "compl_prodWslk2_($(nm))",
930 +
                    )
764 931
                end
765 932
            end
766 933
        end
@@ -768,15 +935,19 @@
Loading
768 935
        new_f = MOIU.operate(dot, T, f_dest, only_variable_functions(dual))
769 936
        if mode.aggregation_group == 0
770 937
            new_f1 = MOIU.operate(-, T, new_f, eps)
771 -
            c1 = MOIU.normalize_and_add_constraint(m, 
938 +
            c1 = MOIU.normalize_and_add_constraint(
939 +
                m,
772 940
                new_f1,
773 -
                MOI.LessThan{T}(0.0))
941 +
                MOI.LessThan{T}(0.0),
942 +
            )
774 943
            appush!(out_ctr, c1)
775 944
            if comp.is_vec # conic
776 945
                new_f2 = MOIU.operate(+, T, new_f, eps)
777 -
                c2 = MOIU.normalize_and_add_constraint(m, 
946 +
                c2 = MOIU.normalize_and_add_constraint(
947 +
                    m,
778 948
                    new_f2,
779 -
                    MOI.GreaterThan{T}(0.0))
949 +
                    MOI.GreaterThan{T}(0.0),
950 +
                )
780 951
                appush!(out_ctr, c2)
781 952
            end
782 953
            if copy_names
@@ -797,8 +968,15 @@
Loading
797 968
    return out_var, out_ctr
798 969
end
799 970
800 -
function add_complement(mode::IndicatorMode{T}, m, comp::Complement,
801 -
    idxmap_primal, idxmap_dual, copy_names::Bool, pass_start::Bool) where T
971 +
function add_complement(
972 +
    mode::IndicatorMode{T},
973 +
    m,
974 +
    comp::Complement,
975 +
    idxmap_primal,
976 +
    idxmap_dual,
977 +
    copy_names::Bool,
978 +
    pass_start::Bool,
979 +
) where {T}
802 980
    f = comp.func_w_cte
803 981
    s = comp.set_w_zero
804 982
    v = comp.variable
@@ -808,12 +986,14 @@
Loading
808 986
    is_tight = false
809 987
    has_start = false
810 988
811 -
    f_dest = MOIU.map_indices(x->idxmap_primal[x], f)
989 +
    f_dest = MOIU.map_indices(x -> idxmap_primal[x], f)
812 990
813 991
    dual = idxmap_dual[v]
814 992
815 993
    if comp.is_vec
816 -
        error("Vector constraint is (currently) not supported by indicator mode")
994 +
        error(
995 +
            "Vector constraint is (currently) not supported by indicator mode",
996 +
        )
817 997
    end
818 998
819 999
    if copy_names
@@ -854,7 +1034,9 @@
Loading
854 1034
855 1035
    if pass_start
856 1036
        val = MOIU.eval_variables(
857 -
            x-> nothing_to_nan(MOI.get(m, MOI.VariablePrimalStart(), x)), f_dest)
1037 +
            x -> nothing_to_nan(MOI.get(m, MOI.VariablePrimalStart(), x)),
1038 +
            f_dest,
1039 +
        )
858 1040
        if !isnan(val)
859 1041
            is_tight = abs(val) < 1e-8
860 1042
            has_start = true
@@ -865,21 +1047,46 @@
Loading
865 1047
        s1 = MOI.Indicator{MOI.ACTIVATE_ON_ONE}(pre_s1)
866 1048
        s2 = MOI.Indicator{MOI.ACTIVATE_ON_ONE}(MOI.EqualTo(zero(T)))
867 1049
        if pass_start && has_start
868 -
            MOI.set(m, MOI.VariablePrimalStart(), vb1, ifelse(is_tight, 1.0, 0.0))
869 -
            MOI.set(m, MOI.VariablePrimalStart(), vb2, ifelse(is_tight, 0.0, 1.0))
1050 +
            MOI.set(
1051 +
                m,
1052 +
                MOI.VariablePrimalStart(),
1053 +
                vb1,
1054 +
                ifelse(is_tight, 1.0, 0.0),
1055 +
            )
1056 +
            MOI.set(
1057 +
                m,
1058 +
                MOI.VariablePrimalStart(),
1059 +
                vb2,
1060 +
                ifelse(is_tight, 0.0, 1.0),
1061 +
            )
870 1062
        end
871 1063
    elseif method == ZERO_ZERO
872 1064
        s1 = MOI.Indicator{MOI.ACTIVATE_ON_ZERO}(pre_s1)
873 1065
        s2 = MOI.Indicator{MOI.ACTIVATE_ON_ZERO}(MOI.EqualTo(zero(T)))
874 1066
        if pass_start && has_start
875 -
            MOI.set(m, MOI.VariablePrimalStart(), vb1, ifelse(is_tight, 0.0, 1.0))
876 -
            MOI.set(m, MOI.VariablePrimalStart(), vb2, ifelse(is_tight, 1.0, 0.0))
1067 +
            MOI.set(
1068 +
                m,
1069 +
                MOI.VariablePrimalStart(),
1070 +
                vb1,
1071 +
                ifelse(is_tight, 0.0, 1.0),
1072 +
            )
1073 +
            MOI.set(
1074 +
                m,
1075 +
                MOI.VariablePrimalStart(),
1076 +
                vb2,
1077 +
                ifelse(is_tight, 1.0, 0.0),
1078 +
            )
877 1079
        end
878 1080
    else
879 1081
        s1 = MOI.Indicator{MOI.ACTIVATE_ON_ONE}(pre_s1)
880 1082
        s2 = MOI.Indicator{MOI.ACTIVATE_ON_ZERO}(MOI.EqualTo(zero(T)))
881 1083
        if pass_start && has_start
882 -
            MOI.set(m, MOI.VariablePrimalStart(), vb1, ifelse(is_tight, 1.0, 0.0))
1084 +
            MOI.set(
1085 +
                m,
1086 +
                MOI.VariablePrimalStart(),
1087 +
                vb1,
1088 +
                ifelse(is_tight, 1.0, 0.0),
1089 +
            )
883 1090
        end
884 1091
    end
885 1092
@@ -894,10 +1101,10 @@
Loading
894 1101
    return c1
895 1102
end
896 1103
897 -
function flip_set(set::MOI.LessThan{T}) where T
1104 +
function flip_set(set::MOI.LessThan{T}) where {T}
898 1105
    return MOI.GreaterThan{T}(0.0)
899 1106
end
900 -
function flip_set(set::MOI.GreaterThan{T}) where T
1107 +
function flip_set(set::MOI.GreaterThan{T}) where {T}
901 1108
    return MOI.LessThan{T}(0.0)
902 1109
end
903 1110
@@ -915,16 +1122,22 @@
Loading
915 1122
    end
916 1123
end
917 1124
918 -
function set_bound(inter::Interval, ::LT{T}) where T
1125 +
function set_bound(inter::Interval, ::LT{T}) where {T}
919 1126
    return inter.hi
920 1127
end
921 -
function set_bound(inter::Interval, ::GT{T}) where T
1128 +
function set_bound(inter::Interval, ::GT{T}) where {T}
922 1129
    return inter.lo
923 1130
end
924 1131
925 -
function add_complement(mode::FortunyAmatMcCarlMode{T}, m, comp::Complement,
926 -
    idxmap_primal, idxmap_dual, copy_names::Bool, pass_start::Bool) where T
927 -
1132 +
function add_complement(
1133 +
    mode::FortunyAmatMcCarlMode{T},
1134 +
    m,
1135 +
    comp::Complement,
1136 +
    idxmap_primal,
1137 +
    idxmap_dual,
1138 +
    copy_names::Bool,
1139 +
    pass_start::Bool,
1140 +
) where {T}
928 1141
    f = comp.func_w_cte
929 1142
    s = comp.set_w_zero
930 1143
    v = comp.variable
@@ -937,11 +1150,16 @@
Loading
937 1150
    end
938 1151
    f_dest = MOIU.map_indices.(Ref(idxmap_primal), f)
939 1152
940 -
    f_bounds = MOIU.eval_variables(vi -> get_bounds(vi, mode.cache.map, mode.primal_big_M), f_dest)
1153 +
    f_bounds = MOIU.eval_variables(
1154 +
        vi -> get_bounds(vi, mode.cache.map, mode.primal_big_M),
1155 +
        f_dest,
1156 +
    )
941 1157
942 1158
    if pass_start
943 1159
        val = MOIU.eval_variables(
944 -
            x-> nothing_to_nan(MOI.get(m, MOI.VariablePrimalStart(), x)), f_dest)
1160 +
            x -> nothing_to_nan(MOI.get(m, MOI.VariablePrimalStart(), x)),
1161 +
            f_dest,
1162 +
        )
945 1163
        if !isnan(val)
946 1164
            is_tight = abs(val) < 1e-8
947 1165
            has_start = true
@@ -950,7 +1168,8 @@
Loading
950 1168
951 1169
    if mode.with_slack
952 1170
        new_f = MOIU.operate(-, T, f_dest, slack)
953 -
        equality = MOIU.normalize_and_add_constraint(m, new_f, MOI.EqualTo(zero(T)))
1171 +
        equality =
1172 +
            MOIU.normalize_and_add_constraint(m, new_f, MOI.EqualTo(zero(T)))
954 1173
        if pass_start && has_start
955 1174
            MOI.set(m, MOI.VariablePrimalStart(), slack, val)
956 1175
        end
@@ -974,17 +1193,17 @@
Loading
974 1193
    Mv = set_bound(v_bounds, s2)
975 1194
976 1195
    if isnan(Ms) || abs(Ms) >= Inf || isnan(Mv) || abs(Mv) >= Inf
977 -
        error("It was not possible to automatically compute bounds"*
978 -
            " for a complementarity pair, please add the arguments"*
979 -
            " primal_big_M and dual_big_M to FortunyAmatMcCarlMode")
1196 +
        error(
1197 +
            "It was not possible to automatically compute bounds" *
1198 +
            " for a complementarity pair, please add the arguments" *
1199 +
            " primal_big_M and dual_big_M to FortunyAmatMcCarlMode",
1200 +
        )
980 1201
    end
981 -
    
1202 +
982 1203
    if mode.with_slack
983 1204
        f1 = MOI.ScalarAffineFunction{T}(
984 -
            MOI.ScalarAffineTerm{T}.(
985 -
                [one(T), -Ms], [slack, bin]
986 -
            ),
987 -
            0.0
1205 +
            MOI.ScalarAffineTerm{T}.([one(T), -Ms], [slack, bin]),
1206 +
            0.0,
988 1207
        )
989 1208
    else
990 1209
        push!(f_dest.terms, MOI.ScalarAffineTerm{T}(-Ms, bin))
@@ -992,10 +1211,8 @@
Loading
992 1211
    end
993 1212
994 1213
    f2 = MOI.ScalarAffineFunction{T}(
995 -
        MOI.ScalarAffineTerm{T}.(
996 -
            [one(T), Mv], [dual, bin]
997 -
        ),
998 -
        -Mv
1214 +
        MOI.ScalarAffineTerm{T}.([one(T), Mv], [dual, bin]),
1215 +
        -Mv,
999 1216
    )
1000 1217
1001 1218
    c1 = MOIU.normalize_and_add_constraint(m, f1, s2)
@@ -1022,7 +1239,7 @@
Loading
1022 1239
    return c1
1023 1240
end
1024 1241
1025 -
function to_vector_affine(f::MOI.VectorAffineFunction{T}) where T
1242 +
function to_vector_affine(f::MOI.VectorAffineFunction{T}) where {T}
1026 1243
    return f
1027 1244
end
1028 1245
function to_vector_affine(f::MOI.VectorOfVariables)
@@ -1032,9 +1249,13 @@
Loading
1032 1249
function handle_lower_objective_sense(lower::MOI.ModelLike)
1033 1250
    lower_objective_sense = MOI.get(lower, MOI.ObjectiveSense())
1034 1251
    if lower_objective_sense == MOI.FEASIBILITY_SENSE
1035 -
        throw(ErrorException("Lower level models with objective_sense: " * 
1036 -
                            lower_objective_sense * 
1037 -
                            " are not supported."))
1252 +
        throw(
1253 +
            ErrorException(
1254 +
                "Lower level models with objective_sense: " *
1255 +
                lower_objective_sense *
1256 +
                " are not supported.",
1257 +
            ),
1258 +
        )
1038 1259
    end
1039 1260
    return
1040 -
end

@@ -8,7 +8,13 @@
Loading
8 8
    lower_only = Dict(
9 9
        JuMP.index(k) => JuMP.index(v) for (k, v) in model.lower_to_upper_link
10 10
    )
11 -
    return _build_single_model(upper, lower, lower_to_upper, lower_only, check_MIPMIP)
11 +
    return _build_single_model(
12 +
        upper,
13 +
        lower,
14 +
        lower_to_upper,
15 +
        lower_only,
16 +
        check_MIPMIP,
17 +
    )
12 18
end
13 19
14 20
function _build_single_model(
@@ -16,7 +22,7 @@
Loading
16 22
    lower::MOI.ModelLike,
17 23
    lower_to_upper_link::Dict{MOI.VariableIndex,MOI.VariableIndex},
18 24
    lower_only::Dict{MOI.VariableIndex,MOI.VariableIndex},
19 -
    check_MIPMIP::Bool = false
25 +
    check_MIPMIP::Bool = false,
20 26
)
21 27
    model = MOI.FileFormats.MPS.Model()
22 28
    upper_to_model_link = MOI.copy_to(model, upper)
@@ -43,29 +49,41 @@
Loading
43 49
        return upper_to_model_link[lower_to_upper_link[x]]
44 50
    end
45 51
46 -
    
47 52
    # Testing if the model is MIP-MIP or not. 
48 53
    if check_MIPMIP
49 -
        int_var = MOI.get(model, MOI.NumberOfConstraints{MOI.VariableIndex, MOI.Integer}())
50 -
        int_var = int_var + MOI.get(model, MOI.NumberOfConstraints{MOI.VariableIndex, MOI.ZeroOne}())
54 +
        int_var = MOI.get(
55 +
            model,
56 +
            MOI.NumberOfConstraints{MOI.VariableIndex,MOI.Integer}(),
57 +
        )
58 +
        int_var =
59 +
            int_var + MOI.get(
60 +
                model,
61 +
                MOI.NumberOfConstraints{MOI.VariableIndex,MOI.ZeroOne}(),
62 +
            )
51 63
        all_var = MOI.get(model, MOI.NumberOfVariables())
52 64
        if int_var != all_var
53 -
            throw("Currently MibS works on only MIP-MIP problems and the input model is not MIP-MIP!!")
65 +
            throw(
66 +
                "Currently MibS works on only MIP-MIP problems and the input model is not MIP-MIP!!",
67 +
            )
54 68
        end
55 69
    end
56 70
57 71
    lower_sense = MOI.get(lower, MOI.ObjectiveSense())
58 72
59 -
    return model, lower_variables, lower_objective, lower_constraints, lower_sense
73 +
    return model,
74 +
    lower_variables,
75 +
    lower_objective,
76 +
    lower_constraints,
77 +
    lower_sense
60 78
end
61 79
62 80
function _index_to_row_link(model::MOI.FileFormats.MPS.Model)
63 81
    i = 0
64 -
    dict = Dict{MOI.ConstraintIndex, Int}()
82 +
    dict = Dict{MOI.ConstraintIndex,Int}()
65 83
    for (S, _) in MOI.FileFormats.MPS.SET_TYPES
66 84
        for ci in MOI.get(
67 85
            model,
68 -
            MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64}, S}(),
86 +
            MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64},S}(),
69 87
        )
70 88
            dict[ci] = i
71 89
            i += 1
@@ -87,13 +105,12 @@
Loading
87 105
    lower_objective::MOI.ScalarAffineFunction,
88 106
    lower_constraints::Vector{MOI.ConstraintIndex},
89 107
    lower_sense::MOI.OptimizationSense,
90 -
    aux_filename::String
108 +
    aux_filename::String,
91 109
)
92 110
    rows = _index_to_row_link(new_model)
93 111
    cols = _index_to_column_link(new_model)
94 -
    obj_coefficients = Dict{MOI.VariableIndex,Float64}(
95 -
        x => 0.0 for x in lower_variables
96 -
    )
112 +
    obj_coefficients =
113 +
        Dict{MOI.VariableIndex,Float64}(x => 0.0 for x in lower_variables)
97 114
    for term in lower_objective.terms
98 115
        if haskey(obj_coefficients, term.variable)
99 116
            obj_coefficients[term.variable] += term.coefficient
@@ -111,7 +128,7 @@
Loading
111 128
        for x in lower_variables
112 129
            println(io, "LO $(obj_coefficients[x])")
113 130
        end
114 -
        println(io, "OS ", lower_sense == MOI.MAX_SENSE ? -1 : 1)
131 +
        return println(io, "OS ", lower_sense == MOI.MAX_SENSE ? -1 : 1)
115 132
    end
116 133
    return
117 134
end
@@ -125,12 +142,12 @@
Loading
125 142
    # write(io, "\n BilevelJuMP Calling MibS \n")
126 143
    io_err = "mibs_errors.txt"
127 144
    mibs_call() do exe
128 -
        run(
145 +
        return run(
129 146
            pipeline(
130 -
                `$(exe) -Alps_instance $(mps_filename) -MibS_auxiliaryInfoFile $(aux_filename)`,
147 +
                `$(exe) -Alps_instance $(mps_filename) -MibS_auxiliaryInfoFile $(aux_filename)`;
131 148
                stdout = io,
132 149
                stderr = io_err,
133 -
            )
150 +
            ),
134 151
        )
135 152
    end
136 153
    # seekstart(io_err)
@@ -141,8 +158,8 @@
Loading
141 158
function _parse_output(
142 159
    output::String,
143 160
    new_model::MOI.FileFormats.MPS.Model,
144 -
    lower_variables::Vector{MOI.VariableIndex}
145 -
    )
161 +
    lower_variables::Vector{MOI.VariableIndex},
162 +
)
146 163
    lines = split(output, '\n')
147 164
    found_status = false
148 165
    objective_value = NaN
@@ -150,7 +167,6 @@
Loading
150 167
    upper = Dict{Int,Float64}()
151 168
    lower = Dict{Int,Float64}()
152 169
153 -
154 170
    all_var = MOI.get(new_model, MOI.ListOfVariableIndices())
155 171
156 172
    CntU = 0
@@ -172,7 +188,7 @@
Loading
172 188
            Dict_Lower_IndexToModel[CntD] = y
173 189
            CntD = CntD + 1
174 190
        else
175 -
            Dict_Upper_Name[CntU] = nameofvar  
191 +
            Dict_Upper_Name[CntU] = nameofvar
176 192
            Dict_Upper_Value[nameofvar] = 0
177 193
            Dict_Upper_IndexToModel[CntU] = y
178 194
            CntU = CntU + 1
@@ -180,7 +196,6 @@
Loading
180 196
        Dict_All[y] = 0
181 197
    end
182 198
183 -
184 199
    for line in lines
185 200
        if !found_status
186 201
            if occursin("Optimal solution", line)
@@ -222,7 +237,7 @@
Loading
222 237
        nonzero_lower = lower,
223 238
        all_upper = Dict_Upper_Value,
224 239
        all_lower = Dict_Lower_Value,
225 -
        all_var = Dict_All
240 +
        all_var = Dict_All,
226 241
    )
227 242
end
228 243
@@ -262,7 +277,7 @@
Loading
262 277
    mktempdir() do path
263 278
        mps_filename = joinpath(path, "model.mps")
264 279
        aux_filename = joinpath(path, "model.aux")
265 -
        new_model, variables, objective, constraints, sense  =
280 +
        new_model, variables, objective, constraints, sense =
266 281
            _build_single_model(model, true)
267 282
        # This MPS file must be strictly compliant with the format
268 283
        MOI.write_to_file(new_model, mps_filename)
@@ -285,24 +300,28 @@
Loading
285 300
            mps_db = joinpath(orig_path, debug_file_prefix * "model.mps")
286 301
            aux_db = joinpath(orig_path, debug_file_prefix * "model.aux")
287 302
            try
288 -
                cp(mps_filename, mps_db, force = true)
303 +
                cp(mps_filename, mps_db; force = true)
289 304
            catch e
290 -
                println("BilevelJuMP failed to write debug file $mps_db: with $e")
305 +
                println(
306 +
                    "BilevelJuMP failed to write debug file $mps_db: with $e",
307 +
                )
291 308
            end
292 309
            try
293 -
                cp(aux_filename, aux_db, force = true)
310 +
                cp(aux_filename, aux_db; force = true)
294 311
            catch e
295 -
                println("BilevelJuMP failed to write debug file $aux_db: with $e")
312 +
                println(
313 +
                    "BilevelJuMP failed to write debug file $aux_db: with $e",
314 +
                )
296 315
            end
297 316
        end
298 317
        if length(err) > 0
299 318
            mibs_error =
300 -
            "MibS returned:\n\n" *
301 -
            "$err\n\n" *
302 -
            "MibS input files can be found at:\n" *
303 -
            "* $mps_db\n" *
304 -
            "* $aux_db\n\n" *
305 -
            "Please include these files if you open an issue.\n"
319 +
                "MibS returned:\n\n" *
320 +
                "$err\n\n" *
321 +
                "MibS input files can be found at:\n" *
322 +
                "* $mps_db\n" *
323 +
                "* $aux_db\n\n" *
324 +
                "Please include these files if you open an issue.\n"
306 325
            error(mibs_error)
307 326
        end
308 327
        if length(output) == 0

@@ -26,49 +26,49 @@
Loading
26 26
    lo::T
27 27
    hi::T
28 28
end
29 -
function Interval(lo::T, hi::T) where {T <: Real}
29 +
function Interval(lo::T, hi::T) where {T<:Real}
30 30
    # if hi < lo <= hi + eps(T)
31 31
    #     lo = hi
32 32
    # end
33 33
    @assert lo <= hi
34 -
    Interval{T}(lo, hi)
34 +
    return Interval{T}(lo, hi)
35 35
end
36 36
37 37
function Base.iszero(a::Interval)
38 -
    iszero(a.hi) && iszero(a.lo)
38 +
    return iszero(a.hi) && iszero(a.lo)
39 39
end
40 40
41 41
+(a::Interval) = a
42 42
-(a::Interval) = Interval(-a.hi, -a.lo)
43 43
44 44
function +(a::Interval{T}, b::T) where {T<:Real}
45 -
    Interval(a.lo + b, a.hi + b)
45 +
    return Interval(a.lo + b, a.hi + b)
46 46
end
47 -
+(b::T, a::Interval{T}) where {T<:Real} = a+b
47 +
+(b::T, a::Interval{T}) where {T<:Real} = a + b
48 48
49 49
function -(a::Interval{T}, b::T) where {T<:Real}
50 -
    Interval(a.lo - b, a.hi - b)
50 +
    return Interval(a.lo - b, a.hi - b)
51 51
end
52 52
function -(b::T, a::Interval{T}) where {T<:Real}
53 -
    Interval(b - a.hi, b - a.lo)
53 +
    return Interval(b - a.hi, b - a.lo)
54 54
end
55 55
56 -
function +(a::Interval{T}, b::Interval{T}) where T<:Real
57 -
    Interval(a.lo + b.lo, a.hi + b.hi)
56 +
function +(a::Interval{T}, b::Interval{T}) where {T<:Real}
57 +
    return Interval(a.lo + b.lo, a.hi + b.hi)
58 58
end
59 59
60 -
function -(a::Interval{T}, b::Interval{T}) where T<:Real
61 -
    Interval(a.lo - b.hi, a.hi - b.lo)
60 +
function -(a::Interval{T}, b::Interval{T}) where {T<:Real}
61 +
    return Interval(a.lo - b.hi, a.hi - b.lo)
62 62
end
63 63
64 64
## Multiplication
65 65
function *(x::T, a::Interval{T}) where {T<:Real}
66 66
    (iszero(a) || iszero(x)) && return Interval(zero(T), zero(T))
67 67
    if x ≥ 0.0
68 -
        return Interval(a.lo*x, a.hi*x)
68 +
        return Interval(a.lo * x, a.hi * x)
69 69
    else
70 -
        return Interval(a.hi*x, a.lo*x)
70 +
        return Interval(a.hi * x, a.lo * x)
71 71
    end
72 72
end
73 73
74 -
*(a::Interval{T}, x::T) where {T<:Real} = x*a
74 +
*(a::Interval{T}, x::T) where {T<:Real} = x * a

@@ -1,18 +1,33 @@
Loading
1 -
JuMP.num_variables(m::AbstractBilevelModel) = JuMP.num_variables(bilevel_model(m))
1 +
function JuMP.num_variables(m::AbstractBilevelModel)
2 +
    return JuMP.num_variables(bilevel_model(m))
3 +
end
2 4
_plural(n) = (isone(n) ? "" : "s")
3 5
function JuMP.show_constraints_summary(io::IO, model::BilevelModel)
4 6
    JuMP.show_constraints_summary(io, Upper(model))
5 -
    JuMP.show_constraints_summary(io, Lower(model), true)
7 +
    return JuMP.show_constraints_summary(io, Lower(model), true)
6 8
end
7 9
function JuMP.show_constraints_summary(io::IO, model::UpperModel)
8 10
    n = JuMP.num_constraints(model)
9 -
    println(io, "Upper Constraint", _plural(n), ": ", n)
11 +
    return println(io, "Upper Constraint", _plural(n), ": ", n)
10 12
end
11 -
function JuMP.show_constraints_summary(io::IO, model::LowerModel, line_break = false)
13 +
function JuMP.show_constraints_summary(
14 +
    io::IO,
15 +
    model::LowerModel,
16 +
    line_break = false,
17 +
)
12 18
    n = JuMP.num_constraints(model)
13 -
    print(io, "Lower Constraint", _plural(n), ": ", n, ifelse(line_break, "\n",""))
19 +
    return print(
20 +
        io,
21 +
        "Lower Constraint",
22 +
        _plural(n),
23 +
        ": ",
24 +
        n,
25 +
        ifelse(line_break, "\n", ""),
26 +
    )
27 +
end
28 +
function JuMP.show_backend_summary(io::IO, model::InnerBilevelModel)
29 +
    return JuMP.show_backend_summary(io, model.m)
14 30
end
15 -
JuMP.show_backend_summary(io::IO, model::InnerBilevelModel) = JuMP.show_backend_summary(io, model.m)
16 31
function JuMP.show_backend_summary(io::IO, model::BilevelModel)
17 32
    println(io, "Bilevel Model")
18 33
    println(io, "Solution method: ", model.mode)
@@ -36,15 +51,20 @@
Loading
36 51
end
37 52
38 53
function JuMP.show_objective_function_summary(io::IO, model::UpperModel)
39 -
    println(io, "Upper objective function type: \n",
40 -
            JuMP.objective_function_type(model))
54 +
    return println(
55 +
        io,
56 +
        "Upper objective function type: \n",
57 +
        JuMP.objective_function_type(model),
58 +
    )
41 59
end
42 60
function JuMP.show_objective_function_summary(io::IO, model::LowerModel)
43 -
    println(io, "Lower objective function type: \n",
44 -
            JuMP.objective_function_type(model))
61 +
    return println(
62 +
        io,
63 +
        "Lower objective function type: \n",
64 +
        JuMP.objective_function_type(model),
65 +
    )
45 66
end
46 67
function JuMP.show_objective_function_summary(io::IO, model::BilevelModel)
47 68
    JuMP.show_objective_function_summary(io, Upper(model))
48 -
    JuMP.show_objective_function_summary(io, Lower(model))
69 +
    return JuMP.show_objective_function_summary(io, Lower(model))
49 70
end
50 -

@@ -6,7 +6,7 @@
Loading
6 6
            MOI.set(dest, MOI.VariableName(), map[vi], name)
7 7
        end
8 8
    end
9 -
    for (F,S) in MOI.get(src, MOI.ListOfConstraintTypesPresent())
9 +
    for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent())
10 10
        if !(F <: MOI.VariableIndex)
11 11
            for con in MOI.get(src, MOI.ListOfConstraintIndices{F,S}())
12 12
                name = MOI.get(src, MOI.ConstraintName(), con)
@@ -18,8 +18,13 @@
Loading
18 18
    end
19 19
end
20 20
21 -
function append_to(dest::MOI.ModelLike, src::MOI.ModelLike, idxmap, 
22 -
    filter_constraints::Union{Nothing, Function}=nothing; allow_single_bounds::Bool = true)
21 +
function append_to(
22 +
    dest::MOI.ModelLike,
23 +
    src::MOI.ModelLike,
24 +
    idxmap,
25 +
    filter_constraints::Union{Nothing,Function} = nothing;
26 +
    allow_single_bounds::Bool = true,
27 +
)
23 28
24 29
    #=
25 30
        This function follows closely the function `default_copy_to` defined in
@@ -36,7 +41,7 @@
Loading
36 41
    vis_src = MOI.get(src, MOI.ListOfVariableIndices())
37 42
    # index_map_for_variable_indices only initializes the data structure
38 43
    # idxmap = index_map_for_variable_indices(vis_src)
39 -
    
44 +
40 45
    # The `NLPBlock` assumes that the order of variables does not change (#849)
41 46
    if MOI.NLPBlock() in MOI.get(src, MOI.ListOfModelAttributesSet())
42 47
        error("NLP models are not supported.")
@@ -91,144 +96,170 @@
Loading
91 96
end
92 97
93 98
# scalar
94 -
function MOIU.promote_operation(::typeof(LinearAlgebra.dot), ::Type{T},
95 -
    ::Type{<:Union{MOI.VariableIndex, MOI.ScalarAffineFunction{T}}},
96 -
    ::Type{T}
97 -
    ) where T
98 -
    MOI.ScalarAffineFunction{T}
99 +
function MOIU.promote_operation(
100 +
    ::typeof(LinearAlgebra.dot),
101 +
    ::Type{T},
102 +
    ::Type{<:Union{MOI.VariableIndex,MOI.ScalarAffineFunction{T}}},
103 +
    ::Type{T},
104 +
) where {T}
105 +
    return MOI.ScalarAffineFunction{T}
99 106
end
100 -
function MOIU.promote_operation(::typeof(LinearAlgebra.dot), ::Type{T},
107 +
function MOIU.promote_operation(
108 +
    ::typeof(LinearAlgebra.dot),
101 109
    ::Type{T},
102 -
    ::Type{<:Union{MOI.VariableIndex, MOI.ScalarAffineFunction{T}}}
103 -
    ) where T
104 -
    MOI.ScalarAffineFunction{T}
110 +
    ::Type{T},
111 +
    ::Type{<:Union{MOI.VariableIndex,MOI.ScalarAffineFunction{T}}},
112 +
) where {T}
113 +
    return MOI.ScalarAffineFunction{T}
105 114
end
106 -
function MOIU.promote_operation(::typeof(LinearAlgebra.dot), ::Type{T},
107 -
    ::Type{<:Union{MOI.VariableIndex, MOI.ScalarAffineFunction{T}}},
108 -
    ::Type{<:Union{MOI.VariableIndex, MOI.ScalarAffineFunction{T}}}
109 -
    ) where T
110 -
    MOI.ScalarQuadraticFunction{T}
115 +
function MOIU.promote_operation(
116 +
    ::typeof(LinearAlgebra.dot),
117 +
    ::Type{T},
118 +
    ::Type{<:Union{MOI.VariableIndex,MOI.ScalarAffineFunction{T}}},
119 +
    ::Type{<:Union{MOI.VariableIndex,MOI.ScalarAffineFunction{T}}},
120 +
) where {T}
121 +
    return MOI.ScalarQuadraticFunction{T}
111 122
end
112 -
function MOIU.promote_operation(::typeof(LinearAlgebra.dot), ::Type{T},
123 +
function MOIU.promote_operation(
124 +
    ::typeof(LinearAlgebra.dot),
125 +
    ::Type{T},
113 126
    ::Type{MOI.ScalarQuadraticFunction{T}},
114 -
    ::Type{T}
115 -
    ) where T
116 -
    MOI.ScalarQuadraticFunction{T}
127 +
    ::Type{T},
128 +
) where {T}
129 +
    return MOI.ScalarQuadraticFunction{T}
117 130
end
118 -
function MOIU.promote_operation(::typeof(LinearAlgebra.dot), ::Type{T},
131 +
function MOIU.promote_operation(
132 +
    ::typeof(LinearAlgebra.dot),
119 133
    ::Type{T},
120 -
    ::Type{MOI.ScalarQuadraticFunction{T}}
121 -
    ) where T
122 -
    MOI.ScalarQuadraticFunction{T}
134 +
    ::Type{T},
135 +
    ::Type{MOI.ScalarQuadraticFunction{T}},
136 +
) where {T}
137 +
    return MOI.ScalarQuadraticFunction{T}
123 138
end
124 139
# flip
125 -
function MOIU.operate(::typeof(LinearAlgebra.dot), ::Type{T},
140 +
function MOIU.operate(
141 +
    ::typeof(LinearAlgebra.dot),
142 +
    ::Type{T},
126 143
    f::Union{
127 144
        MOI.VariableIndex,
128 145
        MOI.ScalarAffineFunction{T},
129 -
        MOI.ScalarQuadraticFunction{T}
130 -
        },
131 -
    α::T) where T
146 +
        MOI.ScalarQuadraticFunction{T},
147 +
    },
148 +
    α::T,
149 +
) where {T}
132 150
    return MOIU.operate(LinearAlgebra.dot, T, α, f)
133 151
end
134 152
# pass to *
135 -
function MOIU.operate(::typeof(LinearAlgebra.dot), ::Type{T},
136 -
    f::Union{
137 -
        T,
138 -
        MOI.VariableIndex,
139 -
        MOI.ScalarAffineFunction{T}
140 -
        },
141 -
    g::Union{
142 -
        MOI.VariableIndex,
143 -
        MOI.ScalarAffineFunction{T}
144 -
        }
145 -
    ) where T
153 +
function MOIU.operate(
154 +
    ::typeof(LinearAlgebra.dot),
155 +
    ::Type{T},
156 +
    f::Union{T,MOI.VariableIndex,MOI.ScalarAffineFunction{T}},
157 +
    g::Union{MOI.VariableIndex,MOI.ScalarAffineFunction{T}},
158 +
) where {T}
146 159
    return MOIU.operate(*, T, f, g)
147 160
end
148 -
function MOIU.operate(::typeof(LinearAlgebra.dot), ::Type{T},
161 +
function MOIU.operate(
162 +
    ::typeof(LinearAlgebra.dot),
163 +
    ::Type{T},
149 164
    α::T,
150 -
    f::MOI.ScalarQuadraticFunction{T}
151 -
    ) where T
165 +
    f::MOI.ScalarQuadraticFunction{T},
166 +
) where {T}
152 167
    return MOIU.operate(*, T, f, α)
153 168
end
154 169
155 170
# vector
156 -
function MOIU.promote_operation(::typeof(LinearAlgebra.dot), ::Type{T},
157 -
    ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}},
158 -
    ::Type{Vector{T}}
159 -
    ) where T
160 -
    MOI.VectorAffineFunction{T}
171 +
function MOIU.promote_operation(
172 +
    ::typeof(LinearAlgebra.dot),
173 +
    ::Type{T},
174 +
    ::Type{<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}},
175 +
    ::Type{Vector{T}},
176 +
) where {T}
177 +
    return MOI.VectorAffineFunction{T}
161 178
end
162 -
function MOIU.promote_operation(::typeof(LinearAlgebra.dot), ::Type{T},
179 +
function MOIU.promote_operation(
180 +
    ::typeof(LinearAlgebra.dot),
181 +
    ::Type{T},
163 182
    ::Type{Vector{T}},
164 -
    ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}
165 -
    ) where T
166 -
    MOI.VectorAffineFunction{T}
183 +
    ::Type{<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}},
184 +
) where {T}
185 +
    return MOI.VectorAffineFunction{T}
167 186
end
168 -
function MOIU.promote_operation(::typeof(LinearAlgebra.dot), ::Type{T},
169 -
    ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}},
170 -
    ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}
171 -
    ) where T
172 -
    MOI.VectorQuadraticFunction{T}
187 +
function MOIU.promote_operation(
188 +
    ::typeof(LinearAlgebra.dot),
189 +
    ::Type{T},
190 +
    ::Type{<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}},
191 +
    ::Type{<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}},
192 +
) where {T}
193 +
    return MOI.VectorQuadraticFunction{T}
173 194
end
174 -
function MOIU.promote_operation(::typeof(LinearAlgebra.dot), ::Type{T},
195 +
function MOIU.promote_operation(
196 +
    ::typeof(LinearAlgebra.dot),
197 +
    ::Type{T},
175 198
    ::Type{MOI.VectorQuadraticFunction{T}},
176 -
    ::Type{Vector{T}}
177 -
    ) where T
178 -
    MOI.VectorQuadraticFunction{T}
199 +
    ::Type{Vector{T}},
200 +
) where {T}
201 +
    return MOI.VectorQuadraticFunction{T}
179 202
end
180 -
function MOIU.promote_operation(::typeof(LinearAlgebra.dot), ::Type{T},
203 +
function MOIU.promote_operation(
204 +
    ::typeof(LinearAlgebra.dot),
205 +
    ::Type{T},
181 206
    ::Type{Vector{T}},
182 -
    ::Type{MOI.VectorQuadraticFunction{T}}
183 -
    ) where T
184 -
    MOI.VectorQuadraticFunction{T}
207 +
    ::Type{MOI.VectorQuadraticFunction{T}},
208 +
) where {T}
209 +
    return MOI.VectorQuadraticFunction{T}
185 210
end
186 211
# flip
187 -
function MOIU.operate(::typeof(LinearAlgebra.dot), ::Type{T},
212 +
function MOIU.operate(
213 +
    ::typeof(LinearAlgebra.dot),
214 +
    ::Type{T},
188 215
    f::Union{
189 216
        MOI.VectorOfVariables,
190 217
        MOI.VectorAffineFunction{T},
191 -
        MOI.VectorQuadraticFunction{T}
192 -
        },
193 -
    α::Vector{T}) where T
218 +
        MOI.VectorQuadraticFunction{T},
219 +
    },
220 +
    α::Vector{T},
221 +
) where {T}
194 222
    return MOIU.operate(LinearAlgebra.dot, T, α, f)
195 223
end
196 224
# pass to _operate(LinearAlgebra.dot, ...)
197 -
function MOIU.operate(::typeof(LinearAlgebra.dot), ::Type{T},
198 -
    f::Union{
199 -
        Vector{T},
200 -
        MOI.VectorOfVariables,
201 -
        MOI.VectorAffineFunction{T}
202 -
        },
203 -
    g::Union{
204 -
        MOI.VectorOfVariables,
205 -
        MOI.VectorAffineFunction{T}
206 -
        }
207 -
    ) where T
225 +
function MOIU.operate(
226 +
    ::typeof(LinearAlgebra.dot),
227 +
    ::Type{T},
228 +
    f::Union{Vector{T},MOI.VectorOfVariables,MOI.VectorAffineFunction{T}},
229 +
    g::Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}},
230 +
) where {T}
208 231
    return _operate(LinearAlgebra.dot, T, f, g)
209 232
end
210 -
function MOIU.operate(::typeof(LinearAlgebra.dot), ::Type{T},
233 +
function MOIU.operate(
234 +
    ::typeof(LinearAlgebra.dot),
235 +
    ::Type{T},
211 236
    α::T,
212 -
    f::MOI.VectorQuadraticFunction{T}
213 -
    ) where T
237 +
    f::MOI.VectorQuadraticFunction{T},
238 +
) where {T}
214 239
    return _operate(LinearAlgebra.dot, T, f, α)
215 240
end
216 -
function _operate(::typeof(LinearAlgebra.dot), ::Type{T},
241 +
function _operate(
242 +
    ::typeof(LinearAlgebra.dot),
243 +
    ::Type{T},
217 244
    f::Union{
218 245
        Vector{T},
219 246
        MOI.VectorOfVariables,
220 247
        MOI.VectorAffineFunction{T},
221 -
        MOI.VectorQuadraticFunction{T}
222 -
        },
248 +
        MOI.VectorQuadraticFunction{T},
249 +
    },
223 250
    g::Union{
224 251
        MOI.VectorOfVariables,
225 252
        MOI.VectorAffineFunction{T},
226 -
        MOI.VectorQuadraticFunction{T}
227 -
    }) where T
228 -
253 +
        MOI.VectorQuadraticFunction{T},
254 +
    },
255 +
) where {T}
229 256
    dim = MOI.output_dimension(g)
230 257
    if MOI.output_dimension(f) != dim
231 -
        throw(DimensionMismatch("f and g are of different MOI.output_dimension's!"))
258 +
        throw(
259 +
            DimensionMismatch(
260 +
                "f and g are of different MOI.output_dimension's!",
261 +
            ),
262 +
        )
232 263
    end
233 264
234 265
    fs = MOIU.scalarize(f)
@@ -241,5 +272,5 @@
Loading
241 272
242 273
    return out
243 274
end
244 -
MOIU.scalarize(v::Vector{T}) where T<:Number = v
245 -
MOI.output_dimension(v::Vector{T}) where T<:Number = length(v)#
275 +
MOIU.scalarize(v::Vector{T}) where {T<:Number} = v
276 +
MOI.output_dimension(v::Vector{T}) where {T<:Number} = length(v)#

@@ -19,17 +19,23 @@
Loading
19 19
    # maps the BilevelVariableRef index
20 20
    # to JuMP variables of the correct level
21 21
    # variable that appear in both levels are inboth dicts
22 -
    var_upper::Dict{Int, JuMP.AbstractVariableRef}
23 -
    var_lower::Dict{Int, JuMP.AbstractVariableRef}
22 +
    var_upper::Dict{Int,JuMP.AbstractVariableRef}
23 +
    var_lower::Dict{Int,JuMP.AbstractVariableRef}
24 24
25 25
    # additional info for variables such as bound hints
26 26
    # holds the variable level: LOWER_BOTH UPPER_BOTH LOWER_ONLY UPPER_ONLY DUAL_OF_LOWER
27 -
    var_info::Dict{Int, BilevelVariableInfo}
27 +
    var_info::Dict{Int,BilevelVariableInfo}
28 28
29 29
    # maps JuMP.VariableRef to BilevelVariableRef
30 30
    # built upon necessity for getting contraints and functions
31 -
    var_upper_rev::Union{Nothing, Dict{JuMP.AbstractVariableRef, JuMP.AbstractVariableRef}}
32 -
    var_lower_rev::Union{Nothing, Dict{JuMP.AbstractVariableRef, JuMP.AbstractVariableRef}}
31 +
    var_upper_rev::Union{
32 +
        Nothing,
33 +
        Dict{JuMP.AbstractVariableRef,JuMP.AbstractVariableRef},
34 +
    }
35 +
    var_lower_rev::Union{
36 +
        Nothing,
37 +
        Dict{JuMP.AbstractVariableRef,JuMP.AbstractVariableRef},
38 +
    }
33 39
34 40
    # JuMP.VariableRef of variables from named level that are NOT linking
35 41
    upper_only::Set{JuMP.AbstractVariableRef}
@@ -37,56 +43,59 @@
Loading
37 43
    # upper level decisions that are "parameters" of the second level
38 44
    # keys are *decision* variables from the upper level
39 45
    # values are *parameter* variables from the lower level (linked to the keys)
40 -
    upper_to_lower_link::Dict{JuMP.AbstractVariableRef, JuMP.AbstractVariableRef}
46 +
    upper_to_lower_link::Dict{JuMP.AbstractVariableRef,JuMP.AbstractVariableRef}
41 47
    # lower level decisions that are input to upper
42 48
    # keys are *decision* variables from the lower level
43 49
    # values are *parameter* variables from the upper level (linked to the keys)
44 -
    lower_to_upper_link::Dict{JuMP.AbstractVariableRef, JuMP.AbstractVariableRef}
50 +
    lower_to_upper_link::Dict{JuMP.AbstractVariableRef,JuMP.AbstractVariableRef}
45 51
    # lower level decisions that are input to upper
46 52
    # keys are upper level variables (representing lower level dual variables)
47 53
    # values are lower level constraints
48 -
    upper_var_to_lower_ctr_link::Dict{JuMP.AbstractVariableRef, JuMP.ConstraintRef}
54 +
    upper_var_to_lower_ctr_link::Dict{
55 +
        JuMP.AbstractVariableRef,
56 +
        JuMP.ConstraintRef,
57 +
    }
49 58
    # joint link
50 59
    # for all variables that appear in both models
51 60
    # keys are upper indices and values are lower indices
52 61
    # same as: merge(upper_to_lower_link, reverse(lower_to_upper_link))
53 -
    link::Dict{JuMP.AbstractVariableRef, JuMP.AbstractVariableRef}
62 +
    link::Dict{JuMP.AbstractVariableRef,JuMP.AbstractVariableRef}
54 63
55 64
    # Integer index of the last constraint added (for indexing BilevelConstraintRef's)
56 65
    nextconidx::Int
57 66
58 67
    # maps the BilevelConstraintRef index
59 68
    # to JuMP ConstraintRef of the correct level
60 -
    ctr_upper::Dict{Int, JuMP.ConstraintRef}
61 -
    ctr_lower::Dict{Int, JuMP.ConstraintRef}
69 +
    ctr_upper::Dict{Int,JuMP.ConstraintRef}
70 +
    ctr_lower::Dict{Int,JuMP.ConstraintRef}
62 71
63 72
    # additional info for constraints such as bound hints and start values
64 -
    ctr_info::Dict{Int, BilevelConstraintInfo}
73 +
    ctr_info::Dict{Int,BilevelConstraintInfo}
65 74
66 75
    # maps JuMP.ConstraintRef to BilevelConstraintRef
67 76
    # built upon necessity for getting contraints and functions
68 -
    ctr_upper_rev::Union{Nothing, Dict{JuMP.ConstraintRef, JuMP.ConstraintRef}} # bilevel ref no defined
69 -
    ctr_lower_rev::Union{Nothing, Dict{JuMP.ConstraintRef, JuMP.ConstraintRef}} # bilevel ref no defined
77 +
    ctr_upper_rev::Union{Nothing,Dict{JuMP.ConstraintRef,JuMP.ConstraintRef}} # bilevel ref no defined
78 +
    ctr_lower_rev::Union{Nothing,Dict{JuMP.ConstraintRef,JuMP.ConstraintRef}} # bilevel ref no defined
70 79
71 80
    #
72 -
    solver#::MOI.ModelLike
73 -
    mode
81 +
    solver::Any#::MOI.ModelLike
82 +
    mode::Any
74 83
75 84
    # maps for MPEC based solution methods
76 85
    # from upper level JuMP.index(JuMP.VariableRef) = (MOI.VI)
77 86
    # to mpec indices
78 -
    upper_to_sblm
87 +
    upper_to_sblm::Any
79 88
    # from lower MOI indices
80 89
    # to mpec indices
81 -
    lower_to_sblm
90 +
    lower_to_sblm::Any
82 91
    # from lower dual MOI indices
83 92
    # to mpec indices
84 -
    lower_dual_to_sblm
93 +
    lower_dual_to_sblm::Any
85 94
    # from mped indices to solver indices
86 -
    sblm_to_solver
95 +
    sblm_to_solver::Any
87 96
    # lower primal to dual map
88 97
    # to obtain dual variables from primal constraints
89 -
    lower_primal_dual_map
98 +
    lower_primal_dual_map::Any
90 99
91 100
    # results from opt process
92 101
    solve_time::Float64
@@ -98,35 +107,34 @@
Loading
98 107
    pass_start::Bool
99 108
100 109
    # for completing the JuMP.Model API
101 -
    objdict::Dict{Symbol, Any}    # Same that JuMP.Model's field `objdict`
110 +
    objdict::Dict{Symbol,Any}    # Same that JuMP.Model's field `objdict`
102 111
103 112
    function BilevelModel()
104 -
105 113
        model = new(
106 114
            JuMP.Model(),
107 115
            JuMP.Model(),
108 116
109 117
            # var
110 118
            0,
111 -
            Dict{Int, JuMP.AbstractVariable}(),
112 -
            Dict{Int, JuMP.AbstractVariable}(),
113 -
            Dict{Int, BilevelVariableInfo}(),
119 +
            Dict{Int,JuMP.AbstractVariable}(),
120 +
            Dict{Int,JuMP.AbstractVariable}(),
121 +
            Dict{Int,BilevelVariableInfo}(),
114 122
            nothing,
115 123
            nothing,
116 124
117 125
            # links
118 126
            Set{JuMP.AbstractVariableRef}(),
119 127
            Set{JuMP.AbstractVariableRef}(),
120 -
            Dict{JuMP.AbstractVariable, JuMP.AbstractVariable}(),
121 -
            Dict{JuMP.AbstractVariable, JuMP.AbstractVariable}(),
122 -
            Dict{JuMP.AbstractVariable, JuMP.ConstraintRef}(),
123 -
            Dict{JuMP.AbstractVariable, JuMP.AbstractVariable}(),
128 +
            Dict{JuMP.AbstractVariable,JuMP.AbstractVariable}(),
129 +
            Dict{JuMP.AbstractVariable,JuMP.AbstractVariable}(),
130 +
            Dict{JuMP.AbstractVariable,JuMP.ConstraintRef}(),
131 +
            Dict{JuMP.AbstractVariable,JuMP.AbstractVariable}(),
124 132
125 133
            #ctr
126 134
            0,
127 -
            Dict{Int, JuMP.AbstractConstraint}(),
128 -
            Dict{Int, JuMP.AbstractConstraint}(),
129 -
            Dict{Int, BilevelConstraintInfo}(),
135 +
            Dict{Int,JuMP.AbstractConstraint}(),
136 +
            Dict{Int,JuMP.AbstractConstraint}(),
137 +
            Dict{Int,BilevelConstraintInfo}(),
130 138
            nothing,
131 139
            nothing,
132 140
@@ -149,19 +157,20 @@
Loading
149 157
            false,
150 158
            true,
151 159
            # jump api
152 -
            Dict{Symbol, Any}(),
153 -
            )
160 +
            Dict{Symbol,Any}(),
161 +
        )
154 162
155 163
        return model
156 164
    end
157 165
end
158 -
function BilevelModel(optimizer_constructor;
159 -
        mode::AbstractBilevelSolverMode = SOS1Mode(),
160 -
        add_bridges::Bool=true
161 -
    )
166 +
function BilevelModel(
167 +
    optimizer_constructor;
168 +
    mode::AbstractBilevelSolverMode = SOS1Mode(),
169 +
    add_bridges::Bool = true,
170 +
)
162 171
    bm = BilevelModel()
163 172
    set_mode(bm, mode)
164 -
    JuMP.set_optimizer(bm, optimizer_constructor; add_bridges=add_bridges)
173 +
    JuMP.set_optimizer(bm, optimizer_constructor; add_bridges = add_bridges)
165 174
    return bm
166 175
end
167 176
function set_mode(bm::BilevelModel, mode::AbstractBilevelSolverMode)
@@ -193,15 +202,23 @@
Loading
193 202
194 203
# obj
195 204
196 -
function set_link!(m::UpperModel, upper::JuMP.AbstractVariableRef, lower::JuMP.AbstractVariableRef)
205 +
function set_link!(
206 +
    m::UpperModel,
207 +
    upper::JuMP.AbstractVariableRef,
208 +
    lower::JuMP.AbstractVariableRef,
209 +
)
197 210
    bilevel_model(m).upper_to_lower_link[upper] = lower
198 211
    bilevel_model(m).link[upper] = lower
199 -
    nothing
212 +
    return nothing
200 213
end
201 -
function set_link!(m::LowerModel, upper::JuMP.AbstractVariableRef, lower::JuMP.AbstractVariableRef)
214 +
function set_link!(
215 +
    m::LowerModel,
216 +
    upper::JuMP.AbstractVariableRef,
217 +
    lower::JuMP.AbstractVariableRef,
218 +
)
202 219
    bilevel_model(m).lower_to_upper_link[lower] = upper
203 220
    bilevel_model(m).link[upper] = lower
204 -
    nothing
221 +
    return nothing
205 222
end
206 223
207 224
# Models to deal with variables that are not exchanged between models
@@ -223,14 +240,25 @@
Loading
223 240
mylevel_var_list(m::LowerOnlyModel) = bilevel_model(m).var_lower
224 241
mylevel_var_list(m::UpperOnlyModel) = bilevel_model(m).var_upper
225 242
226 -
in_upper(l::Level) = l == LOWER_BOTH || l == UPPER_BOTH || l == UPPER_ONLY || l == DUAL_OF_LOWER
243 +
function in_upper(l::Level)
244 +
    return l == LOWER_BOTH ||
245 +
           l == UPPER_BOTH ||
246 +
           l == UPPER_ONLY ||
247 +
           l == DUAL_OF_LOWER
248 +
end
227 249
in_lower(l::Level) = l == LOWER_BOTH || l == UPPER_BOTH || l == LOWER_ONLY
228 250
229 -
function push_single_level_variable!(m::LowerOnlyModel, vref::JuMP.AbstractVariableRef)
230 -
    push!(bilevel_model(m).lower_only, vref)
251 +
function push_single_level_variable!(
252 +
    m::LowerOnlyModel,
253 +
    vref::JuMP.AbstractVariableRef,
254 +
)
255 +
    return push!(bilevel_model(m).lower_only, vref)
231 256
end
232 -
function push_single_level_variable!(m::UpperOnlyModel, vref::JuMP.AbstractVariableRef)
233 -
    push!(bilevel_model(m).upper_only, vref)
257 +
function push_single_level_variable!(
258 +
    m::UpperOnlyModel,
259 +
    vref::JuMP.AbstractVariableRef,
260 +
)
261 +
    return push!(bilevel_model(m).upper_only, vref)
234 262
end
235 263
#### Model ####
236 264
@@ -245,19 +273,21 @@
Loading
245 273
end
246 274
247 275
# Constraints
248 -
const BilevelConstraintRef = JuMP.ConstraintRef{BilevelModel, Int}#, Shape <: AbstractShape
276 +
const BilevelConstraintRef = JuMP.ConstraintRef{BilevelModel,Int}#, Shape <: AbstractShape
249 277
250 278
# Objective
251 279
252 280
# Etc
253 281
254 282
JuMP.object_dictionary(m::BilevelModel) = m.objdict
255 -
JuMP.object_dictionary(m::AbstractBilevelModel) = JuMP.object_dictionary(bilevel_model(m))
283 +
function JuMP.object_dictionary(m::AbstractBilevelModel)
284 +
    return JuMP.object_dictionary(bilevel_model(m))
285 +
end
256 286
257 287
function convert_indices(d::Dict)
258 288
    ret = Dict{VI,VI}()
259 289
    # sizehint!(ret, length(d))
260 -
    for (k,v) in d
290 +
    for (k, v) in d
261 291
        ret[JuMP.index(k)] = JuMP.index(v)
262 292
    end
263 293
    return ret
@@ -265,7 +295,7 @@
Loading
265 295
function index2(d::Dict)
266 296
    ret = Dict{VI,CI}()
267 297
    # sizehint!(ret, length(d))
268 -
    for (k,v) in d
298 +
    for (k, v) in d
269 299
        ret[JuMP.index(k)] = JuMP.index(v)
270 300
    end
271 301
    return ret
@@ -345,7 +375,6 @@
Loading
345 375
    return nothing
346 376
end
347 377
348 -
349 378
# Statuses
350 379
function JuMP.primal_status(model::BilevelModel)
351 380
    _check_solver(model)
@@ -355,8 +384,11 @@
Loading
355 384
    return JuMP.primal_status(model.m)
356 385
end
357 386
358 -
JuMP.dual_status(::BilevelModel) = error(
359 -
    "Dual status cant be queried for BilevelModel, but you can query for Upper and Lower models.")
387 +
function JuMP.dual_status(::BilevelModel)
388 +
    return error(
389 +
        "Dual status cant be queried for BilevelModel, but you can query for Upper and Lower models.",
390 +
    )
391 +
end
360 392
function JuMP.dual_status(model::UpperModel)
361 393
    _check_solver(model.m)
362 394
    return MOI.get(model.m.solver, MOI.DualStatus())
@@ -382,7 +414,7 @@
Loading
382 414
function build_reverse_var_map!(um::UpperModel)
383 415
    m = bilevel_model(um)
384 416
    if m.var_upper_rev === nothing
385 -
        m.var_upper_rev = Dict{JuMP.AbstractVariableRef, BilevelVariableRef}()
417 +
        m.var_upper_rev = Dict{JuMP.AbstractVariableRef,BilevelVariableRef}()
386 418
        for (idx, ref) in m.var_upper
387 419
            m.var_upper_rev[ref] = BilevelVariableRef(m, idx)
388 420
        end
@@ -392,7 +424,7 @@
Loading
392 424
function build_reverse_var_map!(lm::LowerModel)
393 425
    m = bilevel_model(lm)
394 426
    if m.var_lower_rev === nothing
395 -
        m.var_lower_rev = Dict{JuMP.AbstractVariableRef, BilevelVariableRef}()
427 +
        m.var_lower_rev = Dict{JuMP.AbstractVariableRef,BilevelVariableRef}()
396 428
        for (idx, ref) in m.var_lower
397 429
            m.var_lower_rev[ref] = BilevelVariableRef(m, idx)
398 430
        end
@@ -403,70 +435,101 @@
Loading
403 435
get_reverse_var_map(m::LowerModel) = m.m.var_lower_rev
404 436
function reverse_replace_variable(f, m::InnerBilevelModel)
405 437
    build_reverse_var_map!(m)
406 -
    return replace_variables(f, mylevel_model(m), get_reverse_var_map(m), level(m))
438 +
    return replace_variables(
439 +
        f,
440 +
        mylevel_model(m),
441 +
        get_reverse_var_map(m),
442 +
        level(m),
443 +
    )
407 444
end
408 -
function replace_variables(var::VV, # JuMP.VariableRef
445 +
function replace_variables(
446 +
    var::VV, # JuMP.VariableRef
409 447
    model::M,
410 -
    variable_map::Dict{I, V},
411 -
    level::Level) where {I,V<:JuMP.AbstractVariableRef, M, VV<:JuMP.AbstractVariableRef}
448 +
    variable_map::Dict{I,V},
449 +
    level::Level,
450 +
) where {I,V<:JuMP.AbstractVariableRef,M,VV<:JuMP.AbstractVariableRef}
412 451
    return variable_map[var]
413 452
end
414 -
function replace_variables(var::BilevelVariableRef,
453 +
function replace_variables(
454 +
    var::BilevelVariableRef,
415 455
    model::M,
416 -
    variable_map::Dict{I, V},
417 -
    level::Level) where {I,V<:JuMP.AbstractVariableRef, M<:BilevelModel}
456 +
    variable_map::Dict{I,V},
457 +
    level::Level,
458 +
) where {I,V<:JuMP.AbstractVariableRef,M<:BilevelModel}
418 459
    if var.model === model && in_level(var, level)
419 460
        return variable_map[var.idx]
420 461
    elseif var.model === model
421 -
        error("Variable $(var) belonging Only to $(var.level) level, was added in the $(level) level.")
462 +
        error(
463 +
            "Variable $(var) belonging Only to $(var.level) level, was added in the $(level) level.",
464 +
        )
422 465
    else
423 -
        error("A BilevelModel cannot have expression using variables of a BilevelModel different from itself")
466 +
        error(
467 +
            "A BilevelModel cannot have expression using variables of a BilevelModel different from itself",
468 +
        )
424 469
    end
425 470
end
426 -
function replace_variables(aff::JuMP.GenericAffExpr{C, VV},
471 +
function replace_variables(
472 +
    aff::JuMP.GenericAffExpr{C,VV},
427 473
    model::M,
428 -
    variable_map::Dict{I, V},
429 -
    level::Level) where {I,C,V<:JuMP.AbstractVariableRef, M, VV}
430 -
    result = JuMP.GenericAffExpr{C, replace_var_type(M)}(0.0)#zero(aff)
474 +
    variable_map::Dict{I,V},
475 +
    level::Level,
476 +
) where {I,C,V<:JuMP.AbstractVariableRef,M,VV}
477 +
    result = JuMP.GenericAffExpr{C,replace_var_type(M)}(0.0)#zero(aff)
431 478
    result.constant = aff.constant
432 479
    for (coef, var) in JuMP.linear_terms(aff)
433 -
        JuMP.add_to_expression!(result,
434 -
        coef,
435 -
        replace_variables(var, model, variable_map, level))
480 +
        JuMP.add_to_expression!(
481 +
            result,
482 +
            coef,
483 +
            replace_variables(var, model, variable_map, level),
484 +
        )
436 485
    end
437 486
    return result
438 487
end
439 -
function replace_variables(quad::JuMP.GenericQuadExpr{C, VV},
488 +
function replace_variables(
489 +
    quad::JuMP.GenericQuadExpr{C,VV},
440 490
    model::M,
441 -
    variable_map::Dict{I, V},
442 -
    level::Level) where {I,C,V<:JuMP.AbstractVariableRef, M, VV}
491 +
    variable_map::Dict{I,V},
492 +
    level::Level,
493 +
) where {I,C,V<:JuMP.AbstractVariableRef,M,VV}
443 494
    aff = replace_variables(quad.aff, model, variable_map, level)
444 -
    quadv = JuMP.GenericQuadExpr{C, replace_var_type(M)}(aff)
495 +
    quadv = JuMP.GenericQuadExpr{C,replace_var_type(M)}(aff)
445 496
    for (coef, var1, var2) in JuMP.quad_terms(quad)
446 -
        JuMP.add_to_expression!(quadv,
447 -
        coef,
448 -
        replace_variables(var1, model, variable_map, level),
449 -
        replace_variables(var2, model, variable_map, level))
497 +
        JuMP.add_to_expression!(
498 +
            quadv,
499 +
            coef,
500 +
            replace_variables(var1, model, variable_map, level),
501 +
            replace_variables(var2, model, variable_map, level),
502 +
        )
450 503
    end
451 504
    return quadv
452 505
end
453 -
replace_variables(funcs::Vector, args...) = map(f -> replace_variables(f, args...), funcs)
506 +
function replace_variables(funcs::Vector, args...)
507 +
    return map(f -> replace_variables(f, args...), funcs)
508 +
end
454 509
455 510
function print_lp(m, name, file_format = MOI.FileFormats.FORMAT_AUTOMATIC)
456 -
    dest = MOI.FileFormats.Model(format = file_format, filename = name)
511 +
    dest = MOI.FileFormats.Model(; format = file_format, filename = name)
457 512
    MOI.copy_to(dest, m)
458 -
    MOI.write_to_file(dest, name)
513 +
    return MOI.write_to_file(dest, name)
459 514
end
460 515
461 516
# Optimize
462 517
463 -
JuMP.optimize!(::T) where {T<:AbstractBilevelModel} =
464 -
    error("Can't solve a model of type: $T ")
465 -
function JuMP.optimize!(model::BilevelModel;
466 -
    lower_prob = "", upper_prob = "", bilevel_prob = "", solver_prob = "", file_format = MOI.FileFormats.FORMAT_AUTOMATIC)
467 -
518 +
function JuMP.optimize!(::T) where {T<:AbstractBilevelModel}
519 +
    return error("Can't solve a model of type: $T ")
520 +
end
521 +
function JuMP.optimize!(
522 +
    model::BilevelModel;
523 +
    lower_prob = "",
524 +
    upper_prob = "",
525 +
    bilevel_prob = "",
526 +
    solver_prob = "",
527 +
    file_format = MOI.FileFormats.FORMAT_AUTOMATIC,
528 +
)
468 529
    if model.mode === nothing
469 -
        error("No solution mode selected, use `set_mode(model, mode)` or initialize with `BilevelModel(optimizer_constructor, mode = some_mode)`")
530 +
        error(
531 +
            "No solution mode selected, use `set_mode(model, mode)` or initialize with `BilevelModel(optimizer_constructor, mode = some_mode)`",
532 +
        )
470 533
    else
471 534
        mode = model.mode
472 535
    end
@@ -497,8 +560,7 @@
Loading
497 560
498 561
    t0 = time()
499 562
500 -
    moi_upper = JuMP.index.(
501 -
        collect(values(model.upper_to_lower_link)))
563 +
    moi_upper = JuMP.index.(collect(values(model.upper_to_lower_link)))
502 564
    moi_link = convert_indices(model.link)
503 565
    moi_link2 = index2(model.upper_var_to_lower_ctr_link)
504 566
@@ -506,9 +568,20 @@
Loading
506 568
    # build bound for FortunyAmatMcCarlMode
507 569
    build_bounds!(model, mode)
508 570
509 -
    single_blm, upper_to_sblm, lower_to_sblm, lower_primal_dual_map, lower_dual_to_sblm =
510 -
        build_bilevel(upper, lower, moi_link, moi_upper, mode, moi_link2,
511 -
            copy_names = model.copy_names, pass_start = model.pass_start)
571 +
    single_blm,
572 +
    upper_to_sblm,
573 +
    lower_to_sblm,
574 +
    lower_primal_dual_map,
575 +
    lower_dual_to_sblm = build_bilevel(
576 +
        upper,
577 +
        lower,
578 +
        moi_link,
579 +
        moi_upper,
580 +
        mode,
581 +
        moi_link2;
582 +
        copy_names = model.copy_names,
583 +
        pass_start = model.pass_start,
584 +
    )
512 585
513 586
    # pass additional info (hints - not actual problem data)
514 587
    # for lower level dual variables (start, upper hint, lower hint)
@@ -517,8 +590,9 @@
Loading
517 590
            ctr = model.ctr_lower[idx]
518 591
            # this fails for vector-constrained variables due dualization 0.3.5
519 592
            # because of constrained variables that change the dual
520 -
            pre_duals = lower_primal_dual_map.primal_con_dual_var[JuMP.index(ctr)] # vector
521 -
            duals = map(x->lower_dual_to_sblm[x], pre_duals)
593 +
            pre_duals =
594 +
                lower_primal_dual_map.primal_con_dual_var[JuMP.index(ctr)] # vector
595 +
            duals = map(x -> lower_dual_to_sblm[x], pre_duals)
522 596
            pass_dual_info(single_blm, duals, info)
523 597
        end
524 598
    end
@@ -538,7 +612,7 @@
Loading
538 612
    end
539 613
540 614
    sblm_to_solver = MOI.copy_to(solver, single_blm)
541 -
    
615 +
542 616
    if _has_nlp_data(model.upper)
543 617
        # NLP requires an upstream jump model
544 618
        # probably is neough to have the fields:
@@ -556,14 +630,15 @@
Loading
556 630
            vi_ss = sblm_to_solver[vi_sb]
557 631
            vi_is = vars_in_solver[i]
558 632
            if vi_ss != vi_is
559 -
                error("Failed building Non linear problem, please report an issue")
633 +
                error(
634 +
                    "Failed building Non linear problem, please report an issue",
635 +
                )
560 636
                # in case jump or MOI change something in copy/nlpblock
561 637
            end
562 638
        end
563 639
        _load_nlp_data(nlp_model)
564 640
    end
565 641
566 -
567 642
    if length(solver_prob) > 0
568 643
        print_lp(solver, solver_prob, file_format)
569 644
    end
@@ -590,14 +665,18 @@
Loading
590 665
591 666
function pass_primal_info(single_blm, primal, info::BilevelVariableInfo)
592 667
    if !isnan(info.upper) &&
593 -
        !MOI.is_valid(single_blm, CI{MOI.VariableIndex,LT{Float64}}(primal.value))
594 -
        MOI.add_constraint(single_blm,
595 -
            primal, LT{Float64}(info.upper))
668 +
       !MOI.is_valid(
669 +
        single_blm,
670 +
        CI{MOI.VariableIndex,LT{Float64}}(primal.value),
671 +
    )
672 +
        MOI.add_constraint(single_blm, primal, LT{Float64}(info.upper))
596 673
    end
597 674
    if !isnan(info.lower) &&
598 -
        !MOI.is_valid(single_blm, CI{MOI.VariableIndex,GT{Float64}}(primal.value))
599 -
        MOI.add_constraint(single_blm,
600 -
            primal, GT{Float64}(info.lower))
675 +
       !MOI.is_valid(
676 +
        single_blm,
677 +
        CI{MOI.VariableIndex,GT{Float64}}(primal.value),
678 +
    )
679 +
        MOI.add_constraint(single_blm, primal, GT{Float64}(info.lower))
601 680
    end
602 681
    return
603 682
end
@@ -607,31 +686,52 @@
Loading
607 686
        MOI.set(single_blm, MOI.VariablePrimalStart(), dual[], info.start)
608 687
    end
609 688
    if !isnan(info.upper) &&
610 -
        !MOI.is_valid(single_blm, CI{MOI.VariableIndex,LT{Float64}}(dual[].value))
611 -
        MOI.add_constraint(single_blm,
612 -
            dual[], LT{Float64}(info.upper))
689 +
       !MOI.is_valid(
690 +
        single_blm,
691 +
        CI{MOI.VariableIndex,LT{Float64}}(dual[].value),
692 +
    )
693 +
        MOI.add_constraint(single_blm, dual[], LT{Float64}(info.upper))
613 694
    end
614 695
    if !isnan(info.lower) &&
615 -
        !MOI.is_valid(single_blm, CI{MOI.VariableIndex,GT{Float64}}(dual[].value))
616 -
        MOI.add_constraint(single_blm,
617 -
            dual[], GT{Float64}(info.lower))
696 +
       !MOI.is_valid(
697 +
        single_blm,
698 +
        CI{MOI.VariableIndex,GT{Float64}}(dual[].value),
699 +
    )
700 +
        MOI.add_constraint(single_blm, dual[], GT{Float64}(info.lower))
618 701
    end
619 702
    return
620 703
end
621 -
function pass_dual_info(single_blm, dual, info::BilevelConstraintInfo{Vector{Float64}})
704 +
function pass_dual_info(
705 +
    single_blm,
706 +
    dual,
707 +
    info::BilevelConstraintInfo{Vector{Float64}},
708 +
)
622 709
    for i in eachindex(dual)
623 710
        if !isnan(info.start[i])
624 -
            MOI.set(single_blm, MOI.VariablePrimalStart(), dual[i], info.start[i])
711 +
            MOI.set(
712 +
                single_blm,
713 +
                MOI.VariablePrimalStart(),
714 +
                dual[i],
715 +
                info.start[i],
716 +
            )
625 717
        end
626 718
        if !isnan(info.upper[i]) &&
627 -
            !MOI.is_valid(single_blm, CI{MOI.VariableIndex,LT{Float64}}(dual[i].value))
628 -
            MOI.add_constraint(single_blm,
629 -
                dual[i], LT{Float64}(info.upper[i]))
719 +
           !MOI.is_valid(
720 +
            single_blm,
721 +
            CI{MOI.VariableIndex,LT{Float64}}(dual[i].value),
722 +
        )
723 +
            MOI.add_constraint(single_blm, dual[i], LT{Float64}(info.upper[i]))
630 724
        end
631 725
        if !isnan(info.lower[i]) &&
632 -
            !MOI.is_valid(single_blm, CI{MOI.VariableIndex,GT{Float64}}(dual[i].value))
633 -
            MOI.add_constraint(single_blm,
634 -
                dual[i], MOI.GreaterThan{Float64}(info.lower[i]))
726 +
           !MOI.is_valid(
727 +
            single_blm,
728 +
            CI{MOI.VariableIndex,GT{Float64}}(dual[i].value),
729 +
        )
730 +
            MOI.add_constraint(
731 +
                single_blm,
732 +
                dual[i],
733 +
                MOI.GreaterThan{Float64}(info.lower[i]),
734 +
            )
635 735
        end
636 736
    end
637 737
    return
@@ -705,13 +805,13 @@
Loading
705 805
inf_if_nan(::typeof(-), val) = ifelse(isnan(val), -Inf, val)
706 806
707 807
dual_lower_bound(::CI{F,LT{T}}) where {F,T} = -Inf
708 -
dual_upper_bound(::CI{F,LT{T}}) where {F,T} =  0.0
808 +
dual_upper_bound(::CI{F,LT{T}}) where {F,T} = 0.0
709 809
710 -
dual_lower_bound(::CI{F,GT{T}}) where {F,T} =  0.0
810 +
dual_lower_bound(::CI{F,GT{T}}) where {F,T} = 0.0
711 811
dual_upper_bound(::CI{F,GT{T}}) where {F,T} = +Inf
712 812
713 -
dual_lower_bound(::CI{F,ET{T}}) where {F,T} =  0.0
714 -
dual_upper_bound(::CI{F,ET{T}}) where {F,T} =  0.0
813 +
dual_lower_bound(::CI{F,ET{T}}) where {F,T} = 0.0
814 +
dual_upper_bound(::CI{F,ET{T}}) where {F,T} = 0.0
715 815
716 816
dual_lower_bound(::CI{F,S}) where {F,S} = -Inf
717 817
dual_upper_bound(::CI{F,S}) where {F,S} = +Inf
@@ -720,17 +820,23 @@
Loading
720 820
721 821
function _check_solver(bm::BilevelModel)
722 822
    if bm.solver === nothing
723 -
        error("No solver attached, use `set_optimizer(model, optimizer_constructor)` or initialize with `BilevelModel(optimizer_constructor)`")
823 +
        error(
824 +
            "No solver attached, use `set_optimizer(model, optimizer_constructor)` or initialize with `BilevelModel(optimizer_constructor)`",
825 +
        )
724 826
    end
725 827
end
726 828
727 -
function JuMP.set_optimizer(bm::BilevelModel, optimizer_constructor;
728 -
    add_bridges::Bool=true)
829 +
function JuMP.set_optimizer(
830 +
    bm::BilevelModel,
831 +
    optimizer_constructor;
832 +
    add_bridges::Bool = true,
833 +
)
729 834
    # error_if_direct_mode(model, :set_optimizer)
730 835
    if add_bridges
731 836
        # If `default_copy_to` without names is supported,
732 837
        # no need for a second cache.
733 -
        optimizer = MOI.instantiate(optimizer_constructor, with_bridge_type=Float64)
838 +
        optimizer =
839 +
            MOI.instantiate(optimizer_constructor; with_bridge_type = Float64)
734 840
        # for bridge_type in model.bridge_types
735 841
        #     _moi_add_bridge(optimizer, bridge_type)
736 842
        # end
@@ -740,26 +846,34 @@
Loading
740 846
741 847
    bm.solver = optimizer
742 848
    if !MOI.is_empty(bm.solver)
743 -
        error("Calling the `optimizer_constructor` must return an empty optimizer")
849 +
        error(
850 +
            "Calling the `optimizer_constructor` must return an empty optimizer",
851 +
        )
744 852
    end
745 853
    return bm
746 854
end
747 855
748 -
749 -
function pass_cache(bm::BilevelModel, mode::FortunyAmatMcCarlMode{T}) where T
856 +
function pass_cache(bm::BilevelModel, mode::FortunyAmatMcCarlMode{T}) where {T}
750 857
    mode.cache = bm.mode.cache
751 858
    return nothing
752 859
end
753 -
function pass_cache(bm::BilevelModel, mode::AbstractBilevelSolverMode{T}) where T
860 +
function pass_cache(
861 +
    bm::BilevelModel,
862 +
    mode::AbstractBilevelSolverMode{T},
863 +
) where {T}
754 864
    return nothing
755 865
end
756 866
757 -
function check_mixed_mode(::MixedMode{T}) where T
758 -
end
867 +
function check_mixed_mode(::MixedMode{T}) where {T} end
759 868
function check_mixed_mode(mode)
760 -
    error("Cant set/get mode on a specific object because the base mode is $mode while it should be MixedMode in this case. Run `set_mode(model, BilevelJuMP.MixedMode())`")
869 +
    return error(
870 +
        "Cant set/get mode on a specific object because the base mode is $mode while it should be MixedMode in this case. Run `set_mode(model, BilevelJuMP.MixedMode())`",
871 +
    )
761 872
end
762 -
function set_mode(ci::BilevelConstraintRef, mode::AbstractBilevelSolverMode{T}) where T
873 +
function set_mode(
874 +
    ci::BilevelConstraintRef,
875 +
    mode::AbstractBilevelSolverMode{T},
876 +
) where {T}
763 877
    bm = ci.model
764 878
    check_mixed_mode(bm.mode)
765 879
    _mode = deepcopy(mode)
@@ -787,15 +901,18 @@
Loading
787 901
    end
788 902
end
789 903
790 -
function set_mode(::BilevelConstraintRef, ::MixedMode{T}) where T
791 -
    error("Cant set MixedMode in a specific constraint")
904 +
function set_mode(::BilevelConstraintRef, ::MixedMode{T}) where {T}
905 +
    return error("Cant set MixedMode in a specific constraint")
792 906
end
793 -
function set_mode(::BilevelConstraintRef, ::StrongDualityMode{T}) where T
794 -
    error("Cant set StrongDualityMode in a specific constraint")
907 +
function set_mode(::BilevelConstraintRef, ::StrongDualityMode{T}) where {T}
908 +
    return error("Cant set StrongDualityMode in a specific constraint")
795 909
end
796 910
797 911
# mode for variable bounds
798 -
function set_mode(vi::BilevelVariableRef, mode::AbstractBilevelSolverMode{T}) where T
912 +
function set_mode(
913 +
    vi::BilevelVariableRef,
914 +
    mode::AbstractBilevelSolverMode{T},
915 +
) where {T}
799 916
    bm = vi.model
800 917
    check_mixed_mode(bm.mode)
801 918
    _mode = deepcopy(mode)
@@ -824,19 +941,19 @@
Loading
824 941
    end
825 942
end
826 943
827 -
function set_mode(::BilevelVariableRef, ::MixedMode{T}) where T
828 -
    error("Cant set MixedMode in a specific variable")
944 +
function set_mode(::BilevelVariableRef, ::MixedMode{T}) where {T}
945 +
    return error("Cant set MixedMode in a specific variable")
829 946
end
830 -
function set_mode(::BilevelVariableRef, ::StrongDualityMode{T}) where T
831 -
    error("Cant set StrongDualityMode in a specific variable")
947 +
function set_mode(::BilevelVariableRef, ::StrongDualityMode{T}) where {T}
948 +
    return error("Cant set StrongDualityMode in a specific variable")
832 949
end
833 950
834 951
function MOI.set(::BilevelModel, ::MOI.LazyConstraintCallback, func)
835 -
    error("Callbacks are not available in BilevelJuMP Models")
952 +
    return error("Callbacks are not available in BilevelJuMP Models")
836 953
end
837 954
function MOI.set(::BilevelModel, ::MOI.UserCutCallback, func)
838 -
    error("Callbacks are not available in BilevelJuMP Models")
955 +
    return error("Callbacks are not available in BilevelJuMP Models")
839 956
end
840 957
function MOI.set(::BilevelModel, ::MOI.HeuristicCallback, func)
841 -
    error("Callbacks are not available in BilevelJuMP Models")
842 -
end

@@ -1,8 +1,8 @@
Loading
1 1
function in_upper(cref::BilevelConstraintRef)
2 -
    cref.model.ctr_info[cref.index].level == UPPER_ONLY
2 +
    return cref.model.ctr_info[cref.index].level == UPPER_ONLY
3 3
end
4 4
function in_lower(cref::BilevelConstraintRef)
5 -
    cref.model.ctr_info[cref.index].level == LOWER_ONLY
5 +
    return cref.model.ctr_info[cref.index].level == LOWER_ONLY
6 6
end
7 7
function raw_ref(model::BilevelModel, idx::Int)
8 8
    if haskey(model.ctr_upper, idx)
@@ -20,24 +20,34 @@
Loading
20 20
    return JuMP.ConstraintRef(model, idx, raw.shape)
21 21
end
22 22
level(cref::BilevelConstraintRef) = cref.model.ctr_info[cref.index].level
23 -
function JuMP.add_constraint(::BilevelModel, ::JuMP.AbstractConstraint, ::String="")
24 -
    error(
25 -
        "Can't add constraint directly to the bilevel model `m`, "*
26 -
        "attach the constraint to the upper or lower model "*
27 -
        "with @constraint(Upper(m), ...) or @constraint(Lower(m), ...)")
23 +
function JuMP.add_constraint(
24 +
    ::BilevelModel,
25 +
    ::JuMP.AbstractConstraint,
26 +
    ::String = "",
27 +
)
28 +
    return error(
29 +
        "Can't add constraint directly to the bilevel model `m`, " *
30 +
        "attach the constraint to the upper or lower model " *
31 +
        "with @constraint(Upper(m), ...) or @constraint(Lower(m), ...)",
32 +
    )
28 33
end
29 -
function JuMP.constraint_object(con_ref::ConstraintRef{BilevelModel, Int})
34 +
function JuMP.constraint_object(con_ref::ConstraintRef{BilevelModel,Int})
30 35
    raw = raw_ref(con_ref)
31 36
    return JuMP.constraint_object(raw)
32 37
end
33 38
# JuMP.add_constraint(m::UpperModel, c::JuMP.VectorConstraint, name::String="") =
34 39
#     error("no vec ctr")
35 -
function JuMP.add_constraint(m::InnerBilevelModel, c::Union{JuMP.ScalarConstraint{F,S},JuMP.VectorConstraint{F,S}}, name::String="") where {F,S}
40 +
function JuMP.add_constraint(
41 +
    m::InnerBilevelModel,
42 +
    c::Union{JuMP.ScalarConstraint{F,S},JuMP.VectorConstraint{F,S}},
43 +
    name::String = "",
44 +
) where {F,S}
36 45
    blm = bilevel_model(m)
37 46
    blm.nextconidx += 1
38 47
    cref = JuMP.ConstraintRef(blm, blm.nextconidx, JuMP.shape(c))
39 48
    func = JuMP.jump_function(c)
40 -
    level_func = replace_variables(func, bilevel_model(m), mylevel_var_list(m), level(m))
49 +
    level_func =
50 +
        replace_variables(func, bilevel_model(m), mylevel_var_list(m), level(m))
41 51
    level_c = JuMP.build_constraint(error, level_func, c.set)
42 52
    level_cref = JuMP.add_constraint(mylevel_model(m), level_c, name)
43 53
    mylevel_ctr_list(m)[cref.index] = level_cref
@@ -47,12 +57,15 @@
Loading
47 57
    end
48 58
    blm.ctr_upper_rev = nothing
49 59
    blm.ctr_lower_rev = nothing
50 -
    cref
60 +
    return cref
51 61
end
52 62
53 -
JuMP.is_valid(m::BilevelModel, cref::BilevelConstraintRef) = cref.index in keys(m.ctr_info)
54 -
JuMP.is_valid(m::InnerBilevelModel, cref::BilevelConstraintRef) =
55 -
    JuMP.is_valid(bilevel_model(m), cref) && level(cref) == level(m)
63 +
function JuMP.is_valid(m::BilevelModel, cref::BilevelConstraintRef)
64 +
    return cref.index in keys(m.ctr_info)
65 +
end
66 +
function JuMP.is_valid(m::InnerBilevelModel, cref::BilevelConstraintRef)
67 +
    return JuMP.is_valid(bilevel_model(m), cref) && level(cref) == level(m)
68 +
end
56 69
function JuMP.constraint_object(cref::BilevelConstraintRef, F::Type, S::Type)
57 70
    cidx = cref.index
58 71
    model = cref.model
@@ -65,16 +78,21 @@
Loading
65 78
        return reverse_replace_variable(con, Lower(model))
66 79
    end
67 80
end
68 -
function reverse_replace_variable(con::JuMP.VectorConstraint, m::InnerBilevelModel)
81 +
function reverse_replace_variable(
82 +
    con::JuMP.VectorConstraint,
83 +
    m::InnerBilevelModel,
84 +
)
69 85
    func = reverse_replace_variable(con.func, m)
70 -
    JuMP.VectorConstraint(func, con.set, con.shape)
86 +
    return JuMP.VectorConstraint(func, con.set, con.shape)
71 87
end
72 -
function reverse_replace_variable(con::JuMP.ScalarConstraint, m::InnerBilevelModel)
88 +
function reverse_replace_variable(
89 +
    con::JuMP.ScalarConstraint,
90 +
    m::InnerBilevelModel,
91 +
)
73 92
    func = reverse_replace_variable(con.func, m)
74 -
    JuMP.ScalarConstraint(func, con.set)
93 +
    return JuMP.ScalarConstraint(func, con.set)
75 94
end
76 95
77 -
78 96
function empty_info(level, c::JuMP.ScalarConstraint{F,S}) where {F,S}
79 97
    return BilevelConstraintInfo{Float64}(level)
80 98
end
@@ -84,71 +102,101 @@
Loading
84 102
85 103
function _assert_dim(cref, array::Vector, value::Vector)
86 104
    if length(array) != length(value)
87 -
        error("For the Vector constraint {$(cref)}, expected a Vector of length = $(length(array)) and got a Vector of length = $(length(value))")
105 +
        error(
106 +
            "For the Vector constraint {$(cref)}, expected a Vector of length = $(length(array)) and got a Vector of length = $(length(value))",
107 +
        )
88 108
    end
89 109
    return
90 110
end
91 111
function _assert_dim(cref, array::Vector, value::Number)
92 -
    error("For the Vector constraint {$(cref)}, expected a Vector (of length = $(length(array))) and got the scalar $value")
112 +
    error(
113 +
        "For the Vector constraint {$(cref)}, expected a Vector (of length = $(length(array))) and got the scalar $value",
114 +
    )
93 115
    return
94 116
end
95 117
function _assert_dim(cref, array::Number, value::Number)
96 118
    return
97 119
end
98 120
function _assert_dim(cref, array::Number, value::Vector)
99 -
    error("For the Scalar constraint {$(cref)}, expected a Scalar and got the Vector $(value)")
121 +
    error(
122 +
        "For the Scalar constraint {$(cref)}, expected a Scalar and got the Vector $(value)",
123 +
    )
100 124
    return
101 125
end
102 126
103 -
function JuMP.set_dual_start_value(cref::BilevelConstraintRef, value::T) where T<:Number
127 +
function JuMP.set_dual_start_value(
128 +
    cref::BilevelConstraintRef,
129 +
    value::T,
130 +
) where {T<:Number}
104 131
    _assert_dim(cref, cref.model.ctr_info[cref.index].start, value)
105 -
    cref.model.ctr_info[cref.index].start = value
132 +
    return cref.model.ctr_info[cref.index].start = value
106 133
end
107 -
function JuMP.set_dual_start_value(cref::BilevelConstraintRef, value::T) where T<:Vector{S} where S
134 +
function JuMP.set_dual_start_value(
135 +
    cref::BilevelConstraintRef,
136 +
    value::T,
137 +
) where {T<:Vector{S}} where {S}
108 138
    array = cref.model.ctr_info[cref.index].start
109 139
    _assert_dim(cref, array, value)
110 -
    copyto!(array, value)
140 +
    return copyto!(array, value)
111 141
end
112 142
function JuMP.dual_start_value(cref::BilevelConstraintRef)
113 -
    cref.model.ctr_info[cref.index].start
143 +
    return cref.model.ctr_info[cref.index].start
114 144
end
115 145
116 -
function set_dual_upper_bound_hint(cref::BilevelConstraintRef, value::T) where T<:Number
146 +
function set_dual_upper_bound_hint(
147 +
    cref::BilevelConstraintRef,
148 +
    value::T,
149 +
) where {T<:Number}
117 150
    _assert_dim(cref, cref.model.ctr_info[cref.index].upper, value)
118 -
    cref.model.ctr_info[cref.index].upper = value
151 +
    return cref.model.ctr_info[cref.index].upper = value
119 152
end
120 -
function set_dual_upper_bound_hint(cref::BilevelConstraintRef, value::T) where T<:Vector{S} where S
153 +
function set_dual_upper_bound_hint(
154 +
    cref::BilevelConstraintRef,
155 +
    value::T,
156 +
) where {T<:Vector{S}} where {S}
121 157
    array = cref.model.ctr_info[cref.index].upper
122 158
    _assert_dim(cref, array, value)
123 -
    copyto!(array, value)
159 +
    return copyto!(array, value)
124 160
end
125 161
function get_dual_upper_bound_hint(cref::BilevelConstraintRef)
126 -
    cref.model.ctr_info[cref.index].upper
162 +
    return cref.model.ctr_info[cref.index].upper
127 163
end
128 -
function set_dual_lower_bound_hint(cref::BilevelConstraintRef, value::T) where T<:Number
164 +
function set_dual_lower_bound_hint(
165 +
    cref::BilevelConstraintRef,
166 +
    value::T,
167 +
) where {T<:Number}
129 168
    _assert_dim(cref, cref.model.ctr_info[cref.index].lower, value)
130 -
    cref.model.ctr_info[cref.index].lower = value
169 +
    return cref.model.ctr_info[cref.index].lower = value
131 170
end
132 -
function set_dual_lower_bound_hint(cref::BilevelConstraintRef, value::T) where T<:Vector{S} where S
171 +
function set_dual_lower_bound_hint(
172 +
    cref::BilevelConstraintRef,
173 +
    value::T,
174 +
) where {T<:Vector{S}} where {S}
133 175
    array = cref.model.ctr_info[cref.index].lower
134 176
    _assert_dim(cref, array, value)
135 -
    copyto!(array, value)
177 +
    return copyto!(array, value)
136 178
end
137 179
function get_dual_lower_bound_hint(cref::BilevelConstraintRef)
138 -
    cref.model.ctr_info[cref.index].lower
180 +
    return cref.model.ctr_info[cref.index].lower
139 181
end
140 182
141 -
function set_primal_upper_bound_hint(vref::BilevelVariableRef, value::T) where T<:Number
142 -
    vref.model.var_info[vref.idx].upper = value
183 +
function set_primal_upper_bound_hint(
184 +
    vref::BilevelVariableRef,
185 +
    value::T,
186 +
) where {T<:Number}
187 +
    return vref.model.var_info[vref.idx].upper = value
143 188
end
144 189
function get_primal_upper_bound_hint(vref::BilevelVariableRef)
145 -
    vref.model.var_info[vref.idx].upper
190 +
    return vref.model.var_info[vref.idx].upper
146 191
end
147 -
function set_primal_lower_bound_hint(vref::BilevelVariableRef, value::T) where T<:Number
148 -
    vref.model.var_info[vref.idx].lower = value
192 +
function set_primal_lower_bound_hint(
193 +
    vref::BilevelVariableRef,
194 +
    value::T,
195 +
) where {T<:Number}
196 +
    return vref.model.var_info[vref.idx].lower = value
149 197
end
150 198
function get_primal_lower_bound_hint(vref::BilevelVariableRef)
151 -
    vref.model.var_info[vref.idx].lower
199 +
    return vref.model.var_info[vref.idx].lower
152 200
end
153 201
154 202
function JuMP.value(cref::BilevelConstraintRef; result::Int = 1)
@@ -165,7 +213,11 @@
Loading
165 213
    end
166 214
    # Solver constraint associated with the single bilevel model constraint
167 215
    con_solver_idx = cref.model.sblm_to_solver[con_sblm_idx]
168 -
    return MOI.get(cref.model.solver, MOI.ConstraintPrimal(result), con_solver_idx)
216 +
    return MOI.get(
217 +
        cref.model.solver,
218 +
        MOI.ConstraintPrimal(result),
219 +
        con_solver_idx,
220 +
    )
169 221
end
170 222
# variables again (duals)
171 223
# code for using dual variables associated with lower level constraints
@@ -185,14 +237,14 @@
Loading
185 237
end
186 238
function JuMP.num_constraints(model::BilevelModel, f, s)
187 239
    return JuMP.num_constraints(Upper(model), f, s) +
188 -
        JuMP.num_constraints(Lower(model), f, s)
240 +
           JuMP.num_constraints(Lower(model), f, s)
189 241
end
190 242
191 243
struct DualOf
192 244
    ci::BilevelConstraintRef
193 245
end
194 246
function DualOf(::AbstractArray{<:T}) where {T<:JuMP.ConstraintRef}
195 -
    error(
247 +
    return error(
196 248
        "If you are trying to do something like:\n" *
197 249
        "@constraint(Lower(m), my_constraint_vector[t in 1:T], ...)\n" *
198 250
        "@variable(Upper(m), my_variable[1:N], " *
@@ -201,7 +253,7 @@
Loading
201 253
        "@variable(Upper(m), my_variable[t=1:N], " *
202 254
        "DualOf(my_constraint_vector[t]))\n" *
203 255
        "Or use anonynous variables:\n" *
204 -
        "@variable(Upper(m), variable_type = DualOf(my_constraint_vector[t]))"
256 +
        "@variable(Upper(m), variable_type = DualOf(my_constraint_vector[t]))",
205 257
    )
206 258
end
207 259
struct DualVariableInfo
@@ -214,9 +266,10 @@
Loading
214 266
    dual_of::DualOf;
215 267
    extra_kw_args...,
216 268
)
217 -
218 269
    if level(dual_of.ci) != LOWER_ONLY
219 -
        error("Variables can only be tied to LOWER level constraints, got $(dual_of.ci.level) level")
270 +
        error(
271 +
            "Variables can only be tied to LOWER level constraints, got $(dual_of.ci.level) level",
272 +
        )
220 273
    end
221 274
    for (kwarg, _) in extra_kw_args
222 275
        _error("Unrecognized keyword argument $kwarg")
@@ -232,37 +285,48 @@
Loading
232 285
        # info.has_ub = false
233 286
        # info.upper_bound = NaN
234 287
    end
235 -
    info.has_fix   && _error("Dual variable does not support fixing")
288 +
    info.has_fix && _error("Dual variable does not support fixing")
236 289
    if info.has_start
237 290
        JuMP.set_dual_start_value(dual_of.ci, info.start)
238 291
        # info.has_start = false
239 292
        # info.start = NaN
240 293
    end
241 -
    info.binary    && _error("Dual variable cannot be binary")
242 -
    info.integer   && _error("Dual variable cannot be integer")
294 +
    info.binary && _error("Dual variable cannot be binary")
295 +
    info.integer && _error("Dual variable cannot be integer")
243 296
244 -
    info = JuMP.VariableInfo(false, NaN, false, NaN,
245 -
                             false, NaN, false, NaN,
246 -
                             false, false)
247 -
248 -
    return DualVariableInfo(
249 -
        info,
250 -
        dual_of.ci
297 +
    info = JuMP.VariableInfo(
298 +
        false,
299 +
        NaN,
300 +
        false,
301 +
        NaN,
302 +
        false,
303 +
        NaN,
304 +
        false,
305 +
        NaN,
306 +
        false,
307 +
        false,
251 308
    )
309 +
310 +
    return DualVariableInfo(info, dual_of.ci)
252 311
end
253 -
function JuMP.add_variable(inner::UpperModel, dual_info::DualVariableInfo, name::String="")
312 +
function JuMP.add_variable(
313 +
    inner::UpperModel,
314 +
    dual_info::DualVariableInfo,
315 +
    name::String = "",
316 +
)
254 317
    # TODO vector version
255 318
    m = bilevel_model(inner)
256 319
    m.last_variable_index += 1
257 320
    vref = BilevelVariableRef(m, m.last_variable_index, DUAL_OF_LOWER)
258 -
    v_upper = JuMP.add_variable(m.upper, JuMP.ScalarVariable(dual_info.info), name)
321 +
    v_upper =
322 +
        JuMP.add_variable(m.upper, JuMP.ScalarVariable(dual_info.info), name)
259 323
    m.var_upper[vref.idx] = v_upper
260 324
    m.upper_var_to_lower_ctr_link[v_upper] = m.ctr_lower[dual_info.ci.index]
261 325
    m.var_info[vref.idx] = empty_info(DUAL_OF_LOWER)
262 326
    JuMP.set_name(vref, name)
263 327
    m.var_upper_rev = nothing
264 328
    m.var_lower_rev = nothing
265 -
    vref
329 +
    return vref
266 330
end
267 331
268 332
function get_constrain_ref(vref::BilevelVariableRef)
@@ -285,7 +349,8 @@
Loading
285 349
        con_lower_ref = cref.model.ctr_lower[cref.index]
286 350
        con_lower_idx = con_lower_ref.index
287 351
        # Dual variable associated with constraint index
288 -
        model_var_idxs = cref.model.lower_primal_dual_map.primal_con_dual_var[con_lower_idx]
352 +
        model_var_idxs =
353 +
            cref.model.lower_primal_dual_map.primal_con_dual_var[con_lower_idx]
289 354
        # Single bilevel model variable associated with the dual variable
290 355
        sblm_var_idxs = MOI.VariableIndex[]
291 356
        for vi in model_var_idxs
@@ -296,22 +361,27 @@
Loading
296 361
        for vi in sblm_var_idxs
297 362
            push!(solver_var_idxs, cref.model.sblm_to_solver[vi])
298 363
        end
299 -
        pre_duals = MOI.get(cref.model.solver, MOI.VariablePrimal(), solver_var_idxs)
364 +
        pre_duals =
365 +
            MOI.get(cref.model.solver, MOI.VariablePrimal(), solver_var_idxs)
300 366
        return JuMP.reshape_vector(
301 367
            pre_duals,
302 -
            JuMP.dual_shape(con_lower_ref.shape)
303 -
            )
368 +
            JuMP.dual_shape(con_lower_ref.shape),
369 +
        )
304 370
    elseif in_upper(cref)
305 371
        m = cref.model
306 372
        con_upper_ref = cref.model.ctr_upper[cref.index]
307 -
        solver_ctr_idx = m.sblm_to_solver[m.upper_to_sblm[JuMP.index(con_upper_ref)]]
308 -
        pre_duals = MOI.get(cref.model.solver, MOI.ConstraintDual(), solver_ctr_idx)
373 +
        solver_ctr_idx =
374 +
            m.sblm_to_solver[m.upper_to_sblm[JuMP.index(con_upper_ref)]]
375 +
        pre_duals =
376 +
            MOI.get(cref.model.solver, MOI.ConstraintDual(), solver_ctr_idx)
309 377
        return JuMP.reshape_vector(
310 378
            pre_duals,
311 -
            JuMP.dual_shape(con_upper_ref.shape)
312 -
            )
379 +
            JuMP.dual_shape(con_upper_ref.shape),
380 +
        )
313 381
    else
314 -
        error("Dual solutions of upper level constraints are not available. Either the solution method does nto porvide duals or or the solver failed to get one.")
382 +
        error(
383 +
            "Dual solutions of upper level constraints are not available. Either the solution method does nto porvide duals or or the solver failed to get one.",
384 +
        )
315 385
    end
316 386
end
317 387
@@ -327,7 +397,10 @@
Loading
327 397
    return JuMP.add_to_function_constant(raw_ref(cref), val)
328 398
end
329 399
330 -
function JuMP.normalized_coefficient(cref::BilevelConstraintRef, var::BilevelVariableRef)
400 +
function JuMP.normalized_coefficient(
401 +
    cref::BilevelConstraintRef,
402 +
    var::BilevelVariableRef,
403 +
)
331 404
    model = cref.model
332 405
    level_var = if in_upper(cref)
333 406
        model.var_upper[var.idx]
@@ -338,7 +411,10 @@
Loading
338 411
end
339 412
340 413
function JuMP.set_normalized_coefficient(
341 -
    cref::BilevelConstraintRef, var::BilevelVariableRef, val)
414 +
    cref::BilevelConstraintRef,
415 +
    var::BilevelVariableRef,
416 +
    val,
417 +
)
342 418
    model = cref.model
343 419
    level_var = if in_upper(cref)
344 420
        model.var_upper[var.idx]
@@ -356,35 +432,39 @@
Loading
356 432
function JuMP.list_of_constraint_types(
357 433
    model::BilevelModel,
358 434
)::Vector{Tuple{DataType,DataType}}
359 -
    return unique!(vcat(
360 -
        JuMP.list_of_constraint_types(Upper(model)),
361 -
        JuMP.list_of_constraint_types(Lower(model)),
362 -
    ))
435 +
    return unique!(
436 +
        vcat(
437 +
            JuMP.list_of_constraint_types(Upper(model)),
438 +
            JuMP.list_of_constraint_types(Lower(model)),
439 +
        ),
440 +
    )
363 441
end
364 442
365 443
function JuMP.all_constraints(model::BilevelModel, f, s)
366 -
    return unique!(vcat(
367 -
        JuMP.all_constraints(Upper(model), f, s),
368 -
        JuMP.all_constraints(Lower(model), f, s),
369 -
    ))
444 +
    return unique!(
445 +
        vcat(
446 +
            JuMP.all_constraints(Upper(model), f, s),
447 +
            JuMP.all_constraints(Lower(model), f, s),
448 +
        ),
449 +
    )
370 450
end
371 451
372 452
function JuMP.all_constraints(model::InnerBilevelModel, f, s)
373 453
    build_reverse_ctr_map!(model)
374 454
    m = mylevel_model(model)
375 455
    list = JuMP.all_constraints(m, f, s)
376 -
    get_reverse_ctr_map.(model, list)
456 +
    return get_reverse_ctr_map.(model, list)
377 457
end
378 458
function build_reverse_ctr_map!(um::UpperModel)
379 459
    m = bilevel_model(um)
380 -
    m.ctr_upper_rev = Dict{JuMP.ConstraintRef, JuMP.ConstraintRef}()
460 +
    m.ctr_upper_rev = Dict{JuMP.ConstraintRef,JuMP.ConstraintRef}()
381 461
    for (idx, ref) in m.ctr_upper
382 462
        m.ctr_upper_rev[ref] = BilevelConstraintRef(m, idx)
383 463
    end
384 464
end
385 465
function build_reverse_ctr_map!(lm::LowerModel)
386 466
    m = bilevel_model(lm)
387 -
    m.ctr_lower_rev = Dict{JuMP.ConstraintRef, JuMP.ConstraintRef}()
467 +
    m.ctr_lower_rev = Dict{JuMP.ConstraintRef,JuMP.ConstraintRef}()
388 468
    for (idx, ref) in m.ctr_lower
389 469
        m.ctr_lower_rev[ref] = BilevelConstraintRef(m, idx)
390 470
    end
@@ -411,4 +491,4 @@
Loading
411 491
    model.ctr_upper_rev = nothing
412 492
    model.ctr_lower_rev = nothing
413 493
    return nothing
414 -
end

@@ -7,10 +7,16 @@
Loading
7 7
8 8
function JuMP.set_optimizer_attribute(bm::BilevelModel, name::String, value)
9 9
    _check_solver(bm)
10 -
    return JuMP.set_optimizer_attribute(bm, MOI.RawOptimizerAttribute(name), value)
10 +
    return JuMP.set_optimizer_attribute(
11 +
        bm,
12 +
        MOI.RawOptimizerAttribute(name),
13 +
        value,
14 +
    )
11 15
end
12 16
function JuMP.set_optimizer_attribute(
13 -
    bm::BilevelModel, attr::MOI.AbstractOptimizerAttribute, value
17 +
    bm::BilevelModel,
18 +
    attr::MOI.AbstractOptimizerAttribute,
19 +
    value,
14 20
)
15 21
    _check_solver(bm)
16 22
    return MOI.set(bm.solver, attr, value)
@@ -23,10 +29,14 @@
Loading
23 29
24 30
function JuMP.get_optimizer_attribute(bm::BilevelModel, name::String)
25 31
    _check_solver(bm)
26 -
    return JuMP.get_optimizer_attribute(bm.solver, MOI.RawOptimizerAttribute(name))
32 +
    return JuMP.get_optimizer_attribute(
33 +
        bm.solver,
34 +
        MOI.RawOptimizerAttribute(name),
35 +
    )
27 36
end
28 37
function JuMP.get_optimizer_attribute(
29 -
    bm::BilevelModel, attr::MOI.AbstractOptimizerAttribute
38 +
    bm::BilevelModel,
39 +
    attr::MOI.AbstractOptimizerAttribute,
30 40
)
31 41
    _check_solver(bm)
32 42
    return MOI.get(bm.solver, attr)
@@ -95,4 +105,4 @@
Loading
95 105
end
96 106
function get_pass_start(bm::BilevelModel)
97 107
    return bm.pass_start
98 -
end

@@ -1,38 +1,55 @@
Loading
1 -
function JuMP.set_objective_sense(m::InnerBilevelModel, sense::MOI.OptimizationSense)
2 -
    JuMP.set_objective_sense(mylevel_model(m), sense)
1 +
function JuMP.set_objective_sense(
2 +
    m::InnerBilevelModel,
3 +
    sense::MOI.OptimizationSense,
4 +
)
5 +
    return JuMP.set_objective_sense(mylevel_model(m), sense)
6 +
end
7 +
function JuMP.set_objective(
8 +
    m::InnerBilevelModel,
9 +
    sense::MOI.OptimizationSense,
10 +
    f::JuMP.AbstractJuMPScalar,
11 +
)
12 +
    level_f =
13 +
        replace_variables(f, bilevel_model(m), mylevel_var_list(m), level(m))
14 +
    return JuMP.set_objective(mylevel_model(m), sense, level_f)
15 +
end
16 +
function JuMP.set_objective(
17 +
    m::InnerBilevelModel,
18 +
    sense::MOI.OptimizationSense,
19 +
    f::Real,
20 +
)
21 +
    return JuMP.set_objective(mylevel_model(m), sense, f)
22 +
end
23 +
function JuMP.objective_sense(m::InnerBilevelModel)
24 +
    return JuMP.objective_sense(mylevel_model(m))
3 25
end
4 -
function JuMP.set_objective(m::InnerBilevelModel, sense::MOI.OptimizationSense,
5 -
    f::JuMP.AbstractJuMPScalar)
6 -
    level_f = replace_variables(f, bilevel_model(m), mylevel_var_list(m), level(m))
7 -
    JuMP.set_objective(mylevel_model(m), sense, level_f)
8 -
end
9 -
function JuMP.set_objective(m::InnerBilevelModel, sense::MOI.OptimizationSense,
10 -
    f::Real)
11 -
    JuMP.set_objective(mylevel_model(m), sense, f)
12 -
end
13 -
JuMP.objective_sense(m::InnerBilevelModel) = JuMP.objective_sense(mylevel_model(m))
14 26
function JuMP.objective_function_type(m::InnerBilevelModel)
15 27
    tp = JuMP.objective_function_type(mylevel_model(m))
16 28
    return bilevel_type(m, tp)
17 29
end
18 30
bilevel_type(::InnerBilevelModel, ::Type{JuMP.VariableRef}) = BilevelVariableRef
19 -
function bilevel_type(::InnerBilevelModel, ::Type{JuMP.GenericAffExpr{C, V}}
20 -
) where {C, V}
21 -
    JuMP.GenericAffExpr{C, BilevelVariableRef}
22 -
end
23 -
function bilevel_type(::InnerBilevelModel, ::Type{JuMP.GenericQuadExpr{C, V}}
24 -
) where {C, V}
25 -
    JuMP.GenericAffExpr{C, BilevelVariableRef}
31 +
function bilevel_type(
32 +
    ::InnerBilevelModel,
33 +
    ::Type{JuMP.GenericAffExpr{C,V}},
34 +
) where {C,V}
35 +
    return JuMP.GenericAffExpr{C,BilevelVariableRef}
36 +
end
37 +
function bilevel_type(
38 +
    ::InnerBilevelModel,
39 +
    ::Type{JuMP.GenericQuadExpr{C,V}},
40 +
) where {C,V}
41 +
    return JuMP.GenericAffExpr{C,BilevelVariableRef}
26 42
end
27 43
# JuMP.objective_function(m::InnerBilevelModel) = mylevel_obj_function(m)
28 44
function JuMP.objective_function(m::InnerBilevelModel)
29 45
    f = JuMP.objective_function(mylevel_model(m))
30 -
    reverse_replace_variable(f, m)
46 +
    return reverse_replace_variable(f, m)
31 47
end
32 48
function JuMP.objective_function(m::InnerBilevelModel, FT::Type)
33 49
    f = JuMP.objective_function(mylevel_model(m), FT)
34 50
    f2 = reverse_replace_variable(f, m)
35 -
    f2 isa FT || error("The objective function is not of type $FT, show $(typeof(f2))")
51 +
    f2 isa FT ||
52 +
        error("The objective function is not of type $FT, show $(typeof(f2))")
36 53
    return f2
37 54
end
38 55
@@ -43,7 +60,7 @@
Loading
43 60
function JuMP.relative_gap(bm::UpperModel)::Float64
44 61
    return JuMP.relative_gap(bm.m)
45 62
end
46 -
function JuMP.dual_objective_value(bm::BilevelModel; result::Int=1)::Float64
63 +
function JuMP.dual_objective_value(bm::BilevelModel; result::Int = 1)::Float64
47 64
    _check_solver(bm)
48 65
    return MOI.get(bm.solver, MOI.DualObjectiveValue(result))
49 66
end
@@ -55,18 +72,25 @@
Loading
55 72
    return JuMP.objective_bound(bm.m)
56 73
end
57 74
58 -
function JuMP.set_objective(m::BilevelModel, sense::MOI.OptimizationSense,
59 -
    f::JuMP.AbstractJuMPScalar)
60 -
    bilevel_obj_error()
75 +
function JuMP.set_objective(
76 +
    m::BilevelModel,
77 +
    sense::MOI.OptimizationSense,
78 +
    f::JuMP.AbstractJuMPScalar,
79 +
)
80 +
    return bilevel_obj_error()
61 81
end
62 82
JuMP.objective_sense(m::BilevelModel) = JuMP.objective_sense(m.upper)# bilevel_obj_error()
63 83
JuMP.objective_function_type(model::BilevelModel) = bilevel_obj_error()
64 84
JuMP.objective_function(model::BilevelModel) = bilevel_obj_error()
65 85
function JuMP.objective_function(model::BilevelModel, FT::Type)
66 -
    bilevel_obj_error()
86 +
    return bilevel_obj_error()
67 87
end
68 88
69 -
bilevel_obj_error() = error("There is no objective for BilevelModel use Upper(.) and Lower(.)")
89 +
function bilevel_obj_error()
90 +
    return error(
91 +
        "There is no objective for BilevelModel use Upper(.) and Lower(.)",
92 +
    )
93 +
end
70 94
71 95
function JuMP.objective_value(model::BilevelModel)
72 96
    _check_solver(model)
@@ -82,22 +106,26 @@
Loading
82 106
function lower_objective_value(model::BilevelModel; result::Int = 1)
83 107
    f = JuMP.objective_function(Lower(model))
84 108
    # Evaluate the lower objective expression
85 -
    return JuMP.value(f, result = result)
109 +
    return JuMP.value(f; result = result)
86 110
    # return JuMP.value(v -> JuMP.value(v, result = result), f)
87 111
    # return JuMP.value(v -> inner_ref_to_value(Lower(model), v), f)
88 112
end
89 113
90 114
function JuMP.set_objective_coefficient(
91 -
    ::UpperModel, variable::BilevelVariableRef, coeff::Real)
115 +
    ::UpperModel,
116 +
    variable::BilevelVariableRef,
117 +
    coeff::Real,
118 +
)
92 119
    level_var = variable.model.var_upper[variable.idx]
93 120
    model = JuMP.owner_model(level_var)
94 -
    JuMP.set_objective_coefficient(
95 -
        model, level_var, coeff)
121 +
    return JuMP.set_objective_coefficient(model, level_var, coeff)
96 122
end
97 123
function JuMP.set_objective_coefficient(
98 -
    ::LowerModel, variable::BilevelVariableRef, coeff::Real)
124 +
    ::LowerModel,
125 +
    variable::BilevelVariableRef,
126 +
    coeff::Real,
127 +
)
99 128
    level_var = variable.model.var_lower[variable.idx]
100 129
    model = JuMP.owner_model(level_var)
101 -
    JuMP.set_objective_coefficient(
102 -
        model, level_var, coeff)
103 -
end

@@ -1,10 +1,13 @@
Loading
1 1
2 2
mylevel(v::BilevelVariableRef) = v.level
3 -
in_level(v::BilevelVariableRef, level::Level) = (
4 -
    v.level === LOWER_BOTH ||
5 -
    v.level === UPPER_BOTH ||
6 -
    v.level === level ||
7 -
    (v.level === DUAL_OF_LOWER && level === UPPER_ONLY))
3 +
function in_level(v::BilevelVariableRef, level::Level)
4 +
    return (
5 +
        v.level === LOWER_BOTH ||
6 +
        v.level === UPPER_BOTH ||
7 +
        v.level === level ||
8 +
        (v.level === DUAL_OF_LOWER && level === UPPER_ONLY)
9 +
    )
10 +
end
8 11
9 12
in_level(v::BilevelVariableRef, ::UpperModel) = in_upper(v)
10 13
in_level(v::BilevelVariableRef, ::LowerModel) = in_lower(v)
@@ -13,8 +16,8 @@
Loading
13 16
upper_ref(v::BilevelVariableRef) = v.model.var_upper[v.idx]
14 17
lower_ref(v::BilevelVariableRef) = v.model.var_lower[v.idx]
15 18
16 -
const BilevelAffExpr = GenericAffExpr{Float64, BilevelVariableRef}
17 -
const BilevelQuadExpr = GenericQuadExpr{Float64, BilevelVariableRef}
19 +
const BilevelAffExpr = GenericAffExpr{Float64,BilevelVariableRef}
20 +
const BilevelQuadExpr = GenericQuadExpr{Float64,BilevelVariableRef}
18 21
19 22
function jump_var_ref(v::BilevelVariableRef)
20 23
    level = mylevel(v)
@@ -28,22 +31,25 @@
Loading
28 31
function solver_ref(v::BilevelVariableRef)
29 32
    m = v.model
30 33
    if mylevel(v) == LOWER_ONLY
31 -
        return m.sblm_to_solver[
32 -
            m.lower_to_sblm[JuMP.index(lower_ref(v))]]
34 +
        return m.sblm_to_solver[m.lower_to_sblm[JuMP.index(lower_ref(v))]]
33 35
    else
34 -
        return m.sblm_to_solver[
35 -
            m.upper_to_sblm[JuMP.index(upper_ref(v))]]
36 +
        return m.sblm_to_solver[m.upper_to_sblm[JuMP.index(upper_ref(v))]]
36 37
    end
37 38
end
38 39
39 40
Base.broadcastable(v::BilevelVariableRef) = Ref(v)
40 41
Base.copy(v::BilevelVariableRef) = v
41 -
Base.:(==)(v::BilevelVariableRef, w::BilevelVariableRef) =
42 -
    v.model === w.model && v.idx == w.idx && v.level == w.level
42 +
function Base.:(==)(v::BilevelVariableRef, w::BilevelVariableRef)
43 +
    return v.model === w.model && v.idx == w.idx && v.level == w.level
44 +
end
43 45
JuMP.owner_model(v::BilevelVariableRef) = v.model
44 46
JuMP.isequal_canonical(v::BilevelVariableRef, w::BilevelVariableRef) = v == w
45 47
# add in both levels
46 -
function JuMP.add_variable(inner::InnerBilevelModel, v::JuMP.AbstractVariable, name::String="")
48 +
function JuMP.add_variable(
49 +
    inner::InnerBilevelModel,
50 +
    v::JuMP.AbstractVariable,
51 +
    name::String = "",
52 +
)
47 53
    m = bilevel_model(inner)
48 54
    m.last_variable_index += 1
49 55
    vref = BilevelVariableRef(m, m.last_variable_index, level_both(inner))
@@ -58,9 +64,13 @@
Loading
58 64
    JuMP.set_name(vref, name)
59 65
    m.var_upper_rev = nothing
60 66
    m.var_lower_rev = nothing
61 -
    vref
67 +
    return vref
62 68
end
63 -
function JuMP.add_variable(single::SingleBilevelModel, v::JuMP.AbstractVariable, name::String="")
69 +
function JuMP.add_variable(
70 +
    single::SingleBilevelModel,
71 +
    v::JuMP.AbstractVariable,
72 +
    name::String = "",
73 +
)
64 74
    m = bilevel_model(single)
65 75
    m.last_variable_index += 1
66 76
    vref = BilevelVariableRef(m, m.last_variable_index, level(single))
@@ -71,7 +81,7 @@
Loading
71 81
    JuMP.set_name(vref, name)
72 82
    m.var_upper_rev = nothing
73 83
    m.var_lower_rev = nothing
74 -
    vref
84 +
    return vref
75 85
end
76 86
function JuMP.delete(::BilevelModel, vref::BilevelVariableRef)
77 87
    model = vref.model
@@ -97,9 +107,12 @@
Loading
97 107
    model.var_lower_rev = nothing
98 108
    return nothing
99 109
end
100 -
JuMP.is_valid(m::BilevelModel, vref::BilevelVariableRef) = vref.idx in keys(m.var_info)
101 -
JuMP.is_valid(m::InnerBilevelModel, vref::BilevelVariableRef) =
102 -
    JuMP.is_valid(bilevel_model(m), vref) && in_level(vref, level(m))
110 +
function JuMP.is_valid(m::BilevelModel, vref::BilevelVariableRef)
111 +
    return vref.idx in keys(m.var_info)
112 +
end
113 +
function JuMP.is_valid(m::InnerBilevelModel, vref::BilevelVariableRef)
114 +
    return JuMP.is_valid(bilevel_model(m), vref) && in_level(vref, level(m))
115 +
end
103 116
JuMP.num_variables(m::BilevelModel) = length(m.var_info)
104 117
JuMP.num_variables(m::UpperModel) = length(m.m.var_upper)
105 118
JuMP.num_variables(m::LowerModel) = length(m.m.var_lower)
@@ -130,22 +143,38 @@
Loading
130 143
"""
131 144
function split_variable(::UpperModel, v::JuMP.AbstractVariable)
132 145
    var_upper = v
133 -
    var_lower = JuMP.ScalarVariable(JuMP.VariableInfo(
134 -
        false, NaN,
135 -
        false, NaN,
136 -
        false, NaN,
137 -
        v.info.has_start, v.info.start,
138 -
        false, false))
146 +
    var_lower = JuMP.ScalarVariable(
147 +
        JuMP.VariableInfo(
148 +
            false,
149 +
            NaN,
150 +
            false,
151 +
            NaN,
152 +
            false,
153 +
            NaN,
154 +
            v.info.has_start,
155 +
            v.info.start,
156 +
            false,
157 +
            false,
158 +
        ),
159 +
    )
139 160
    return var_upper, var_lower
140 161
end
141 162
function split_variable(::LowerModel, v::JuMP.AbstractVariable)
142 163
    var_lower = v
143 -
    var_upper = JuMP.ScalarVariable(JuMP.VariableInfo(
144 -
        false, NaN,
145 -
        false, NaN,
146 -
        false, NaN,
147 -
        v.info.has_start, v.info.start,
148 -
        false, false))
164 +
    var_upper = JuMP.ScalarVariable(
165 +
        JuMP.VariableInfo(
166 +
            false,
167 +
            NaN,
168 +
            false,
169 +
            NaN,
170 +
            false,
171 +
            NaN,
172 +
            v.info.has_start,
173 +
            v.info.start,
174 +
            false,
175 +
            false,
176 +
        ),
177 +
    )
149 178
    return var_upper, var_lower
150 179
end
151 180
@@ -153,14 +182,14 @@
Loading
153 182
    if mylevel(vref) == DUAL_OF_LOWER
154 183
        return !isnan(get_dual_lower_bound_hint(get_constrain_ref(vref)))
155 184
    end
156 -
    JuMP.has_lower_bound(jump_var_ref(vref))
185 +
    return JuMP.has_lower_bound(jump_var_ref(vref))
157 186
end
158 187
function JuMP.lower_bound(vref::BilevelVariableRef)
159 188
    if mylevel(vref) == DUAL_OF_LOWER
160 189
        return get_dual_lower_bound_hint(get_constrain_ref(vref))
161 190
    end
162 191
    # @assert !JuMP.is_fixed(vref)
163 -
    JuMP.lower_bound(jump_var_ref(vref))
192 +
    return JuMP.lower_bound(jump_var_ref(vref))
164 193
end
165 194
function JuMP.set_lower_bound(vref::BilevelVariableRef, lower::Number)
166 195
    if mylevel(vref) == DUAL_OF_LOWER
@@ -182,14 +211,14 @@
Loading
182 211
    if mylevel(vref) == DUAL_OF_LOWER
183 212
        return !isnan(get_dual_upper_bound_hint(get_constrain_ref(vref)))
184 213
    end
185 -
    JuMP.has_upper_bound(jump_var_ref(vref))
214 +
    return JuMP.has_upper_bound(jump_var_ref(vref))
186 215
end
187 216
function JuMP.upper_bound(vref::BilevelVariableRef)
188 217
    if mylevel(vref) == DUAL_OF_LOWER
189 218
        return get_dual_upper_bound_hint(get_constrain_ref(vref))
190 219
    end
191 220
    # @assert !JuMP.is_fixed(vref)
192 -
    JuMP.upper_bound(jump_var_ref(vref))
221 +
    return JuMP.upper_bound(jump_var_ref(vref))
193 222
end
194 223
function JuMP.set_upper_bound(vref::BilevelVariableRef, upper)
195 224
    if mylevel(vref) == DUAL_OF_LOWER
@@ -204,29 +233,29 @@
Loading
204 233
        set_dual_upper_bound_hint(get_constrain_ref(vref), NaN)
205 234
        return
206 235
    end
207 -
    JuMP.delete_upper_bound(jump_var_ref(vref))
236 +
    return JuMP.delete_upper_bound(jump_var_ref(vref))
208 237
end
209 238
210 239
JuMP.is_fixed(vref::BilevelVariableRef) = JuMP.is_fixed(jump_var_ref(vref))
211 240
JuMP.fix_value(vref::BilevelVariableRef) = JuMP.fix_value(jump_var_ref(vref))
212 -
function JuMP.fix(vref::BilevelVariableRef, value; force::Bool=false)
241 +
function JuMP.fix(vref::BilevelVariableRef, value; force::Bool = false)
213 242
    if mylevel(vref) == DUAL_OF_LOWER
214 243
        error("Dual variable cannot be fixed.")
215 244
    end
216 -
    JuMP.fix(jump_var_ref(vref), value; force=force)
245 +
    return JuMP.fix(jump_var_ref(vref), value; force = force)
217 246
end
218 247
function JuMP.unfix(vref::BilevelVariableRef)
219 248
    if mylevel(vref) == DUAL_OF_LOWER
220 249
        error("Dual variable cannot be fixed.")
221 250
    end
222 -
    JuMP.unfix(jump_var_ref(vref))
251 +
    return JuMP.unfix(jump_var_ref(vref))
223 252
end
224 253
225 254
function JuMP.start_value(vref::BilevelVariableRef)
226 255
    if mylevel(vref) == DUAL_OF_LOWER
227 256
        return JuMP.dual_start_value(get_constrain_ref(vref))
228 257
    end
229 -
    JuMP.start_value(jump_var_ref(vref))
258 +
    return JuMP.start_value(jump_var_ref(vref))
230 259
end
231 260
function JuMP.set_start_value(vref::BilevelVariableRef, start)
232 261
    if mylevel(vref) == DUAL_OF_LOWER
@@ -243,26 +272,26 @@
Loading
243 272
    if mylevel(vref) == DUAL_OF_LOWER
244 273
        error("Dual variable cannot be binary.")
245 274
    end
246 -
    JuMP.set_binary(jump_var_ref(vref))
275 +
    return JuMP.set_binary(jump_var_ref(vref))
247 276
end
248 277
function JuMP.unset_binary(vref::BilevelVariableRef)
249 278
    if mylevel(vref) == DUAL_OF_LOWER
250 279
        error("Dual variable cannot be binary.")
251 280
    end
252 -
    JuMP.unset_binary(jump_var_ref(vref))
281 +
    return JuMP.unset_binary(jump_var_ref(vref))
253 282
end
254 283
JuMP.is_integer(vref::BilevelVariableRef) = JuMP.is_integer(jump_var_ref(vref))
255 284
function JuMP.set_integer(vref::BilevelVariableRef)
256 285
    if mylevel(vref) == DUAL_OF_LOWER
257 286
        error("Dual variable cannot be integer.")
258 287
    end
259 -
    JuMP.set_integer(jump_var_ref(vref))
288 +
    return JuMP.set_integer(jump_var_ref(vref))
260 289
end
261 290
function JuMP.unset_integer(vref::BilevelVariableRef)
262 291
    if mylevel(vref) == DUAL_OF_LOWER
263 292
        error("Dual variable cannot be integer.")
264 293
    end
265 -
    JuMP.unset_integer(jump_var_ref(vref))
294 +
    return JuMP.unset_integer(jump_var_ref(vref))
266 295
end
267 296
268 297
function JuMP.value(v::BilevelVariableRef; result::Int = 1)::Float64
@@ -270,4 +299,4 @@
Loading
270 299
    solver = m.solver
271 300
    ref = solver_ref(v)
272 301
    return MOI.get(solver, MOI.VariablePrimal(result), ref)
273 -
end

@@ -6,7 +6,7 @@
Loading
6 6
=#
7 7
8 8
function _has_nlp_data(model)
9 -
    return  model.nlp_data !== nothing
9 +
    return model.nlp_data !== nothing
10 10
end
11 11
function _load_nlp_data(model)
12 12
    # callen in JuMP.optimize!
@@ -35,7 +35,9 @@
Loading
35 35
36 36
no_nlp() = error("Non-linear data must be passed to the Upper(.) model")
37 37
no_nlp_lower() = error("NLconstraint(s) are not allowed in the lower level")
38 -
no_nlp_lower_param() = error("NLparameter(s) are not allowed in the lower level")
38 +
function no_nlp_lower_param()
39 +
    return error("NLparameter(s) are not allowed in the lower level")
40 +
end
39 41
40 42
JuMP._init_NLP(m::UpperModel) = JuMP._init_NLP(mylevel_model(m))
41 43
JuMP._init_NLP(m::LowerModel) = no_nlp_lower()
@@ -49,10 +51,10 @@
Loading
49 51
end
50 52
51 53
function JuMP._new_parameter(::LowerModel, ::Number)
52 -
    no_nlp_lower_param()
54 +
    return no_nlp_lower_param()
53 55
end
54 56
function JuMP._new_parameter(::BilevelModel, ::Number)
55 -
    no_nlp()
57 +
    return no_nlp()
56 58
end
57 59
58 60
# function JuMP.set_objective_function(m::UpperModel, func::JuMP._NonlinearExprData)
@@ -73,7 +75,8 @@
Loading
73 75
    m.nlp_data.nlobj = ex
74 76
    return
75 77
end
76 -
function JuMP.set_objective(::LowerModel,
78 +
function JuMP.set_objective(
79 +
    ::LowerModel,
77 80
    ::MOI.OptimizationSense,
78 81
    ::JuMP._NonlinearExprData,
79 82
)
@@ -86,14 +89,26 @@
Loading
86 89
    return nothing
87 90
end
88 91
89 -
function JuMP._parse_NL_expr_runtime(m::UpperModel, x::BilevelVariableRef, tape, parent, values)
92 +
function JuMP._parse_NL_expr_runtime(
93 +
    m::UpperModel,
94 +
    x::BilevelVariableRef,
95 +
    tape,
96 +
    parent,
97 +
    values,
98 +
)
90 99
    if !in_upper(x)
91 100
        error(
92 101
            "Variable in nonlinear expression does not belong to the " *
93 102
            "upper model, it is a LowerOnly variable",
94 103
        )
95 104
    end
96 -
    JuMP._parse_NL_expr_runtime(mylevel_model(m), upper_ref(x), tape, parent, values)
105 +
    JuMP._parse_NL_expr_runtime(
106 +
        mylevel_model(m),
107 +
        upper_ref(x),
108 +
        tape,
109 +
        parent,
110 +
        values