bigraph-viz
Advanced tools
| Metadata-Version: 2.1 | ||
| Name: bigraph-viz | ||
| Version: 0.1.3 | ||
| Version: 0.1.5 | ||
| Summary: A graphviz-based plotting tool for compositional bigraph schema | ||
@@ -8,4 +8,2 @@ Home-page: https://github.com/vivarium-collective/bigraph-viz | ||
| Author-email: agmon.eran@gmail.com | ||
| License: UNKNOWN | ||
| Platform: UNKNOWN | ||
| Classifier: Development Status :: 3 - Alpha | ||
@@ -95,3 +93,1 @@ Classifier: Intended Audience :: Developers | ||
| Bigraph-viz is open-source software released under the [Apache 2 License](https://github.com/vivarium-collective/bigraph-viz/blob/main/LICENSE). | ||
| import os | ||
| import inspect | ||
| import graphviz | ||
| import numpy as np | ||
| from bigraph_schema import TypeSystem, is_schema_key | ||
| from bigraph_schema import TypeSystem, is_schema_key, hierarchy_depth | ||
| from bigraph_viz.dict_utils import absolute_path | ||
@@ -40,2 +41,3 @@ | ||
| wires = wires or {} | ||
| ports_schema = ports_schema or {} | ||
| inferred_ports = set(list(ports_schema.keys()) + list(wires.keys())) | ||
@@ -61,17 +63,9 @@ | ||
| elif isinstance(wire, (list, tuple, str)): | ||
| # the wire is defined, add it to edges | ||
| if isinstance(wire, str): | ||
| wire = [wire] | ||
| target_path = absolute_path(edge_path[:-1], tuple(wire)) # TODO -- make sure this resolves ".." | ||
| if schema_key == 'inputs': | ||
| edge_key = 'input_edges' | ||
| elif schema_key == 'outputs': | ||
| edge_key = 'output_edges' | ||
| else: | ||
| raise Exception(f'invalid schema key {schema_key}') | ||
| graph_dict[edge_key].append({ | ||
| 'edge_path': edge_path, | ||
| 'target_path': target_path, | ||
| 'port': port, | ||
| 'type': schema_key}) | ||
| graph_dict = get_single_wire(edge_path, graph_dict, port, schema_key, wire) | ||
| elif isinstance(wire, dict): | ||
| flat_wires = hierarchy_depth(wires) | ||
| for subpath, subwire in flat_wires.items(): | ||
| subport = '/'.join(subpath) | ||
| graph_dict = get_single_wire(edge_path, graph_dict, subport, schema_key, subwire) | ||
| else: | ||
@@ -98,2 +92,26 @@ raise ValueError(f"Unexpected wire type: {wires}") | ||
| def get_single_wire(edge_path, graph_dict, port, schema_key, wire): | ||
| # the wire is defined, add it to edges | ||
| if isinstance(wire, str): | ||
| wire = [wire] | ||
| elif isinstance(wire, (list, tuple)): | ||
| # only use strings in the wire | ||
| # TODO -- make this more general so it only skips integers if they go into an array | ||
| wire = [item for item in wire if isinstance(item, str)] | ||
| target_path = absolute_path(edge_path[:-1], tuple(wire)) # TODO -- make sure this resolves ".." | ||
| if schema_key == 'inputs': | ||
| edge_key = 'input_edges' | ||
| elif schema_key == 'outputs': | ||
| edge_key = 'output_edges' | ||
| else: | ||
| raise Exception(f'invalid schema key {schema_key}') | ||
| graph_dict[edge_key].append({ | ||
| 'edge_path': edge_path, | ||
| 'target_path': target_path, | ||
| 'port': port, | ||
| 'type': schema_key}) | ||
| return graph_dict | ||
| def plot_edges( | ||
@@ -168,6 +186,9 @@ graph, | ||
| node_label_size='12pt', | ||
| process_label_size=None, | ||
| size='16,10', | ||
| rankdir='TB', | ||
| aspect_ratio='auto', # 'compress', 'expand', 'auto', 'fill' | ||
| dpi='70', | ||
| significant_digits=2, | ||
| undirected_edges=False, | ||
| show_values=False, | ||
@@ -187,8 +208,9 @@ show_types=False, | ||
| invisible_edges = invisible_edges or [] | ||
| process_label_size = process_label_size or node_label_size | ||
| # node specs | ||
| state_node_spec = { | ||
| 'shape': 'circle', 'penwidth': '2', 'margin': label_margin, 'fontsize': node_label_size} | ||
| 'shape': 'circle', 'penwidth': '2', 'constraint': 'false', 'margin': label_margin, 'fontsize': node_label_size} | ||
| process_node_spec = { | ||
| 'shape': 'box', 'penwidth': '2', 'constraint': 'false', 'margin': label_margin, 'fontsize': node_label_size} | ||
| 'shape': 'box', 'penwidth': '2', 'constraint': 'false', 'margin': label_margin, 'fontsize': process_label_size} | ||
| input_edge_spec = { | ||
@@ -198,6 +220,16 @@ 'style': 'dashed', 'penwidth': '1', 'arrowhead': 'normal', 'arrowsize': '1.0', 'dir': 'forward'} | ||
| 'style': 'dashed', 'penwidth': '1', 'arrowhead': 'normal', 'arrowsize': '1.0', 'dir': 'back'} | ||
| bidirectional_edge_spec = { | ||
| 'style': 'dashed', 'penwidth': '1', 'arrowhead': 'normal', 'arrowsize': '1.0', 'dir': 'both'} | ||
| if undirected_edges: | ||
| input_edge_spec['dir'] = 'none' | ||
| output_edge_spec['dir'] = 'none' | ||
| bidirectional_edge_spec['dir'] = 'none' | ||
| # initialize graph | ||
| graph = graphviz.Digraph(name='bigraph', engine='dot') | ||
| graph.attr(size=size, overlap='false', rankdir=rankdir, dpi=dpi) | ||
| graph.attr(size=size, overlap='false', rankdir=rankdir, dpi=dpi, | ||
| ratio=aspect_ratio, # "fill", | ||
| splines = 'true', | ||
| ) | ||
@@ -257,2 +289,14 @@ # state nodes | ||
| plot_edges(graph, edge, port_labels, port_label_size, state_node_spec, constraint='true') | ||
| # bidirectional edges | ||
| for edge in graph_dict['bidirectional_edges']: | ||
| if 'bridge_outputs' not in edge['type'] and 'bridge_inputs' not in edge['type']: | ||
| graph.attr('edge', **bidirectional_edge_spec) | ||
| plot_edges(graph, edge, port_labels, port_label_size, state_node_spec, constraint='true') | ||
| else: | ||
| if 'bridge_outputs' in edge['type']: | ||
| graph.attr('edge', **input_edge_spec) # reverse arrow direction to go from store to composite | ||
| plot_edges(graph, edge, port_labels, port_label_size, state_node_spec, constraint='false') | ||
| if 'bridge_inputs' in edge['type']: | ||
| graph.attr('edge', **output_edge_spec) # reverse arrow direction to go from composite to store | ||
| plot_edges(graph, edge, port_labels, port_label_size, state_node_spec, constraint='false') | ||
@@ -290,2 +334,5 @@ # state nodes again | ||
| for group in node_groups: | ||
| # convert lists to tuples | ||
| group = [tuple(item) for item in group] | ||
| group_name = str(group) | ||
@@ -321,2 +368,3 @@ with graph.subgraph(name=group_name) as c: | ||
| filename=None, | ||
| file_format='png', | ||
| **kwargs | ||
@@ -353,2 +401,3 @@ ): | ||
| out_dir=out_dir, | ||
| file_format=file_format, | ||
| options=get_graphviz_kwargs) | ||
@@ -429,2 +478,29 @@ | ||
| # get bidirectional wires | ||
| input_edges_to_remove = [] | ||
| output_edges_to_remove = [] | ||
| for input_edge in graph['input_edges']: | ||
| for output_edge in graph['output_edges']: | ||
| if (input_edge['target_path'] == output_edge['target_path']) and \ | ||
| (input_edge['port'] == output_edge['port']) and \ | ||
| (input_edge['edge_path'] == output_edge['edge_path']): | ||
| graph['bidirectional_edges'].append({ | ||
| 'edge_path': input_edge['edge_path'], | ||
| 'target_path': input_edge['target_path'], | ||
| 'port': input_edge['port'], | ||
| 'type': (input_edge['type'], output_edge['type']), | ||
| # 'type': 'bidirectional' | ||
| }) | ||
| input_edges_to_remove.append(input_edge) | ||
| output_edges_to_remove.append(output_edge) | ||
| break # prevent matching the same input_edge with multiple output_edges | ||
| # Remove matched edges after iteration | ||
| for edge in input_edges_to_remove: | ||
| graph['input_edges'].remove(edge) | ||
| for edge in output_edges_to_remove: | ||
| graph['output_edges'].remove(edge) | ||
| # get the input and output bridge wires | ||
@@ -474,2 +550,3 @@ if bridge_wires: | ||
| graph) | ||
| return graph | ||
@@ -519,2 +596,3 @@ | ||
| 'output_edges': [], | ||
| 'bidirectional_edges': [], | ||
| 'disconnected_input_edges': [], | ||
@@ -938,43 +1016,138 @@ 'disconnected_output_edges': []} | ||
| '_type': 'composite', | ||
| '_inputs': { | ||
| 'port1': 'any', | ||
| }, | ||
| '_outputs': { | ||
| 'port2': 'any', | ||
| }, | ||
| 'inputs': { | ||
| 'port1': ['external store'], | ||
| }, | ||
| '_inputs': {'port1': 'any'}, | ||
| '_outputs': {'port2': 'any'}, | ||
| 'inputs': {'port1': ['external store']}, | ||
| 'store1': 'any', | ||
| 'store2': 'any', | ||
| 'bridge': { | ||
| 'inputs': { | ||
| 'port1': ['store1'], | ||
| }, | ||
| 'outputs': { | ||
| 'port2': ['store2'], | ||
| } | ||
| }, | ||
| 'inputs': {'port1': ['store1']}, | ||
| 'outputs': {'port2': ['store2']}}, | ||
| 'process1': { | ||
| '_type': 'process', | ||
| '_inputs': { | ||
| 'port3': 'any', | ||
| }, | ||
| '_outputs': { | ||
| 'port4': 'any', | ||
| }, | ||
| 'inputs': { | ||
| 'port3': ['store1'], | ||
| }, | ||
| 'outputs': { | ||
| 'port4': ['store2'], | ||
| } | ||
| }, | ||
| '_inputs': {'port3': 'any'}, | ||
| '_outputs': {'port4': 'any',}, | ||
| 'inputs': {'port3': ['store1']}, | ||
| 'outputs': {'port4': ['store2']}}}} | ||
| plot_bigraph( | ||
| spec, | ||
| core=core, | ||
| filename='composite_process', | ||
| **plot_settings) | ||
| def test_bidirectional_edges(): | ||
| core = VisualizeTypes() | ||
| spec = { | ||
| 'process1': { | ||
| '_type': 'process', | ||
| '_inputs': {'port1': 'any'}, | ||
| '_outputs': {'port1': 'any'}, | ||
| 'inputs': {'port1': ['external store']}, | ||
| 'outputs': {'port1': ['external store']}}, | ||
| 'process2': { | ||
| '_type': 'process', | ||
| '_inputs': {'port3': 'any'}, | ||
| '_outputs': {'port4': 'any'}, | ||
| 'inputs': {'port3': ['external store']}, | ||
| 'outputs': {'port4': ['external store']} | ||
| } | ||
| } | ||
| plot_bigraph( | ||
| spec, | ||
| core=core, | ||
| filename='bidirectional_edges', | ||
| **plot_settings) | ||
| def test_array_paths(): | ||
| core = VisualizeTypes() | ||
| spec = { | ||
| 'dFBA[0,0]': { | ||
| '_type': 'process', | ||
| 'address': 'local:DynamicFBA', | ||
| # 'config': {}, | ||
| 'inputs': { | ||
| 'substrates': { | ||
| 'acetate': ['fields', 'acetate', | ||
| 0, 0 | ||
| ], | ||
| 'biomass': ['fields', 'biomass', | ||
| 0, 0 | ||
| ], | ||
| 'glucose': ['fields', 'glucose', | ||
| 0, 0 | ||
| ]}}, | ||
| 'outputs': { | ||
| 'substrates': { | ||
| 'acetate': ['fields', 'acetate', | ||
| 0, 0 | ||
| ], | ||
| 'biomass': ['fields', 'biomass', | ||
| 0, 0 | ||
| ], | ||
| 'glucose': ['fields', 'glucose', | ||
| 0, 0 | ||
| ]}}, | ||
| }, | ||
| 'dFBA[1,0]': { | ||
| '_type': 'process', | ||
| 'address': 'local:DynamicFBA', | ||
| # 'config': {}, | ||
| 'inputs': { | ||
| 'substrates': { | ||
| 'acetate': ['fields', 'acetate', | ||
| 1, 0 | ||
| ], | ||
| 'biomass': ['fields', 'biomass', | ||
| 1, 0 | ||
| ], | ||
| 'glucose': ['fields', 'glucose', | ||
| 1, 0 | ||
| ]}}, | ||
| 'outputs': { | ||
| 'substrates': { | ||
| 'acetate': ['fields', 'acetate', | ||
| 1, 0 | ||
| ], | ||
| 'biomass': ['fields', 'biomass', | ||
| 1, 0 | ||
| ], | ||
| 'glucose': ['fields', 'glucose', | ||
| 1, 0 | ||
| ]}}, | ||
| }, | ||
| 'fields': { | ||
| 'acetate': np.array([[1.0], [2.0]]), | ||
| 'biomass': np.array([[3.0],[4.0]]), | ||
| 'glucose': np.array([[5.0],[6.0]]) | ||
| } | ||
| } | ||
| schema = { | ||
| 'fields': { | ||
| 'acetate': { | ||
| '_type': 'array', | ||
| '_shape': (2, 1), | ||
| '_data': 'float' | ||
| }, | ||
| 'biomass': { | ||
| '_type': 'array', | ||
| '_shape': (2, 1), | ||
| '_data': 'float', | ||
| }, | ||
| 'glucose': { | ||
| '_type': 'array', | ||
| '_shape': (2, 1), | ||
| '_data': 'float', | ||
| } | ||
| } | ||
| } | ||
| plot_bigraph( | ||
| spec, | ||
| schema=schema, | ||
| core=core, | ||
| filename='composite_process', | ||
| filename='array_paths', | ||
| **plot_settings) | ||
@@ -996,1 +1169,3 @@ | ||
| test_composite_process() | ||
| test_bidirectional_edges() | ||
| test_array_paths() |
+1
-5
| Metadata-Version: 2.1 | ||
| Name: bigraph-viz | ||
| Version: 0.1.3 | ||
| Version: 0.1.5 | ||
| Summary: A graphviz-based plotting tool for compositional bigraph schema | ||
@@ -8,4 +8,2 @@ Home-page: https://github.com/vivarium-collective/bigraph-viz | ||
| Author-email: agmon.eran@gmail.com | ||
| License: UNKNOWN | ||
| Platform: UNKNOWN | ||
| Classifier: Development Status :: 3 - Alpha | ||
@@ -95,3 +93,1 @@ Classifier: Intended Audience :: Developers | ||
| Bigraph-viz is open-source software released under the [Apache 2 License](https://github.com/vivarium-collective/bigraph-viz/blob/main/LICENSE). | ||
+2
-2
@@ -5,3 +5,3 @@ import re | ||
| VERSION = '0.1.3' | ||
| VERSION = '0.1.5' | ||
@@ -18,3 +18,3 @@ | ||
| r']\(([\w/.-]+)\)', | ||
| r'](https://github.com/vivarium-collective/bigraph-viz/blob/main/\1)', | ||
| r'](https://github.com/vivarium-collective/bigraph-viz/blob /main/\1)', | ||
| description2) | ||
@@ -21,0 +21,0 @@ |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
78161
9.34%1448
12.16%