Skip to content

Commit 24c2d72

Browse files
Better drawing function interface
1 parent c2ff003 commit 24c2d72

File tree

7 files changed

+46
-33
lines changed

7 files changed

+46
-33
lines changed

docs/utils.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ mpe_model.solve()
2929
# Draw the solution
3030
if mpe_model.is_solved():
3131
solution = mpe_model.get_solution()
32-
fp.utils.draw_solution_basic(
32+
fp.utils.draw_solution(
3333
graph=graph,
34+
filename="simple_graph.pdf",
3435
flow_attr="flow",
3536
paths=solution["paths"],
3637
weights=solution["weights"],
37-
id=graph.graph["id"], # this will be used as filename
3838
draw_options={
3939
"show_graph_edges": True,
4040
"show_edge_weights": False,
@@ -44,7 +44,7 @@ if mpe_model.is_solved():
4444
})
4545
```
4646

47-
This produces two files: one with extension `.dot` storing the source of the dot graph, and one `.pdf` storing the PDF image of the graph.
47+
This produces a file with extension `.pdf` storing the PDF image of the graph.
4848

4949
!!! warning "Graphviz dependency"
5050
Drawing graphs as above requires the Python package `graphviz`. Install via:

examples/min_flow_decomp.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ def main():
5252
def process_solution(model: fp.MinFlowDecomp):
5353
if model.is_solved():
5454
print(model.get_solution())
55-
# fp.utils.draw_solution_basic(model.G, flow_attr="flow", paths = solution["paths"], weights = solution["weights"], id = model.G.graph["id"])
5655
else:
5756
print("Model could not be solved.")
5857

examples/node_weights_mfd.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ def process_expanded_solution(neGraph: fp.NodeExpandedDiGraph, model: fp.MinFlow
111111
print("Expanded paths:", expanded_paths)
112112
print("Original paths:", original_paths)
113113
print("Weights:", solution["weights"])
114-
# fp.utils.draw_solution_basic(model.G, flow_attr="flow", paths = solution["paths"], weights = solution["weights"], id = model.G.graph["id"])
115114
else:
116115
print("Model could not be solved.")
117116

examples/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@
2222
# Draw the solution
2323
if mpe_model.is_solved():
2424
solution = mpe_model.get_solution()
25-
fp.utils.draw_solution_basic(
25+
fp.utils.draw_solution(
2626
graph=graph,
27+
filename="simple_graph.pdf",
2728
flow_attr="flow",
2829
paths=solution["paths"],
2930
weights=solution["weights"],
30-
id=graph.graph["id"],
3131
draw_options={
3232
"show_graph_edges": True,
3333
"show_edge_weights": False,

flowpaths/utils/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
from .graphutils import min_cost_flow
22
from .graphutils import max_bottleneck_path
33
from .graphutils import check_flow_conservation
4-
from .graphutils import draw_solution_basic
4+
from .graphutils import draw_solution
55

66
__all__ = [
77
"min_cost_flow",
88
"max_bottleneck_path",
99
"check_flow_conservation",
10-
"draw_solution_basic",
10+
"draw_solution",
1111
]

flowpaths/utils/graphutils.py

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -220,21 +220,22 @@ def max_occurrence(seq, paths_in_DAG, edge_lengths: dict = {}) -> int:
220220

221221
return max_occurence
222222

223-
def draw_solution_basic(
223+
def draw_solution(
224224
graph: nx.DiGraph,
225+
filename: str,
225226
flow_attr: str = None,
226227
paths: list = [],
227228
weights: list = [],
228-
id: str = "",
229+
additional_starts: list = [],
230+
additional_ends: list = [],
231+
subpath_constraints: list = [],
229232
draw_options: dict = {
230233
"show_graph_edges": True,
231234
"show_edge_weights": False,
232235
"show_path_weights": False,
233236
"show_path_weight_on_first_edge": True,
234237
"pathwidth": 3.0
235238
},
236-
additional_starts: list = [],
237-
additional_ends: list = [],
238239
):
239240
"""
240241
Draw the graph with the paths and their weights highlighted.
@@ -246,21 +247,33 @@ def draw_solution_basic(
246247
247248
The input directed acyclic graph, as networkx DiGraph.
248249
250+
- `filename`: str
251+
252+
The name of the file to save the drawing. The file type is inferred from the extension. Supported extensions are '.bmp', '.canon', '.cgimage', '.cmap', '.cmapx', '.cmapx_np', '.dot', '.dot_json', '.eps', '.exr', '.fig', '.gd', '.gd2', '.gif', '.gtk', '.gv', '.ico', '.imap', '.imap_np', '.ismap', '.jp2', '.jpe', '.jpeg', '.jpg', '.json', '.json0', '.pct', '.pdf', '.pic', '.pict', '.plain', '.plain-ext', '.png', '.pov', '.ps', '.ps2', '.psd', '.sgi', '.svg', '.svgz', '.tga', '.tif', '.tiff', '.tk', '.vml', '.vmlz', '.vrml', '.wbmp', '.webp', '.x11', '.xdot', '.xdot1.2', '.xdot1.4', '.xdot_json', '.xlib'
253+
249254
- `flow_attr`: str
250255
251-
The attribute name from where to get the flow values on the edges.
256+
The attribute name from where to get the flow values on the edges. Default is an empty string, in which case no edge weights are shown.
252257
253258
- `paths`: list
254259
255-
The list of paths to highlight, as lists of nodes. Default is an empty list, in which case no path is drawn.
260+
The list of paths to highlight, as lists of nodes. Default is an empty list, in which case no path is drawn. Default is an empty list.
256261
257262
- `weights`: list
258263
259-
The list of weights corresponding to the paths. Default is an empty list, in which case no path is drawn.
264+
The list of weights corresponding to the paths, of various colors. Default is an empty list, in which case no path is drawn.
260265
261-
- `id`: str
266+
- `additional_starts`: list
267+
268+
A list of additional nodes to highlight in green as starting nodes. Default is an empty list.
269+
270+
- `additional_ends`: list
271+
272+
A list of additional nodes to highlight in red as ending nodes. Default is an empty list.
262273
263-
The identifier of the graph, to be used as filename of the file containing the drawings. Default is an empty string, in which case the object id of the graph object will be used.
274+
- `subpath_constraints`: list
275+
276+
A list of subpaths to highlight in the graph as dashed edges, of various colors. Each subpath is a list of edges. Default is an empty list. There is no association between the subpath colors and the path colors.
264277
265278
- `draw_options`: dict
266279
@@ -286,18 +299,8 @@ def draw_solution_basic(
286299
287300
The width of the path to be drawn. Default is `3.0`.
288301
289-
- `additional_starts`: list
290-
291-
A list of additional nodes to highlight in green as starting nodes. Default is an empty list.
292-
293-
- `additional_ends`: list
294-
295-
A list of additional nodes to highlight in red as ending nodes. Default is an empty list.
296302
"""
297303

298-
if id == "":
299-
id = id(graph)
300-
301304
if len(paths) != len(weights):
302305
raise ValueError("Paths and weights must have the same length, if provided.")
303306

@@ -376,15 +379,27 @@ def draw_solution_basic(
376379
dot.edge(
377380
str(path[i]),
378381
str(path[i + 1]),
379-
fontcolor=pathColor,
380382
color=pathColor,
381383
penwidth=str(draw_options.get("pathwidth", 3.0)),
382384
)
385+
386+
for index, path in enumerate(subpath_constraints):
387+
pathColor = colors[index % len(colors)]
388+
for i in range(len(path)):
389+
if len(path[i]) != 2:
390+
raise ValueError("Subpaths must be lists of edges.")
391+
dot.edge(
392+
str(path[i][0]),
393+
str(path[i][1]),
394+
color=pathColor,
395+
style="dashed",
396+
penwidth="2.0"
397+
)
383398

384399
if len(path) == 1:
385400
dot.node(str(path[0]), color=pathColor, penwidth=str(draw_options.get("pathwidth", 3.0)))
386-
387-
dot.render(f"{id}.dot", view=False)
401+
402+
dot.render(outfile=filename, view=False, cleanup=True)
388403

389404
except ImportError:
390405
raise ImportError("graphviz module not found. Please install it via pip (pip install graphviz).")
@@ -420,7 +435,7 @@ def get_subgraph_between_topological_nodes(graph: nx.DiGraph, topo_order: list,
420435

421436
return subgraph
422437

423-
def draw_solution(graph: nx.DiGraph, paths: list, weights: list, id:str):
438+
def draw_solution_WIP(graph: nx.DiGraph, paths: list, weights: list, id:str):
424439

425440
import matplotlib.pyplot as plt
426441
import pydot

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "flowpaths"
3-
version = "0.1.10"
3+
version = "0.1.11"
44
description = "A Python package to quickly decompose weighted graphs into weights paths, under various models."
55
readme = "README.md"
66
authors = [{name="Graph Algorithms and Bioinformatics Group @ University of Helsinki, and external collaborators"}]

0 commit comments

Comments
 (0)