-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathattack_routing_solver.py
More file actions
150 lines (120 loc) · 5.25 KB
/
attack_routing_solver.py
File metadata and controls
150 lines (120 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
'''
Attack routing solver:
Optimizing for the Optimal Attack problem with the rate of attacks 'nu' fixed
'''
import cplex_interface
from cvxopt import matrix, spmatrix, sparse, spdiag, solvers
import numpy as np
__author__ = 'jeromethai'
class AttackRoutingSolver:
# class for computing the best attacks with the rate of attacks fixed
def __init__(self, network, attack_rates, k, full_adj=True, eps=1e-8, cplex=True):
self.network = network
self.nu = attack_rates # fixed attack rate
self.phi = self.network.rates # rates before the attacks
self.delta = network.routing # routing prob. before attacks
self.k = k # a_k is set to 1
self.eps = eps
self.cplex = cplex
self.N = network.size
self.w = network.weights # weights for the availabilities in the obj
self.adj = network.full_adjacency if full_adj else network.adjacency
self.check()
def check(self):
# check if the attack_rates are valid
assert self.nu.shape[0] == self.N, 'attack rates do not match network size'
assert np.min(self.nu) >= 0.0, 'attack rates are negative'
def solve(self):
# solver for the attack routing
# solver = self.cplex_solver if self.cplex else self.cvxopt_solver
#flow = solver()
flow = self.cplex_solver()
return self.flow_to_availabilities_routing(flow)
# deprecated
def cvxopt_solver(self):
c = matrix(np.repeat(self.w, self.N))
b, A = self.constraints()
# lp solver here
x = np.squeeze(solvers.lp(c,A,b)['x'])
return x.reshape((self.N, self.N))
def cplex_solver(self):
open('tmp.lp', 'w').write(self.to_cplex_lp_file())
variables, sols = cplex_interface.solve_from_file('tmp.lp', 'o')
# reconstruct flow matrix from 'sols' returned by cplex solver
non_zeros = np.where(sols)
flow = np.zeros((self.N, self.N))
for i in non_zeros[0]:
if variables[i][0] == 'a': continue
a,b = [int(j) for j in variables[i][2:].split('_')]
flow[a,b] = sols[i]
return flow
def constraints(self):
# construct the constraints for the attack routing problem
N = self.N
u = np.tile(range(N), N)
v = np.repeat(range(N),N)
w = np.array(range(N*N))
# build constraint matrix
A1 = spmatrix(np.repeat(self.nu, N), u, w, (N, N*N))
A2 = -spmatrix(np.repeat(self.nu + self.phi, N), v, w, (N, N*N))
I = np.array(range(N))
J = I + np.array(range(N)) * N
A3 = spmatrix(self.phi, I, J, (N, N*N))
tmp = np.dot(np.diag(self.phi), self.delta).transpose()
A4 = matrix(np.repeat(tmp, N, axis=1))
A5 = -spmatrix(tmp.flatten(), v, np.tile(J, N), (N, N*N))
A6 = A1 + A2 + A3 + A4 + A5
I = np.array([0]*(N-1))
J = np.array(range(self.k)+range((self.k+1),N)) + N * self.k
A7 = spmatrix(1., I, J, (1, N*N))
A = sparse([[A6, -A6, A7, -A7, -spdiag([1.]*(N*N))]])
tmp = np.zeros(2*N + 2 + N*N)
tmp[2*N] = 1.
tmp[2*N + 1] = -1.
b = matrix(tmp)
return b, A
def flow_to_availabilities_routing(self, flow):
# convert the flow solution of the min cost flow problem back to
# availabilities and routing probabilities
flow[range(self.N), range(self.N)] = 0.0
# availabilities
avail = np.sum(flow, 1)
assert np.min(avail) >= self.eps, \
'min avail. too small: {} < {}'.format(np.min(avail), self.eps)
# routing probabilities for the attacks
tmp = np.divide(np.ones((self.N,)), avail)
opt_routing = np.dot(np.diag(tmp), flow)
return avail, opt_routing
def to_cplex_lp_file(self):
# generate input file for CPLEX solver
# http://lpsolve.sourceforge.net/5.5/CPLEX-format.htm
lam = self.phi + self.nu
N = self.N
tmp = np.dot(np.diag(self.phi), self.delta).transpose()
obj = ' + '.join(['{} a_{}'.format(self.w[i], i)
for i in range(self.k) + range(self.k+1, N)])
# equality constraints
cst = []
for i in range(self.k) + range(self.k + 1, N):
eqn = []
for j in range(i) + range(i+1, N):
if self.adj[i,j] == 0.: continue
eqn.append('{0} y_{3}_{2} - {1} y_{2}_{3}'\
.format(self.nu[j], lam[i], i, j))
if j != self.k:
eqn.append('{} a_{}'.format(tmp[i,j], j))
cst.append('{} = - {}'.format(' + '.join(eqn), tmp[i,self.k]))
# constraints on the a_i
for i in range(N):
eqn = ' + '.join(['y_{}_{}'.format(i,j)
for j in range(i) + range(i+1, N)
if self.adj[i,j] == 1.])
end = '= 1.0' if i == self.k else '- a_{} = 0.0'
cst.append(eqn + end.format(i))
cst = '\n '.join(cst)
# bounds
bnd = '\n '.join(['0 <= y_{}_{}'.format(i,j)
for i in range(N)
for j in range(i) + range(i+1, N)
if self.adj[i,j]== 1.0])
return cplex_interface.template.format(obj, cst, bnd)