You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

bigraph-viz

Package Overview
Dependencies
Maintainers
2
Versions
48
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bigraph-viz - pypi Package Compare versions

Comparing version
0.1.5
to
0.1.6
+41
bigraph_viz/convert.py
import os
from pdf2image import convert_from_path
from PIL import Image
# Print current working directory
cwd = os.getcwd()
print("Current working directory:", cwd)
# Go up one level in the directory
cwd = os.path.dirname(cwd)
# Input PDF file (single page)
pdf_path = os.path.join(cwd, "notebooks/out/ecoli.pdf")
# Output directory and files
output_dir = os.path.join(cwd, "notebooks/out/")
output_full_res = os.path.join(output_dir, "ecoli_full_res.png")
output_scaled = os.path.join(output_dir, "ecoli_google_slides.png")
# Ensure the output directory exists
os.makedirs(output_dir, exist_ok=True)
# Convert PDF to an image at high DPI
images = convert_from_path(pdf_path, dpi=1200) # Use high DPI for sharpness
if images:
# Save the **full resolution** image first
images[0].save(output_full_res, "PNG")
print(f"Full resolution image saved at: {output_full_res}")
# Resize image to match Google Slides width (3000 px)
target_width = 3000
aspect_ratio = images[0].width / images[0].height
target_height = int(target_width / aspect_ratio)
resized_image = images[0].resize((target_width, target_height), Image.LANCZOS)
resized_image.save(output_scaled, "PNG")
print(f"Resized Google Slides image saved at: {output_scaled}")
else:
print("Error: No images found in PDF!")
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "bigraph-viz"
version = "0.1.6"
description = "A visualization method for displaying the structure of process bigraphs"
readme = "README.md"
requires-python = "==3.12.9"
license = { file = "LICENSE" }
authors = [
{ name = "Eran Agmon", email = "agmon.eran@gmail.com" }
]
maintainers = [
{ name = "Eran Agmon", email = "agmon.eran@gmail.com" }
]
keywords = ["visualization", "systems biology", "bigraph", "graphviz", "vivarium"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Topic :: Scientific/Engineering :: Visualization",
"Topic :: Software Development :: Libraries"
]
dependencies = [
"bigraph-schema",
"graphviz"
]
[tool.setuptools]
packages = ["bigraph_viz"]
[tool.uv.sources]
bigraph-schema = { path = "../bigraph-schema", editable = true }
+5
-1
Metadata-Version: 2.1
Name: bigraph-viz
Version: 0.1.5
Version: 0.1.6
Summary: A graphviz-based plotting tool for compositional bigraph schema

@@ -8,2 +8,4 @@ Home-page: https://github.com/vivarium-collective/bigraph-viz

Author-email: agmon.eran@gmail.com
License: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha

@@ -93,1 +95,3 @@ 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).
AUTHORS.md
LICENSE
README.md
pyproject.toml
setup.py
bigraph_viz/__init__.py
bigraph_viz/convert.py
bigraph_viz/convert_vivarium_v1.py

@@ -7,0 +9,0 @@ bigraph_viz/dict_utils.py

+123
-66
import os
import inspect
import graphviz
from itertools import islice
import numpy as np

@@ -9,3 +10,2 @@

PROCESS_SCHEMA_KEYS = [

@@ -22,2 +22,8 @@ 'config',

def chunked(iterable, size):
"""Yield successive chunks from iterable."""
it = iter(iterable)
return iter(lambda: tuple(islice(it, size)), ())
def make_label(label):

@@ -32,6 +38,6 @@ # Insert line breaks after every max_length characters

def get_graph_wires(
ports_schema, # the ports schema
wires, # the wires, from port to path
graph_dict, # the current graph dict that is being built
schema_key, # inputs or outputs
ports_schema, # the ports schema
wires, # the wires, from port to path
graph_dict, # the current graph dict that is being built
schema_key, # inputs or outputs
edge_path, # the path up to this process

@@ -79,3 +85,3 @@ bridge_wires=None,

graph_dict['input_edges'].append({
'edge_path': edge_path ,
'edge_path': edge_path,
'target_path': target_path,

@@ -190,3 +196,3 @@ 'port': f'bridge_{port}',

rankdir='TB',
aspect_ratio='auto', # 'compress', 'expand', 'auto', 'fill'
aspect_ratio='auto', # 'compress', 'expand', 'auto', 'fill'
dpi='70',

@@ -204,2 +210,3 @@ significant_digits=2,

node_groups=False,
max_nodes_per_row=None,
):

@@ -233,3 +240,3 @@ """make a graphviz figure from a graph_dict"""

ratio=aspect_ratio, # "fill",
splines = 'true',
splines='true',
)

@@ -239,18 +246,57 @@

graph.attr('node', **state_node_spec)
for node in graph_dict['state_nodes']:
node_path = node['path']
node_name = add_node_to_graph(graph, node, state_node_spec, show_values, show_types, significant_digits)
node_names.append(node_name)
state_nodes = graph_dict['state_nodes']
if max_nodes_per_row:
previous_node = None
for i, chunk in enumerate(chunked(state_nodes, max_nodes_per_row)):
with graph.subgraph(name=f'state_row_{i}') as row:
row.attr(rank='same')
chunk_node_names = []
for node in chunk:
node_name = add_node_to_graph(graph, node, state_node_spec, show_values, show_types,
significant_digits)
node_names.append(node_name)
chunk_node_names.append(node_name)
# Add invisible edge to stack rows
if previous_node and chunk_node_names:
graph.edge(previous_node, chunk_node_names[0], style='invis', weight='10')
if chunk_node_names:
previous_node = chunk_node_names[-1]
else:
for node in state_nodes:
node_name = add_node_to_graph(graph, node, state_node_spec, show_values, show_types, significant_digits)
node_names.append(node_name)
# process nodes
process_paths = []
graph.attr('node', **process_node_spec)
for node in graph_dict['process_nodes']:
node_path = node['path']
process_paths.append(node_path)
node_name = str(node_path)
node_names.append(node_name)
label = make_label(node_path[-1])
graph.node(node_name, label=label)
process_nodes = graph_dict['process_nodes']
if max_nodes_per_row:
previous_node = None
for i, chunk in enumerate(chunked(process_nodes, max_nodes_per_row)):
with graph.subgraph(name=f'process_row_{i}') as row:
row.attr(rank='same')
chunk_node_names = []
for node in chunk:
node_path = node['path']
process_paths.append(node_path)
node_name = str(node_path)
node_names.append(node_name)
chunk_node_names.append(node_name)
label = make_label(node_path[-1])
row.node(node_name, label=label)
if previous_node and chunk_node_names:
graph.edge(previous_node, chunk_node_names[0], style='invis', weight='10')
if chunk_node_names:
previous_node = chunk_node_names[-1]
else:
for node in process_nodes:
node_path = node['path']
process_paths.append(node_path)
node_name = str(node_path)
node_names.append(node_name)
label = make_label(node_path[-1])
graph.node(node_name, label=label)
# place edges

@@ -278,3 +324,3 @@ graph.attr('edge', arrowhead='none', penwidth='2')

if edge['type'] == 'bridge_inputs':
graph.attr('edge', **output_edge_spec) # reverse arrow direction to go from composite to store
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')

@@ -287,3 +333,3 @@ else:

if edge['type'] == 'bridge_outputs':
graph.attr('edge', **input_edge_spec) # reverse arrow direction to go from store to composite
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')

@@ -300,6 +346,6 @@ else:

if 'bridge_outputs' in edge['type']:
graph.attr('edge', **input_edge_spec) # reverse arrow direction to go from store to composite
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
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')

@@ -396,3 +442,3 @@

(),
options=viztype_kwargs # TODO
options=viztype_kwargs # TODO
)

@@ -431,3 +477,3 @@

if not is_schema_key(key):
subpath = path + (key,)
subpath = path + (key,)
graph = core.get_graph_dict(

@@ -441,4 +487,4 @@ schema.get(key, {}),

return graph
def graphviz_edge(core, schema, state, path, options, graph):

@@ -490,3 +536,2 @@ # add process node to graph

(input_edge['edge_path'] == output_edge['edge_path']):
graph['bidirectional_edges'].append({

@@ -523,5 +568,7 @@ 'edge_path': input_edge['edge_path'],

def graphviz_none(core, schema, state, path, options, graph):
return graph
def graphviz_composite(core, schema, state, path, options, graph):

@@ -591,3 +638,2 @@ # add the composite edge

def get_graph_dict(self, schema, state, path, options, graph=None):

@@ -621,3 +667,2 @@ path = path or ()

def generate_graph_dict(self, schema, state, path, options):

@@ -627,3 +672,2 @@ full_schema, full_state = self.generate(schema, state)

def plot_graph(self,

@@ -657,3 +701,2 @@ graph_dict,

# Begin Tests

@@ -667,2 +710,3 @@ ###############

def test_simple_store():

@@ -677,2 +721,3 @@ simple_store_state = {

def test_forest():

@@ -692,2 +737,3 @@ forest = {

def test_nested_composite():

@@ -708,27 +754,27 @@ state = {

'config': {'_type': 'quote',
'state': {'grow': {'_type': 'process',
'address': 'local:grow',
'config': {'rate': 0.03},
'inputs': {'mass': ['mass']},
'outputs': {'mass': ['mass']}},
'divide': {'_type': 'process',
'address': 'local:divide',
'config': {'agent_id': '0',
'agent_schema': {'mass': 'float'},
'threshold': 2.0,
'divisions': 2},
'inputs': {'trigger': ['mass']},
'outputs': {'environment': ['environment']}},
'global_time': 0.0},
'bridge': {'inputs': {'mass': ['mass']},
'outputs': {'mass': ['mass'],
'environment': ['environment']}},
'composition': {'global_time': 'float'},
'interface': {'inputs': {}, 'outputs': {}},
'emitter': {'path': ['emitter'],
'address': 'local:ram-emitter',
'config': {},
'mode': 'none',
'emit': {}},
'global_time_precision': None}
'state': {'grow': {'_type': 'process',
'address': 'local:grow',
'config': {'rate': 0.03},
'inputs': {'mass': ['mass']},
'outputs': {'mass': ['mass']}},
'divide': {'_type': 'process',
'address': 'local:divide',
'config': {'agent_id': '0',
'agent_schema': {'mass': 'float'},
'threshold': 2.0,
'divisions': 2},
'inputs': {'trigger': ['mass']},
'outputs': {'environment': ['environment']}},
'global_time': 0.0},
'bridge': {'inputs': {'mass': ['mass']},
'outputs': {'mass': ['mass'],
'environment': ['environment']}},
'composition': {'global_time': 'float'},
'interface': {'inputs': {}, 'outputs': {}},
'emitter': {'path': ['emitter'],
'address': 'local:ram-emitter',
'config': {},
'mode': 'none',
'emit': {}},
'global_time_precision': None}
}}}}

@@ -739,2 +785,3 @@ plot_bigraph(state,

def test_graphviz():

@@ -744,3 +791,3 @@ cell = {

'_type': 'map[float]',
'a': 11.0, #{'_type': 'float', '_value': 11.0},
'a': 11.0, # {'_type': 'float', '_value': 11.0},
'b': 3333.33},

@@ -782,2 +829,3 @@ 'cell': {

def test_bigraph_cell():

@@ -787,3 +835,3 @@ cell = {

'_type': 'map[float]',
'a': 11.0, #{'_type': 'float', '_value': 11.0},
'a': 11.0, # {'_type': 'float', '_value': 11.0},
'b': 3333.33},

@@ -793,3 +841,3 @@ 'cell': {

'config': {},
'address': 'local:cell', # TODO -- this is where the ports/inputs/outputs come from
'address': 'local:cell', # TODO -- this is where the ports/inputs/outputs come from
'internal': 1.0,

@@ -820,2 +868,3 @@ '_inputs': {

def test_bio_schema():

@@ -852,3 +901,3 @@ core = VisualizeTypes()

'inputs': {
'fields': ['fields',]
'fields': ['fields', ]
},

@@ -864,2 +913,3 @@ 'outputs': {

def test_flat_composite():

@@ -893,2 +943,3 @@ flat_composite_spec = {

def test_multi_processes():

@@ -915,2 +966,3 @@ process_schema = {

def test_nested_processes():

@@ -947,2 +999,3 @@ nested_process_spec = {

def test_cell_hierarchy():

@@ -983,5 +1036,5 @@ core = VisualizeTypes()

core.register('cell', {
'membrane': 'membrane',
'cytoplasm': 'cytoplasm',
'nucleoid': 'nucleoid'})
'membrane': 'membrane',
'cytoplasm': 'cytoplasm',
'nucleoid': 'nucleoid'})

@@ -1009,2 +1062,3 @@ # state

def test_multiple_disconnected_ports():

@@ -1034,2 +1088,3 @@ core = VisualizeTypes()

def test_composite_process():

@@ -1052,3 +1107,3 @@ core = VisualizeTypes()

'_inputs': {'port3': 'any'},
'_outputs': {'port4': 'any',},
'_outputs': {'port4': 'any', },
'inputs': {'port3': ['store1']},

@@ -1063,2 +1118,3 @@ 'outputs': {'port4': ['store2']}}}}

def test_bidirectional_edges():

@@ -1089,2 +1145,3 @@ core = VisualizeTypes()

def test_array_paths():

@@ -1150,4 +1207,4 @@ core = VisualizeTypes()

'acetate': np.array([[1.0], [2.0]]),
'biomass': np.array([[3.0],[4.0]]),
'glucose': np.array([[5.0],[6.0]])
'biomass': np.array([[3.0], [4.0]]),
'glucose': np.array([[5.0], [6.0]])
}

@@ -1154,0 +1211,0 @@ }

Metadata-Version: 2.1
Name: bigraph-viz
Version: 0.1.5
Version: 0.1.6
Summary: A graphviz-based plotting tool for compositional bigraph schema

@@ -8,2 +8,4 @@ Home-page: https://github.com/vivarium-collective/bigraph-viz

Author-email: agmon.eran@gmail.com
License: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha

@@ -93,1 +95,3 @@ 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).

@@ -5,3 +5,3 @@ import re

VERSION = '0.1.5'
VERSION = '0.1.6'

@@ -8,0 +8,0 @@