Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions corneto/contrib/annnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from annnet import AnnNet


_ANNNET_VERTEX_RESERVED = {"layer", "slice", "vertex_id"}
_ANNNET_NODE_RESERVED = {"layer", "slice", "node_id"}
_ANNNET_EDGE_RESERVED = {
"as_entity",
"default_edge_directed",
Expand Down Expand Up @@ -102,22 +102,22 @@ def to_annnet(graph: BaseGraph, *, copy_attributes: bool = True) -> "AnnNet":
annnet = import_optional_module("annnet")
result = annnet.AnnNet(directed=None)

vertex_ids = {}
node_ids = {}
used_ids = set()
for vertex in graph.V:
vertex_id = str(vertex)
if vertex_id in used_ids:
raise ValueError(f"Multiple CORNETO vertices map to AnnNet vertex {vertex_id!r}.")
used_ids.add(vertex_id)
vertex_ids[vertex] = vertex_id
node_id = str(vertex)
if node_id in used_ids:
raise ValueError(f"Multiple CORNETO vertices map to AnnNet node {node_id!r}.")
used_ids.add(node_id)
node_ids[vertex] = node_id
attributes = {}
if copy_attributes:
attributes = _copy_supported_attributes(
graph.get_attr_vertex(vertex),
_ANNNET_VERTEX_RESERVED,
"vertex",
_ANNNET_NODE_RESERVED,
"node",
)
result.add_vertices(vertex_id, **attributes)
result.add_nodes(node_id, **attributes)

if copy_attributes:
result.uns.update(deepcopy(dict(graph.get_graph_attributes())))
Expand All @@ -143,8 +143,8 @@ def to_annnet(graph: BaseGraph, *, copy_attributes: bool = True) -> "AnnNet":
"edge",
)

annnet_source = [vertex_ids[v] for v in source]
annnet_target = [vertex_ids[v] for v in target]
annnet_source = [node_ids[v] for v in source]
annnet_target = [node_ids[v] for v in target]
uniform_weight = _uniform_magnitude(source, target, edge_attributes)

if not directed:
Expand Down Expand Up @@ -174,8 +174,8 @@ def to_annnet(graph: BaseGraph, *, copy_attributes: bool = True) -> "AnnNet":
source_arg = annnet_source[0] if len(annnet_source) == 1 else annnet_source
target_arg = annnet_target[0] if len(annnet_target) == 1 else annnet_target
else:
source_arg = {vertex_ids[v]: -_endpoint_magnitude(edge_attributes, Attr.SOURCE_ATTR, v) for v in source}
target_arg = {vertex_ids[v]: _endpoint_magnitude(edge_attributes, Attr.TARGET_ATTR, v) for v in target}
source_arg = {node_ids[v]: -_endpoint_magnitude(edge_attributes, Attr.SOURCE_ATTR, v) for v in source}
target_arg = {node_ids[v]: _endpoint_magnitude(edge_attributes, Attr.TARGET_ATTR, v) for v in target}

result.add_edges(
source_arg,
Expand Down Expand Up @@ -219,18 +219,18 @@ def from_annnet(graph: "AnnNet", *, copy_attributes: bool = True) -> Graph:
result = Graph()
result.get_graph_attributes().update(graph_attributes)

vertices = list(graph.vertices())
for vertex in vertices:
nodes = list(graph.nodes())
for vertex in nodes:
attributes = {}
if copy_attributes:
attributes = dict(graph.attrs.get_vertex_attrs(vertex))
attributes.pop("vertex_id", None)
attributes = dict(graph.attrs.get_node_attrs(vertex))
attributes.pop("node_id", None)
result.add_vertex(vertex, **deepcopy(attributes))

edge_ids = list(graph.edges())
directed_edge_ids = set(graph.get_edges_by_direction(True))
matrix = graph.X()
row_by_vertex = {graph.get_vertex(i): i for i in range(graph.nv)}
matrix = graph.S
row_by_vertex = {graph.N[i]: i for i in range(graph.nv)}

for edge_index, edge_id in enumerate(edge_ids):
edge = graph.get_edge(edge_id)
Expand Down
22 changes: 11 additions & 11 deletions corneto/methods/signaling/annnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ def add_cellnopt_conditions(
{condition_aspect: list(condition_names)},
)

known_vertices = set(graph.vertices())
base_vertices = list(graph.vertices())
known_nodes = set(graph.nodes())
base_nodes = list(graph.nodes())
layers = {condition: (condition,) for condition in condition_names}
for condition, layer in layers.items():
graph.add_vertices(base_vertices, layer=layer)
graph.add_nodes(base_nodes, layer=layer)
values_by_role = (
(inputs[condition], input_attr),
(inhibitors[condition], inhibitor_attr),
Expand All @@ -119,12 +119,12 @@ def add_cellnopt_conditions(
for values, attribute in values_by_role:
if not isinstance(values, Mapping):
raise TypeError(f"Values for condition {condition!r} must be mappings keyed by protein.")
unknown = set(values) - known_vertices
unknown = set(values) - known_nodes
if unknown:
protein = sorted(unknown, key=str)[0]
raise ValueError(f"Unknown protein {protein!r} in condition {condition!r}.")
for protein, value in values.items():
graph.layers.set_vertex_layer_attrs(str(protein), layer, **{attribute: value})
graph.layers.set_node_attrs(str(protein), layer, **{attribute: value})
return layers


Expand Down Expand Up @@ -201,9 +201,9 @@ def build_cellnopt_from_annnet(
inputs[condition] = {}
inhibitors[condition] = {}
measurements[condition] = {}
for protein in graph.layers.layer_vertex_set(layer):
for protein in graph.layers.layer_node_set(layer):
protein_id = str(protein[0]) if isinstance(protein, tuple) else str(protein)
attributes = graph.layers.get_vertex_layer_attrs(protein_id, layer)
attributes = graph.layers.node_attrs(protein_id, layer)
if input_attr in attributes:
inputs[condition][protein_id] = attributes[input_attr]
if attributes.get(inhibitor_attr):
Expand Down Expand Up @@ -292,17 +292,17 @@ def add_cellnopt_results(
condition_errors = {}
for condition_index, condition in enumerate(condition_names):
layer = context.condition_layers[condition]
graph.add_vertices(vertices, layer=layer)
graph.add_nodes(vertices, layer=layer)
endpoint_error = 0.0
for vertex_index, protein in enumerate(vertices):
predicted = float(predictions[vertex_index, condition_index])
attributes = {prediction_attr: predicted}
existing = graph.layers.get_vertex_layer_attrs(protein, layer)
existing = graph.layers.node_attrs(protein, layer)
if measurement_attr in existing:
error = abs(predicted - float(existing[measurement_attr]))
attributes[error_attr] = error
endpoint_error += error
graph.layers.set_vertex_layer_attrs(protein, layer, **attributes)
graph.layers.set_node_attrs(protein, layer, **attributes)

condition_edges = []
for reaction_index in selected_indices:
Expand Down Expand Up @@ -344,7 +344,7 @@ def add_cellnopt_results(
}
if solution is not None and getattr(solution, "status", None) is not None:
layer_attributes["solver_status"] = str(solution.status)
graph.layers.set_layer_attrs(layer, **layer_attributes)
graph.layers.set_attrs(layer, **layer_attributes)
condition_errors[condition] = endpoint_error

graph.history.snapshot("cellnopt_results_added")
Expand Down
6 changes: 3 additions & 3 deletions docs/guide/interoperability/annnet.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ restored = from_annnet(annotated)
```

Directed binary edges and hyperedges, parallel edges, endpoint coefficients,
and ordinary graph, vertex, and edge attributes are preserved. CORNETO edge
and ordinary graph, node, and edge attributes are preserved. CORNETO edge
indices become AnnNet IDs such as `corneto_edge_0`. When converting in the
other direction, the original AnnNet ID is stored as the CORNETO edge
attribute `_annnet_edge_id`.
Expand All @@ -46,8 +46,8 @@ hypergraphs normally used by CORNETO:

- AnnNet layers, slice membership, edge-entities, and flexible direction
policies are not reproduced in CORNETO.
- Vertex identifiers are converted to strings for AnnNet. A collision after
conversion raises an error.
- A CORNETO vertex identifier becomes an AnnNet node id, as a string. A
collision after conversion raises an error.
- CORNETO edges with an empty source or target set are not supported.
- An undirected hyperedge is converted as one member set. Its original
CORNETO source/target partition cannot be recovered; converting it back uses
Expand Down
18 changes: 9 additions & 9 deletions docs/tutorials/annnet-signaling/annnet-cellnopt-dag.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@
"signaling = an.AnnNet(directed=True)\n",
"signaling.history.enable(True)\n",
"signaling.slices.add(\"prior\", role=\"prior_knowledge\")\n",
"signaling.add_vertices(sorted(display_name))\n",
"signaling.add_nodes(sorted(display_name))\n",
"\n",
"for protein, label in display_name.items():\n",
" signaling.attrs.set_vertex_attrs(protein, label=label, entity_type=\"protein\")\n",
" signaling.attrs.set_node_attrs(protein, label=label, entity_type=\"protein\")\n",
"\n",
"prior_edge_ids = []\n",
"for index, (source, sign, target) in enumerate(pkn_edges):\n",
Expand Down Expand Up @@ -495,7 +495,7 @@
" signaling,\n",
" backend=\"graphviz\",\n",
" layout=\"dot\",\n",
" vertex_label_key=\"label\",\n",
" node_label_key=\"label\",\n",
" use_weight_style=False,\n",
" graph_attr={\"rankdir\": \"LR\", \"size\": \"12,7\"},\n",
" node_attr={\"shape\": \"ellipse\", \"style\": \"filled\", \"fillcolor\": \"#eef4fb\"},\n",
Expand Down Expand Up @@ -599,12 +599,12 @@
},
"outputs": [],
"source": [
"def vertex_layer_table(network, layers, columns):\n",
" \"\"\"Return selected vertex-layer attributes as a pandas table.\"\"\"\n",
"def node_layer_table(network, layers, columns):\n",
" \"\"\"Return selected node-layer attributes as a pandas table.\"\"\"\n",
" rows = {}\n",
" for condition, layer in layers.items():\n",
" rows[condition] = {\n",
" label: network.layers.get_vertex_layer_attrs(protein, layer).get(attribute, np.nan)\n",
" label: network.layers.node_attrs(protein, layer).get(attribute, np.nan)\n",
" for label, (protein, attribute) in columns.items()\n",
" }\n",
" return pd.DataFrame.from_dict(rows, orient=\"index\").rename_axis(\"condition\")"
Expand Down Expand Up @@ -818,7 +818,7 @@
" \"AKT observed\": (\"akt\", \"observed\"),\n",
" \"HSP27 observed\": (\"hsp27\", \"observed\"),\n",
"}\n",
"experiment = vertex_layer_table(signaling, condition_layers, experiment_columns).fillna(0)\n",
"experiment = node_layer_table(signaling, condition_layers, experiment_columns).fillna(0)\n",
"experiment"
]
},
Expand Down Expand Up @@ -1790,7 +1790,7 @@
}
],
"source": [
"akt_response = vertex_layer_table(\n",
"akt_response = node_layer_table(\n",
" signaling,\n",
" condition_layers,\n",
" {\n",
Expand Down Expand Up @@ -2000,7 +2000,7 @@
"an.io.write(signaling, analysis_path, overwrite=True)\n",
"restored = an.io.read(analysis_path)\n",
"\n",
"restored_akt = vertex_layer_table(\n",
"restored_akt = node_layer_table(\n",
" restored,\n",
" condition_layers,\n",
" {\n",
Expand Down
12 changes: 6 additions & 6 deletions tests/contrib/test_annnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ def test_directed_hypergraph_roundtrip():

converted = to_annnet(graph)

assert converted.vertices() == ["A", "B", "C"]
assert converted.nodes() == ["A", "B", "C"]
assert converted.get_edge("corneto_edge_0") == (
frozenset({"A", "B"}),
frozenset({"C"}),
)
assert converted.get_edges_by_direction(True) == ["corneto_edge_0"]
assert converted.attrs.get_vertex_attrs("A")["kind"] == "gene"
assert converted.attrs.get_node_attrs("A")["kind"] == "gene"
assert converted.attrs.get_edge_attrs("corneto_edge_0")["relation"] == "reaction"
assert converted.uns["name"] == "example"

Expand Down Expand Up @@ -77,23 +77,23 @@ def test_undirected_hyperedge_is_canonicalized_to_member_set():
assert restored.get_attr_edge(0).get_attr(Attr.EDGE_TYPE) == EdgeType.UNDIRECTED.value


def test_non_string_vertex_ids_are_converted_to_strings():
def test_non_string_node_ids_are_converted_to_strings():
"""CORNETO vertex identifiers are stringified for AnnNet."""
graph = Graph()
graph.add_edge(1, 2)

converted = to_annnet(graph)

assert converted.vertices() == ["1", "2"]
assert converted.nodes() == ["1", "2"]


def test_string_conversion_rejects_vertex_id_collisions():
def test_string_conversion_rejects_node_id_collisions():
"""Stringification cannot silently merge distinct CORNETO vertices."""
graph = Graph()
graph.add_vertex(1)
graph.add_vertex("1")

with pytest.raises(ValueError, match="map to AnnNet vertex"):
with pytest.raises(ValueError, match="map to AnnNet node"):
to_annnet(graph)


Expand Down
8 changes: 4 additions & 4 deletions tests/methods/signaling/test_cellnopt_annnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ def test_cellnopt_reads_conditions_and_adds_results_to_annnet(backend):

assert solution.status == "optimal"
assert layers == {"off": ("off",), "on": ("on",), "blocked": ("blocked",)}
assert graph.layers.get_vertex_layer_attrs("L", ("on",))["input"] == 1
assert graph.layers.get_vertex_layer_attrs("A", ("blocked",))["inhibited"] == 1
assert graph.layers.get_vertex_layer_attrs("Y", ("on",))["predicted"] == 1
assert graph.layers.get_layer_attrs(("blocked",))["endpoint_absolute_error"] == 0
assert graph.layers.node_attrs("L", ("on",))["input"] == 1
assert graph.layers.node_attrs("A", ("blocked",))["inhibited"] == 1
assert graph.layers.node_attrs("Y", ("on",))["predicted"] == 1
assert graph.layers.attrs(("blocked",))["endpoint_absolute_error"] == 0
assert graph.slices.exists("cellnopt_selected")
assert summary["selected_reactions"] == 2
assert summary["condition_errors"] == {"off": 0.0, "on": 0.0, "blocked": 0.0}
Loading