Skip to content

Commit

Permalink
Add size_limit and optimize_source_nodes to block expansion.
Browse files Browse the repository at this point in the history
  • Loading branch information
daemontus committed Aug 23, 2024
1 parent 9f27fec commit 84b01ef
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 10 deletions.
19 changes: 16 additions & 3 deletions biobalm/_sd_algorithms/expand_source_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
def expand_source_blocks(
sd: SuccessionDiagram,
check_maa: bool = True,
size_limit: int | None = None,
optimize_source_nodes: bool = True,
) -> bool:
"""
Base correctness assumptions:
- Expanding two minimal blocks is always independent.
-
- Expanding two minimal blocks is always independent. Every minimal trap space is contained in one of the maximal trap
spaces of a single block. Furthermore, expanding a minimal block cannot intrfere with the expansion of the other
stable motifs.
- Any path in the "inner" succession diagram based on a single block can be reproduced in the larger succession diagram,
meaning absence of attractors in the inner diagram proves absence of attractors in the outer diagram.
"""

if sd.config["debug"]:
Expand All @@ -41,14 +46,17 @@ def expand_source_blocks(
print(f" > Computed source/input variable(s): {sources}")

# get source nodes combinations and expand root node
if len(sources) != 0:
if len(sources) != 0 and optimize_source_nodes:
# If there are too many source nodes, this can generate an absurdly large SD.
# This would be a problem even without the SCC expansion, but we can just
# stop the whole thing faster because we know how many nodes it generates.
if 2 ** len(sources) > sd.config["max_motifs_per_node"]:
raise RuntimeError(
f"Exceeded the maximum amount of stable motifs per node ({sd.config['max_motifs_per_node']}; see `SuccessionDiagramConfiguration.max_motifs_per_node`)."
)
elif size_limit is not None and 2 ** len(sources) > size_limit:
# Cannot expand. Size limit would be exceeded.
return False
else:
if sd.config["debug"]:
print(
Expand Down Expand Up @@ -84,6 +92,11 @@ def expand_source_blocks(
# We re-discovered a previously expanded node.
continue

# Only continue if the succession diagram isn't too large.
if (size_limit is not None) and (len(sd) >= size_limit):
# Size limit reached.
return False

# Compute successors as if this was a normal expansion procedure.
successors = sd.node_successors(node, compute=True)
# Sort successors to avoid non-determinism.
Expand Down
26 changes: 22 additions & 4 deletions biobalm/succession_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -1193,17 +1193,35 @@ def expand_scc(self, find_motif_avoidant_attractors: bool = True) -> bool:
"""
return expand_source_SCCs(self, check_maa=find_motif_avoidant_attractors)

def expand_block(self, find_motif_avoidant_attractors: bool = True) -> bool:
def expand_block(
self,
find_motif_avoidant_attractors: bool = True,
size_limit: int | None = None,
optimize_source_nodes: bool = True,
) -> bool:
"""
Expand the succession diagram using the source block method.
There is a minor difference in behavior depending on `find_motif_avoidant_attractors`.
If set to `False`, the expansion only expands one "source block" for each node,
without checking any attractor properties. If set to `True`, the expansion might
expand some nodes fully to uncover nodes that precisely cover motif
avoidant attractors.
"""
return expand_source_blocks(self, find_motif_avoidant_attractors)
avoidant attractors. As a byproduct, if set to `True` and no motif avoidant attractors
are detected for some node, this is result is saved and the attractors don't
need to be recomputed later.
By default, the method also detects any source nodes and directly expands these
into trap spaces where all source nodes are fixed. This has no correctness impact on
attractor search and always produces a smaller succession diagram, but if you need to
obtain a succession diagram where this does not happen (e.g. for testing), you can turn
this off using `optimize_source_nodes`.
"""
return expand_source_blocks(
self,
find_motif_avoidant_attractors,
size_limit=size_limit,
optimize_source_nodes=optimize_source_nodes,
)

def expand_bfs(
self,
Expand Down
27 changes: 24 additions & 3 deletions tests/succession_diagram_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ def test_expansion_comparisons(network_file: str):
sd_min = SuccessionDiagram(bn)
assert sd_min.expand_minimal_spaces(size_limit=NODE_LIMIT)

assert sd_bfs.is_isomorphic(sd_dfs)
assert sd_min.is_subgraph(sd_bfs)
assert sd_min.is_subgraph(sd_dfs)

# Expand the first node fully, and then expand the rest
# until minimal trap spaces are found.
sd_min_larger = SuccessionDiagram(bn)
Expand All @@ -183,13 +187,30 @@ def test_expansion_comparisons(network_file: str):
if not sd_min_larger.node_data(node_id)["expanded"]:
assert sd_min_larger.expand_minimal_spaces(node_id=node_id)

assert sd_bfs.is_isomorphic(sd_dfs)
assert sd_min.is_subgraph(sd_bfs)
assert sd_min.is_subgraph(sd_dfs)
assert sd_min_larger.is_subgraph(sd_bfs)
assert sd_min_larger.is_subgraph(sd_dfs)
assert len(sd_min_larger) >= len(sd_min)

# Normal block expansion with size limit.
sd_block = SuccessionDiagram(bn)
assert sd_block.expand_block(
find_motif_avoidant_attractors=True,
size_limit=NODE_LIMIT,
optimize_source_nodes=False, # Needed for compatibility.
)

assert sd_block.is_subgraph(sd_bfs)
assert sd_block.is_subgraph(sd_dfs)

block_trap = [
sd_block.node_data(i)["space"] for i in sd_block.minimal_trap_spaces()
]
min_trap = [sd_min.node_data(i)["space"] for i in sd_min.minimal_trap_spaces()]
for t in block_trap:
assert t in min_trap
for t in min_trap:
assert t in block_trap

sd_attr = SuccessionDiagram(bn)
assert sd_attr.expand_attractor_seeds(size_limit=NODE_LIMIT)

Expand Down

1 comment on commit 84b01ef

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
biobalm
   _pint_reachability.py615018%24, 40–54, 69–93, 101–146
   control.py1141488%107, 119, 125, 129, 134, 143–159, 477, 480, 493
   interaction_graph_utils.py52688%11–13, 151–152, 222–223
   petri_net_translation.py1491292%22–26, 79, 136, 234, 308–309, 333–334, 343, 452
   space_utils.py1322085%26–28, 104–110, 133–139, 347–350, 414, 462
   succession_diagram.py3806483%6, 120, 210–215, 228, 275–282, 386–393, 410–411, 421, 427, 543, 630–636, 752, 755, 873–891, 923, 933, 936, 976, 983, 1034, 1052, 1174, 1360, 1371, 1379, 1422, 1434, 1439, 1445
   symbolic_utils.py32584%10, 39–44, 100, 128
   trappist_core.py1842388%14–18, 55, 57, 92, 215, 217, 219, 254–256, 276–282, 340, 342, 372, 420, 422
biobalm/_sd_algorithms
   expand_attractor_seeds.py60788%6, 28, 42, 109–114, 119
   expand_bfs.py28196%6
   expand_dfs.py30197%6
   expand_minimal_spaces.py40295%6, 37
   expand_source_SCCs.py1111686%11–13, 50, 69, 77, 82, 103, 112, 120, 131, 140, 143, 167, 179, 242–243
   expand_source_blocks.py1151587%11, 31, 46, 54, 59, 62, 86, 98, 141, 167, 176, 209, 219, 225, 234
   expand_to_target.py31390%6, 38, 43
biobalm/_sd_attractors
   attractor_candidates.py2659066%13–15, 26–27, 93, 101, 107–108, 130, 152, 187, 193–204, 223, 239–320, 325, 329, 335, 341, 356, 383, 388, 392, 398, 400–438, 511, 582–583, 684
   attractor_symbolic.py1141686%6–7, 75, 88–92, 103, 112, 144, 179, 191–193, 202, 230, 236
TOTAL198734583% 

Tests Skipped Failures Errors Time
359 0 💤 0 ❌ 0 🔥 53.807s ⏱️

Please sign in to comment.