-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Algorithms for Network Interdiction (flow) #6
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
# Network Interdiction | ||
|
||
## Flow | ||
|
||
Network Interdiction is a family of problems where some elements of graph | ||
(vertices or links usually) are forbidden, destroyed or have failed. The goal is | ||
then to compute some objective function, as a maximum flow or a shortest path. | ||
|
||
Currently, `LightGraphsExtras.jl` implement methods for three kinds of Interdiction | ||
Flow's problems. The Interdiction flow problems can be seen as a game between two | ||
players, a defender (that maximizes the flow) and an attacker (that destroy some | ||
links). Called using respectively `NetworkInterdictionProblem()`, | ||
`AdaptiveFlowArcProblem()` and `AdaptiveFlowPathProblem()`, the three following | ||
variants, as described by | ||
[Bertsimas et al.](http://dx.doi.org/10.1016/j.orl.2015.11.005), are considered : | ||
|
||
- The Network Interdiction Flow where the attacker strikes first, then a maximum-flow | ||
is computed on the remaining network. The goal is for the defender to predict the | ||
best worst case. The problem is called by using | ||
- Adaptive Network Flow whre the defender computes a flow first. There are two | ||
variants. | ||
|
||
- Arc version : the flow is expressed as combination of flow on the links. The | ||
destruction of an edge only implies the flow on that to be destroyed (it could | ||
results with some flow on other egdes not reaching the target node). | ||
- Path version : the flow is now combination of paths from the souce to the sink | ||
where each edge that is destroyed also implies the destruction of all the paths | ||
going through this edge. | ||
|
||
|
||
This Interdiction Flow (including all the variants) for general graphs is an NP-hard problem and several exact and approximative algorithms have been designed in the academic litterature. Called using respectively `MultilinkAttackAlgorithm()` and `BilevelMixedIntegerLinearProgram()`, `LightGraphsExtras.jl` provides the following methods (any help to implement other algorithms is welcome): | ||
|
||
- A deterministic pollynomial algorithm called Multilink Attack relying on the | ||
Extended Multiroute Flow algorithm (available in `LightGraphs.jl`) introduced by | ||
[Baffier et al.](http://dx.doi.org/10.1016/j.disopt.2016.05.002). For a certain | ||
category of graph, it solves Network Interdiction Flow and Adaptive Flow (arc) | ||
exactly. When it fails, it provides upper and lower bounds. It provides a | ||
2-approximation for Adaptive Flow (path), see | ||
[Suppakitpaisarn et al.](http://dx.doi.org/10.1109/HPSR.2015.7483079) | ||
for more details. | ||
- A Bilevel Mixed Integer Linear Program (BMILP) framework using `JuMP.jl` that is | ||
guaranteed to converge. (Only the adaptive flow (arc) vairant is covered yet, the | ||
others use dummy functions). The results of this framework through | ||
`LightGraphsExtras.jl` will be appear in the following month in the litterature. | ||
|
||
The `interdiction_flow{T<:AbstractFloat}` function takes the following arguments: | ||
|
||
- flow_graph::DiGraph # the input graph | ||
- source::Int # the source vertex | ||
- target::Int # the target vertex | ||
- capacity_matrix::AbstractArray{T, 2} # edge flow capacities | ||
- attacks::Int # argument for attacks | ||
- algorithm::AbstractInterdictionFlowAlgorithm # argument for algorithm, | ||
- problem::NetworkInterdictionProblem # argument for problem | ||
- solver::AbstractMathProgSolver # argument for solver (BMILP only) | ||
- rtol::T # relative tolerance (BMILP only) | ||
- atol::T # absolute tolerance (BMILP only) | ||
- time_limit::Float64 # time limit (BMILP only) | ||
|
||
If no number of attacks is given, it will be set to -1 (that means for any possible | ||
number of attacks between 0 and the source-sink connectivity). | ||
The function defaults to `MultilinkAttackAlgorithm()` and `NetworkInterdictionProblem()` | ||
for algorithm and problem. If `BilevelMixedIntegerLinearProgram()` is used, | ||
the solver, rtol, atol, and time_limit are also used and default as respectively | ||
`UnsetSolver()`, `sqrt(eps())`, `0.`, `Inf`. The relative and absolute tolerance are | ||
used to compare the upper and lower bounds for termination. | ||
Please consult the [`JuMP.jl` documentation](http://http://jump.readthedocs.io) for the | ||
use of the solver keyword, as it is similar to the `Model()` method there. | ||
|
||
All algorithms return a tuple with 1) a lower bound and 2) an upper bound. | ||
For the Multilink Attack algorithm, it also returns the restriction values (to use with | ||
the function maximum_flow in LightGraphs.jl) associated with 3-a) the lower bound and | ||
4-a) the upper bound. When the BMILP is used, the third element returned is 3-b) the | ||
time used by the algorithm. | ||
|
||
When the number of attacks is set to -1, an array with the results for any possible number of attacks will be output. Each result will be output as above. | ||
|
||
### Approximative comparison | ||
|
||
Approximative comparison to deal with Float precision. LaTeX command : `\precapproc` | ||
|
||
```julia | ||
function ⪷{T<:AbstractFloat}( | ||
x::T, | ||
y::T | ||
) | ||
return x ≈ y || x < y | ||
end | ||
``` | ||
|
||
### Usage Example : | ||
|
||
```julia | ||
|
||
# Create a flow-graph and a capacity matrix | ||
flow_graph = DiGraph(8) | ||
flow_edges = [ | ||
(1, 2, 10), (1, 3, 5), (1, 4, 15), (2, 3, 4), (2, 5, 9), | ||
(2, 6, 15), (3, 4, 4), (3, 6, 8), (4, 7, 16), (5, 6, 15), | ||
(5, 8, 10), (6, 7, 15), (6, 8, 10), (7, 3, 6), (7, 8, 10) | ||
] | ||
capacity_matrix = zeros(Float64, 8, 8) | ||
for e in flow_edges | ||
u, v, f = e | ||
add_edge!(flow_graph, u, v) | ||
capacity_matrix[u, v] = f | ||
end | ||
|
||
# Run default values | ||
lower_bound, upper_bound , lower_restriction, upper_restriction = | ||
interdiction_flow(flow_graph, 1, 8, capacity_matrix) | ||
|
||
# Run default values but with a specific number of attacks | ||
lower_bound, upper_bound , lower_restriction, upper_restriction = | ||
interdiction_flow(flow_graph, 1, 8, capacity_matrix, 1) | ||
|
||
# Run with BMILP for 1 attack and adaptive flow (arc) problem | ||
lower_bound, upper_bound , time_used = | ||
interdiction_flow(flow_graph, 1, 8, capacity_matrix, 1, | ||
algorithm = BilevelMixedIntegerLinearProgram(), | ||
problem = AdaptiveFlowArcProblem()) | ||
|
||
if lower_bound ⪷ upper_bound | ||
print("Lower bound = $lower_bound and Upper Bound = $upper_bound") | ||
println(" in $time_used seconds.") | ||
else | ||
error("There is a bound error in the BMILP") | ||
end | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# Method when the algorithm used is the Multilink Attack algorithm | ||
function adaptive_arc{T<:AbstractFloat}( | ||
flow_graph::DiGraph, # the input graph | ||
source::Int, # the source vertex | ||
target::Int, # the target vertex | ||
capacity_matrix::AbstractArray{T, 2}, # edge flow capacities | ||
attacks::Int, # argument for attacks | ||
algorithm::MultilinkAttackAlgorithm, # argument for algorithm | ||
solver::AbstractMathProgSolver, # argument for solver (unused) | ||
rtol::T, # absolute tolerance (unused) | ||
atol::T, # relative tolerance (unused) | ||
time_limit::Float64 # time limit (seconds) (unused) | ||
) | ||
return multilink_attack(flow_graph, source, target, capacity_matrix, attacks) | ||
end | ||
|
||
# Method when the algorithm used is a Bilevel Mixed Integer Linear Program | ||
function adaptive_arc{T<:AbstractFloat}( | ||
flow_graph::DiGraph, # the input graph | ||
source::Int, # the source vertex | ||
target::Int, # the target vertex | ||
capacity_matrix::AbstractArray{T, 2}, # edge flow capacities | ||
attacks::Int, # argument for attacks | ||
algorithm::BilevelMixedIntegerLinearProgram, # argument for algorithm | ||
solver::AbstractMathProgSolver, # argument for solver | ||
rtol::T, # absolute tolerance | ||
atol::T, # relative tolerance | ||
time_limit::Float64 # time limit (seconds) | ||
) | ||
return bilevel_adaptive_arc(flow_graph, source, target, capacity_matrix, | ||
attacks, solver, rtol, atol, time_limit) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Method when the algorithm used is the Multilink Attack algorithm | ||
function adaptive_path{T<:AbstractFloat}( | ||
flow_graph::DiGraph, # the input graph | ||
source::Int, # the source vertex | ||
target::Int, # the target vertex | ||
capacity_matrix::AbstractArray{T, 2}, # edge flow capacities | ||
attacks::Int, # argument for attacks | ||
algorithm::MultilinkAttackAlgorithm, # argument for algorithm | ||
solver::AbstractMathProgSolver, # argument for solver (unused) | ||
rtol::T, # absolute tolerance (unused) | ||
atol::T, # relative tolerance (unused) | ||
time_limit::Float64 # time limit (seconds) (unused) | ||
) | ||
lower_bound, upper_bound , lower_restriction, upper_restriction = | ||
multilink_attack(flow_graph, source, target, capacity_matrix, attacks) | ||
|
||
# MultilinkAttackAlgorithm is a 2-approximation for AdaptiveFlowPathProblem | ||
return lower_bound / 2, upper_bound, lower_restriction, upper_restriction | ||
end | ||
|
||
# Method when the algorithm used is a Bilevel Mixed Integer Linear Program | ||
function adaptive_path{T<:AbstractFloat}( | ||
flow_graph::DiGraph, # the input graph | ||
source::Int, # the source vertex | ||
target::Int, # the target vertex | ||
capacity_matrix::AbstractArray{T, 2}, # edge flow capacities | ||
attacks::Int, # argument for attacks | ||
algorithm::BilevelMixedIntegerLinearProgram, # argument for algorithm | ||
solver::AbstractMathProgSolver, # argument for solver (unused) | ||
rtol::T, # absolute tolerance (unused) | ||
atol::T, # relative tolerance (unused) | ||
time_limit::Float64 # time limit (seconds) (unused) | ||
) | ||
return bilevel_adaptive_path(flow_graph, source, target, capacity_matrix, | ||
attacks, solver, rtol, atol, time_limit) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
function init_first_level_arc{T<:AbstractFloat}( | ||
flow_graph::DiGraph, # the input graph | ||
source::Int, # the source vertex | ||
target::Int, # the target vertex | ||
capacity_matrix::AbstractArray{T, 2}, # edge flow capacities | ||
solver::AbstractMathProgSolver # keyword for solver | ||
) | ||
n = nv(flow_graph) # size of the network | ||
|
||
first_level = Model(solver = solver) | ||
|
||
# x : first level binary variables | ||
@variable(first_level, 0 ≤ x[i = 1:n, j = 1:n] ≤ capacity_matrix[i, j]) | ||
# z : objective function | ||
@variable(first_level, z) | ||
@objective(first_level, Max, z) | ||
|
||
# flow conservation constraints | ||
for v in vertices(flow_graph) | ||
if (v ≠ source && v ≠ target) | ||
@constraint(first_level, sum{x[i, j], i = 1:n, j = 1:n; | ||
capacity_matrix[i ,j] > 0} - sum{x[j, i], i = 1:n, j = 1:n; | ||
capacity_matrix[j, i] > 0} == 0) | ||
end | ||
end | ||
|
||
# objective function upper bound | ||
# @constraint(first_level, z ≤ prevfloat(typemax(T))) | ||
@constraint(first_level, z ≤ 10000000) | ||
|
||
return first_level, x, z | ||
end | ||
|
||
function init_second_level_arc{T<:AbstractFloat}( | ||
flow_graph::DiGraph, # the input graph | ||
source::Int, # the source vertex | ||
target::Int, # the target vertex | ||
capacity_matrix::AbstractArray{T, 2}, # edge flow capacities | ||
attacks::Int, # argument for attacks | ||
solver::AbstractMathProgSolver, # keyword for solver | ||
x::Matrix{JuMP.Variable} # flows from first_level | ||
) | ||
n = nv(flow_graph) # size of the network | ||
x_value = getvalue(x) # extract the value of variable x | ||
|
||
second_level = Model(solver = solver) | ||
|
||
# variable that model if an arc is attacked or not | ||
@variable(second_level, 0 ≤ μ[i = 1:n, j = 1:n] ≤ 1, Int) | ||
# first cut variables | ||
@variable(second_level, δ[i = 1:n,j = 1:n] ≥ 0) | ||
# second cut variables | ||
@variable(second_level, σ[i = 1:n] ≥ 0) | ||
# linearization variables: ν = δμ | ||
@variable(second_level, ν[i = 1:n, j = 1:n] ≥ 0) | ||
|
||
# set objective | ||
@objective(second_level, Min, sum{ | ||
x_value[i, j] * δ[i, j] - x_value[i, j] * ν[i, j], i = 1:n, j = 1:n; | ||
capacity_matrix[i, j] > 0}) | ||
|
||
# constraints over the edges | ||
for e in edges(flow_graph) | ||
i, j = src(e), dst(e) | ||
# first linearization constraint for v | ||
@constraint(second_level, ν[i, j] ≤ μ[i, j]) | ||
# second linearization constraint for v | ||
@constraint(second_level, ν[i, j] ≤ δ[i, j]) | ||
if i == source | ||
# cut constraint for edge (i,j) when i is the source | ||
if j != target | ||
@constraint(second_level, δ[i, j] + σ[j] ≥ 1 ) | ||
else | ||
@constraint(second_level, δ[i, j] ≥ 1 ) | ||
end | ||
elseif j == target | ||
# cut constraint for edge (i,j) when j is the destination | ||
if i != source | ||
@constraint(second_level, δ[i, j] - σ[i] ≥ 0 ) | ||
end | ||
else | ||
# cut constraint for edge (i,j) in the remaining cases | ||
@constraint(second_level, δ[i, j] + σ[j] - σ[i] ≥ 0) | ||
end | ||
end | ||
|
||
# constraint on the upper bound for the number of attacks | ||
@constraint(second_level, sum{μ[i, j], i = 1:n, j = 1:n} ≤ attacks) | ||
|
||
return second_level, δ, ν | ||
end | ||
|
||
function bilevel_adaptive_arc{T<:AbstractFloat}( | ||
flow_graph::DiGraph, # the input graph | ||
source::Int, # the source vertex | ||
target::Int, # the target vertex | ||
capacity_matrix::AbstractArray{T, 2}, # edge flow capacities | ||
attacks::Int, # argument for attacks | ||
solver::AbstractMathProgSolver, # keyword for solver | ||
rtol::T, # relative tolerance | ||
atol::T, # absolute tolerance | ||
time_limit::Float64 # time limit (seconds) | ||
) | ||
start_time = time() # time stamp (seconds) | ||
n = nv(flow_graph) # size of the network | ||
lower_bound = 0. | ||
upper_bound = 0. | ||
|
||
# Initialization : first level | ||
first_level, x, z = | ||
init_first_level_arc(flow_graph, source, target, capacity_matrix, solver) | ||
|
||
# Loop over the first level while adding cuts from the second level | ||
while solve(first_level) ≠ :Infeasible && time() - start_time < time_limit | ||
# Store first level results | ||
upper_bound, x_value = getobjectivevalue(first_level), getvalue(x) | ||
|
||
# Initialization : second level | ||
second_level, δ, ν = | ||
init_second_level_arc(flow_graph, source, target, capacity_matrix, | ||
attacks, solver, x) | ||
|
||
# Solve second level and update lower_bound | ||
solve(second_level) | ||
lower_bound = getobjectivevalue(second_level) | ||
|
||
# If the bounds are tight, exit the loop | ||
(isapprox(upper_bound, lower_bound, rtol = rtol, atol = atol)) && break | ||
|
||
# Otherwise add the new cut to the first level | ||
δ_value = getvalue(δ) | ||
ν_value = getvalue(ν) | ||
@constraint(first_level, z ≤ sum{ | ||
δ_value[i, j] * x[i, j] - ν_value[i, j] * x[i, j], i = 1:n, j = 1:n; | ||
capacity_matrix[i, j] > 0}) | ||
end | ||
|
||
# Return objective value and elapsed time | ||
return lower_bound, upper_bound, time() - start_time | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
function bilevel_adaptive_path{T<:AbstractFloat}( | ||
flow_graph::DiGraph, # the input graph | ||
source::Int, # the source vertex | ||
target::Int, # the target vertex | ||
capacity_matrix::AbstractArray{T, 2}, # edge flow capacities | ||
attacks::Int, # argument for attacks | ||
solver::AbstractMathProgSolver, # keyword for solver | ||
rtol::T, # absolute tolerance | ||
atol::T, # relative tolerance | ||
time_limit::Float64 # time limit | ||
) | ||
start_time = time() # time stamp | ||
n = nv(flow_graph) # size of the network | ||
lower_bound = 0. | ||
|
||
# TODO: Write the proper algorithm | ||
|
||
# Return objective value and elapsed time | ||
return lower_bound, time() - start_time | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this indentation is a bit weird.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be OK? I was trying to make the term in the sum readable!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specifically lines 118-119. and lines 125-126
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, the problem does not appear on my editor (anymore maybe). I will update PR and check if the indentation problem repeats.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, this indentation thing is really odd. It appears on GitHub but not on my editor. I will correct it tomorrow with using a basic text editor. Sorry for the delay.