-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.py
More file actions
151 lines (137 loc) · 5.83 KB
/
Copy pathutils.py
File metadata and controls
151 lines (137 loc) · 5.83 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
151
import networkx as nx
def check_valid_multi_flow(G: nx.Graph, num_flows: int, perfect_flow: str = "perfect", flow_attr: str = "flow", subpath_constr: list = []) -> bool:
if not nx.is_directed_acyclic_graph(G):
print("uh oh")
raise ValueError('Input graph is not a directed acyclic graph')
return False
if not check_st_graph(G):
print("uh oh")
raise ValueError('Input graph is not an st graph')
return False
if not check_correct_num_flows(G, num_flows, flow_attr):
print("uh oh")
raise ValueError('Number of flows does not match')
return False
if perfect_flow and not check_multi_flow_conservation(G, num_flows, flow_attr):
print("uh oh")
raise ValueError('Input graph does not conserve flow')
return False
if subpath_constr and not check_subpath_constr(G, subpath_constr):
print("uh oh")
return False
return True
def check_st_graph(G: nx.DiGraph) -> bool:
if not nx.is_directed_acyclic_graph(G): return False
single_source = False
single_sink = False
for v in G.nodes():
if G.in_degree(v) == 0:
if single_source:
return False
else:
single_source = True
continue
if G.out_degree(v) == 0:
if single_sink:
return False
else:
single_sink = True
continue
return single_source and single_sink
def check_correct_num_flows(G: nx.DiGraph, num_flows: int, flow_attr: str = "flow") -> bool:
for u, v, data in G.edges(data = True):
if len(data.get(flow_attr)) != num_flows:
return False
return True
def check_valid_flow_format(G: nx.DiGraph, num_flows: int, flow_attr: str = "flow") -> bool:
for u, v, data in G.edges(data = True):
if not all(isinstance(flow_val, (int, float)) for flow_val in data.get(flow_attr)):
return False
return True
def check_multi_flow_conservation(G: nx.DiGraph, num_flows: int, flow_attr: str = "flow") -> bool:
for v in G.nodes():
if G.out_degree(v) == 0 or G.in_degree(v) == 0:
continue
for j in range(num_flows):
out_flow = 0
for x, y, data in G.out_edges(v, data=True):
out_flow += data[flow_attr][j]
in_flow = 0
for x, y, data in G.in_edges(v, data=True):
in_flow += data[flow_attr][j]
if out_flow != in_flow:
return False
return True
def check_subpath_constr(G: nx.DiGraph, subpath_constr: list) -> bool:
if not isinstance(subpath_constr, list) or not all(
isinstance(subpath, list) and all(isinstance(item, str) for item in subpath)
for subpath in subpath_constr
):
raise ValueError("data must be a list of lists of strings")
return False
for subpath in subpath_constr:
for i in range(1,len(subpath)):
if not G.has_edge(subpath[i-1], subpath[i]):
raise ValueError("subpaths must be connected")
return False
return True
def check_valid_inexact_flows(G: nx.DiGraph, num_flows: int, flow_attr: str = "flow") -> bool:
for u,v,data in G.edges(data=True):
for j in range(num_flows):
if not isinstance(data.get(flow_attr)[j], tuple):
raise ValueError("Flow attributes must be bounds expressed as tuples")
return False
if len(data.get(flow_attr)[j]) != 2:
raise ValueError("There must be a single upper and lower bound for each edge flow value")
return False
if not data.get(flow_attr)[j][0] <= data.get(flow_attr)[j][1]:
raise ValueError("Lower bound must be less than or equal to upper bound for each edge flow value")
return False
return True
def get_max_flow(G: nx.DiGraph, num_flows: int, flow_attr: str = "flow") -> int:
w_max = float("-inf")
if not check_correct_num_flows(G, num_flows, flow_attr):
print("uh oh")
raise ValueError(
"Some edges missing flows"
)
for u, v, data in G.edges(data=True):
if not flow_attr in data:
print("uh oh")
raise ValueError(
f"Edge ({u},{v}) does not have the required flow attribute '{flow_attr}'. Check that the attribute passed under 'flow_attr' is present in the edge data."
)
if any(flow < 0 for flow in data[flow_attr]):
print("uh oh")
raise ValueError(
f"Edge ({u},{v}) has negative flow value {data[flow_attr]}. All flow values must be >=0."
)
w_max = max(w_max, max(data[flow_attr]))
return w_max
def get_max_inexact_flow(G: nx.DiGraph, num_flows: int, flow_attr: str = "flow") -> int:
w_max = float("-inf")
if not check_correct_num_flows(G, num_flows, flow_attr):
print("uh oh")
raise ValueError(
"Some edges missing flows"
)
for u, v, data in G.edges(data=True):
if not flow_attr in data:
print("uh oh")
raise ValueError(
f"Edge ({u},{v}) does not have the required flow attribute '{flow_attr}'. Check that the attribute passed under 'flow_attr' is present in the edge data."
)
if any(flow[0] < 0 for flow in data[flow_attr]):
print("uh oh")
raise ValueError(
f"Edge ({u},{v}) has negative flow value {data[flow_attr]}. All flow values must be >=0."
)
w_max = max(w_max, max(data[flow_attr][1]))
return w_max
def is_decomposed(flow_network, num_flows, source):
edges = flow_network.edges(source)
for u, v in edges:
for i in range(0, num_flows):
if flow_network[u][v]['flow'][i] != 0:
return False
return True