Skip to content

Commit f5f8e18

Browse files
Flow correction now works also for graphs with cycles
1 parent 4eec55a commit f5f8e18

File tree

3 files changed

+70
-12
lines changed

3 files changed

+70
-12
lines changed

examples/min_error_flow_cycles.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import flowpaths as fp
2+
import networkx as nx
3+
4+
def process_solution(graph: nx.DiGraph, model: fp.MinErrorFlow):
5+
if model.is_solved():
6+
solution = model.get_solution()
7+
print(solution["graph"].edges(data=True))
8+
print("error", solution["error"])
9+
print("objective_value", solution["objective_value"])
10+
fp.utils.draw_solution(graph, filename=f"uncorrected_graph_{id(model.get_corrected_graph())}.pdf", flow_attr="flow", draw_options={"show_edge_weights": True})
11+
fp.utils.draw_solution(solution["graph"], filename=f"corrected_graph_{id(model.get_corrected_graph())}.pdf", flow_attr="flow", draw_options={"show_edge_weights": True})
12+
else:
13+
print("Model could not be solved.")
14+
15+
graph = nx.DiGraph()
16+
graph.add_edge("s", "a", flow=7)
17+
graph.add_edge("s", "b", flow=7)
18+
graph.add_edge("a", "b", flow=2)
19+
graph.add_edge("a", "c", flow=4)
20+
graph.add_edge("b", "c", flow=9)
21+
graph.add_edge("c", "d", flow=7)
22+
graph.add_edge("c", "t", flow=7)
23+
graph.add_edge("d", "t", flow=6)
24+
graph.add_edge("t", "s", flow=15)
25+
26+
# We create a the Minimum Error Flow solver with default settings
27+
correction_model = fp.MinErrorFlow(graph, flow_attr="flow")
28+
correction_model.solve()
29+
30+
if correction_model.is_solved():
31+
corrected_graph = correction_model.get_corrected_graph()
32+
process_solution(graph, correction_model)
33+
34+
# We create a the Minimum Error Flow solver, by also requiring that
35+
# the number of different flow values used in the correction is small
36+
# (parameter different_flow_values_epsilon)
37+
correction_model_diff_flow_values = fp.MinErrorFlow(
38+
graph,
39+
flow_attr="flow",
40+
weight_type=int,
41+
few_flow_values_epsilon = 0.25,
42+
)
43+
correction_model_diff_flow_values.solve()
44+
45+
# We process its solution
46+
process_solution(graph, correction_model_diff_flow_values)
47+

flowpaths/minerrorflow.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,27 @@ def __init__(
8181
"""
8282

8383
self.original_graph_copy = deepcopy(G)
84-
self.G = stdigraph.stDiGraph(G, additional_starts=additional_starts, additional_ends=additional_ends)
84+
self.sparsity_lambda = sparsity_lambda
85+
86+
if nx.is_directed_acyclic_graph(G):
87+
self.is_acyclic = True
88+
self.G = stdigraph.stDiGraph(G, additional_starts=additional_starts, additional_ends=additional_ends)
89+
self.edges_to_ignore = set(edges_to_ignore).union(self.G.source_sink_edges)
90+
else:
91+
self.G = G
92+
self.is_acyclic = False
93+
self.edges_to_ignore = set(edges_to_ignore)
94+
if self.sparsity_lambda != 0:
95+
utils.logger.error(f"{__name__}: You cannot set sparsity_lambda != 0 for a graph with cycles.")
96+
raise ValueError(f"You cannot set sparsity_lambda != 0 for a graph with cycles.")
97+
8598
self.flow_attr = flow_attr
8699
if weight_type not in [int, float]:
87100
utils.logger.error(f"{__name__}: weight_type must be either int or float, not {weight_type}")
88101
raise ValueError(f"weight_type must be either int or float, not {weight_type}")
89102
self.weight_type = weight_type
90103
self.solver_options = solver_options
91-
92-
self.sparsity_lambda = sparsity_lambda
93-
self.edges_to_ignore = set(edges_to_ignore).union(self.G.source_sink_edges)
104+
94105
self.edge_error_scaling = edge_error_scaling
95106
# Checking that every entry in self.edge_error_scaling is between 0 and 1
96107
for key, value in self.edge_error_scaling.items():
@@ -156,7 +167,7 @@ def __encode_flow(self):
156167

157168
# Adding flow conservation constraints
158169
for node in self.G.nodes():
159-
if node in [self.G.source, self.G.sink]:
170+
if self.G.in_degree(node) == 0 or self.G.out_degree(node) == 0:
160171
continue
161172
# Flow conservation constraint
162173
self.solver.add_constraint(
@@ -212,9 +223,9 @@ def __encode_min_sum_errors_objective(self):
212223
self.edge_error_vars[(u, v)] * self.edge_error_scaling.get((u, v), 1)
213224
for (u, v) in self.G.edges()
214225
if (u, v) not in self.edges_to_ignore
215-
) + self.sparsity_lambda * self.solver.quicksum(
226+
) + (self.sparsity_lambda * self.solver.quicksum(
216227
self.edge_vars[(u, v)]
217-
for (u, v) in self.G.out_edges(self.G.source)
228+
for (u, v) in self.G.out_edges(self.G.source)) if self.sparsity_lambda > 0 else 0
218229
),
219230
sense="minimize",
220231
)
@@ -286,9 +297,9 @@ def __encode_different_flow_values_and_objective(
286297
self.edge_error_vars[(u, v)] * self.edge_error_scaling.get((u, v), 1)
287298
for (u, v) in self.G.edges()
288299
if (u, v) not in self.edges_to_ignore
289-
) + self.sparsity_lambda * self.solver.quicksum(
300+
) + (self.sparsity_lambda * self.solver.quicksum(
290301
self.edge_vars[(u, v)]
291-
for (u, v) in self.G.out_edges(self.G.source)
302+
for (u, v) in self.G.out_edges(self.G.source)) if self.sparsity_lambda > 0 else 0
292303
) <= (1 + self.different_flow_values_epsilon) * objective_value,
293304
name="epsilon_constraint",
294305
)

mkdocs.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,23 @@ theme:
3939
- media: "(prefers-color-scheme)"
4040
primary: black
4141
toggle:
42-
icon: material/brightness-auto
42+
icon: material/lightbulb-auto
4343
name: Switch to light mode
4444

4545
# Palette toggle for light mode
4646
- media: "(prefers-color-scheme: light)"
4747
primary: black
4848
scheme: default
4949
toggle:
50-
icon: material/brightness-7
50+
icon: material/lightbulb
5151
name: Switch to dark mode
5252

5353
# Palette toggle for dark mode
5454
- media: "(prefers-color-scheme: dark)"
5555
primary: black
5656
scheme: slate
5757
toggle:
58-
icon: material/brightness-4
58+
icon: material/lightbulb-outline
5959
name: Switch to system preference
6060

6161
features:

0 commit comments

Comments
 (0)