Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aeon 1.0 #94

Merged
merged 30 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8754bff
Start migrating from `pyeda` to `aeon-py`.
daemontus Jan 25, 2024
74642a1
Fix minor test issues and update to latest alpha.
daemontus Jan 29, 2024
a2ac6f1
Clean up some additional details and tests.
daemontus Jan 29, 2024
66fa99b
Merge branch 'main' into aeon-1.0
daemontus Jan 29, 2024
8782913
Update aeon in `pyproject.toml`.
daemontus Jan 29, 2024
8674680
Update benchmark scripts to use `balm`.
daemontus Jan 29, 2024
616bdb3
Optimized DNF generator.
daemontus Jan 29, 2024
b76d5d7
Faster percolation.
daemontus Jan 29, 2024
ead2378
Add benchmark script for PN translation.
daemontus Jan 30, 2024
485f66c
Add logging to PN translation.
daemontus Jan 30, 2024
06b2ed7
Improve percolation performance.
daemontus Jan 31, 2024
42dea15
Add BN input sampling generator.
daemontus Jan 31, 2024
e371f9c
Merge branch 'main' into aeon-1.0
daemontus Jan 31, 2024
b99f561
Make LDOI input generic.
daemontus Jan 31, 2024
2c60545
Make inputs to `control` generic and update documentation.
daemontus Jan 31, 2024
964d1a8
Merge space and state utils.
daemontus Jan 31, 2024
2ba2e2a
Additional cleanup in tests.
daemontus Jan 31, 2024
3eb5f4a
Model `146` is now fast enough.
daemontus Jan 31, 2024
f36cad4
Model `75` is not fast enough.
daemontus Jan 31, 2024
4c650cb
Tests are now generally fast enough to lift the node limit in attract…
daemontus Jan 31, 2024
7fc8afb
Move the `cleanup_network` to a more logical place.
daemontus Jan 31, 2024
02a5c35
Simplify types in `interaction_graph_utils`.
daemontus Jan 31, 2024
daa366e
Remove an unnecessary function.
daemontus Jan 31, 2024
11c6798
Minor docs update.
daemontus Jan 31, 2024
ab949a8
Fix ignored error.
daemontus Jan 31, 2024
bc3f5ea
Remove regulations correctly in the input sampling script.
daemontus Jan 31, 2024
4fb28fe
ran file through black formatter and ignored a type error
jcrozum Jan 31, 2024
7a24c14
Apply review.
daemontus Jan 31, 2024
1d5507c
Add comment about the `IndexError`.
daemontus Jan 31, 2024
fec611a
Add a very basic benchmark for control, just for sanity checks.
daemontus Jan 31, 2024
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,7 @@ dmypy.json
mole-*

# Ignore benchmark runs.
_run_*
_run_*

# Some weird temporary Z3 files.
.z3-trace
38 changes: 19 additions & 19 deletions balm/SuccessionDiagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Iterator

import networkx as nx # type: ignore
from biodivine_aeon import BooleanNetwork
from biodivine_aeon import AsynchronousGraph, BooleanNetwork

from balm._sd_algorithms.compute_attractor_seeds import compute_attractor_seeds
from balm._sd_algorithms.expand_attractor_seeds import expand_attractor_seeds
Expand All @@ -15,7 +15,7 @@
from balm._sd_algorithms.expand_minimal_spaces import expand_minimal_spaces
from balm._sd_algorithms.expand_source_SCCs import expand_source_SCCs
from balm._sd_algorithms.expand_to_target import expand_to_target
from balm.interaction_graph_utils import feedback_vertex_set
from balm.interaction_graph_utils import cleanup_network, feedback_vertex_set
from balm.petri_net_translation import network_to_petrinet
from balm.space_utils import percolate_space, space_unique_key
from balm.trappist_core import trappist
Expand Down Expand Up @@ -96,6 +96,7 @@ class SuccessionDiagram:

__slots__ = (
"network",
"symbolic",
"petri_net",
"nfvs",
"dag",
Expand All @@ -104,7 +105,8 @@ class SuccessionDiagram:

def __init__(self, network: BooleanNetwork):
# Original Boolean network.
self.network = network
self.network = cleanup_network(network)
self.symbolic = AsynchronousGraph(self.network)
# A Petri net representation of the original Boolean network.
self.petri_net = network_to_petrinet(network)
# Negative feedback vertex set.
Expand Down Expand Up @@ -133,7 +135,11 @@ def __getstate__(self) -> dict[str, str | nx.DiGraph | list[str] | dict[int, int
def __setstate__(
self, state: dict[str, str | nx.DiGraph | list[str] | dict[int, int]]
):
self.network = BooleanNetwork.from_aeon(str(state["network rules"]))
# In theory, the network should be cleaned-up at this point, but just in case...
self.network = cleanup_network(
BooleanNetwork.from_aeon(str(state["network rules"]))
)
self.symbolic = AsynchronousGraph(self.network)
self.petri_net = cast(nx.DiGraph, state["petri net"])
self.nfvs = cast(list[str], state["nfvs"])
self.dag = cast(nx.DiGraph, state["G"]) # type: ignore
Expand Down Expand Up @@ -269,20 +275,16 @@ def find_node(self, node_space: BooleanSpace) -> int | None:
if no such node exists in this succession diagram.
"""
try:
key = space_unique_key(node_space, self.network)
key = space_unique_key(node_space, self.network) # throws IndexError
if key in self.node_indices:
i = self.node_indices[key]
# This assertion could be violated if a user gives a node space
# that is not based on the same network as this succession
# diagram.
assert node_space == self.node_space(i)
return i
return self.node_indices[key]
else:
return None
except RuntimeError:
# If the user gives us a space that uses variables not used by this
# network, we should get an error that we can catch and report that
# no such space exists here.
except IndexError:
Copy link
Owner

Choose a reason for hiding this comment

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

Don't we actually expect this to be an AssertionError?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Well... what we are actually intending to catch here is an error that is raised by space_unique_key if you feed it something that cannot be converted to a valid key. The assertion in this function is actually a "true" assertion: if it fails, something is wrong and we should terminate. But I agree that this is a bit confusing :/

Copy link
Owner

Choose a reason for hiding this comment

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

OK, that makes sense. Maybe just add a comment here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's there, underneath :) It's just not visible in the diff, because it was already there before. I changed the error to something that seemed more appropriate (after all, RuntimeError could be literally anything).

But I guess it is kind of vague, I'll make it more explicit.

Copy link
Owner

Choose a reason for hiding this comment

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

Yeah, I saw that comment; I'll paste it here so we can see it:

# If the user gives us a space that uses variables not used by this
# network, we should get an error that we can catch and report that
# no such space exists here.

I meant we should add a comment to key = space_unique_key(node_space, self.network) if that's where the exception can be thrown. An even better solution would be to have space_unique_key handle bad inputs more gracefully, e.g., returning None in those cases. Then we can just write

if not key:
    return None

Copy link
Owner

Choose a reason for hiding this comment

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

That being said, I think we can merge this and worry about it later since it's correct.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I gave it a bit more thought and I think the assertion is actually unsatisfiable (at least at this point, maybe it made sense in the past). For every key in node_indices, we should have that if key = space_unique_key(space), then node_space(node_indices(k)) == space, because the keys are unique hashes: every space has its own key, so there can be no collision. I guess my initial confusion was that if the key were to be computed for a different network with compatible variable names, this sort of collision could happen. But that is not what is happening in this method.

# If `space_unique_key` finds a variable that does not exist in this
# `SuccessionDiagram`, it throws an `IndexError`. This can happen
# for example if we are comparing two succession diagrams based on
# completely different networks.
return None

def is_subgraph(self, other: SuccessionDiagram) -> bool:
Expand Down Expand Up @@ -586,7 +588,7 @@ def _expand_one_node(self, node_id: int):
if DEBUG:
print(f"[{node_id}] Expanding: {len(self.node_space(node_id))} fixed vars.")

if len(current_space) == self.network.num_vars():
if len(current_space) == self.network.variable_count():
# This node is a fixed-point. Trappist would just
# return this fixed-point again. No need to continue.
if DEBUG:
Expand Down Expand Up @@ -632,9 +634,7 @@ def _ensure_node(self, parent_id: int | None, stable_motif: BooleanSpace) -> int
considered to be zero (i.e. the node is the root).
"""

fixed_vars = percolate_space(
self.network, stable_motif, strict_percolation=False
)
fixed_vars = percolate_space(self.symbolic, stable_motif)

key = space_unique_key(fixed_vars, self.network)

Expand Down
14 changes: 8 additions & 6 deletions balm/_sd_algorithms/compute_attractor_seeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from typing import TYPE_CHECKING

from balm.state_utils import state_list_to_bdd
from balm.symbolic_utils import state_list_to_bdd

if TYPE_CHECKING:
from balm.SuccessionDiagram import SuccessionDiagram
Expand Down Expand Up @@ -32,7 +32,7 @@ def compute_attractor_seeds(

node_space = sd.node_space(node_id)

if len(node_space) == sd.network.num_vars():
if len(node_space) == sd.network.variable_count():
# This node is a fixed-point.
return [node_space]

Expand All @@ -48,14 +48,16 @@ def compute_attractor_seeds(
# We add the whole node space to the retain set because we know
# the space is a trap and this will remove the corresponding unnecessary
# Petri net transitions.
retained_set = make_retained_set(sd.network, sd.nfvs, node_space, child_spaces)
retained_set = make_retained_set(sd.symbolic, sd.nfvs, node_space, child_spaces)

if len(retained_set) == sd.network.num_vars() and len(child_spaces) == 0:
if len(retained_set) == sd.network.variable_count() and len(child_spaces) == 0:
# There is only a single attractor remaining here,
# and its "seed" is the retained set.
return [retained_set]

terminal_restriction_space = ~state_list_to_bdd(child_spaces)
terminal_restriction_space = state_list_to_bdd(
sd.symbolic.symbolic_context(), child_spaces
).l_not()

candidate_seeds = compute_fixed_point_reduced_STG(
sd.petri_net,
Expand All @@ -73,7 +75,7 @@ def compute_attractor_seeds(
return candidate_seeds
else:
attractors = detect_motif_avoidant_attractors(
sd.network,
sd.symbolic,
sd.petri_net,
candidate_seeds,
terminal_restriction_space,
Expand Down
2 changes: 1 addition & 1 deletion balm/_sd_algorithms/expand_attractor_seeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def expand_attractor_seeds(sd: SuccessionDiagram, size_limit: int | None = None)
# are not covered by the already expanded children.

successor_space = sd.node_space(successors[-1])
retained_set = make_retained_set(sd.network, sd.nfvs, successor_space)
retained_set = make_retained_set(sd.symbolic, sd.nfvs, successor_space)

avoid_or_none = [
intersect(successor_space, child) for child in expanded_motifs
Expand Down
Loading