-
Notifications
You must be signed in to change notification settings - Fork 0
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
Conversation
Coverage Report
|
…or detection to something less trivial.
This is great! While you're overhauling the SCC expansion, watch out for the import cycle. Maybe with the new approach, there will be a nicer way to resolve it. Basically, the |
I just pushed some formatting changes to this PR. We should probably add |
@@ -279,7 +285,7 @@ def find_node(self, node_space: BooleanSpace) -> int | None: | |||
return i | |||
else: | |||
return None | |||
except RuntimeError: | |||
except IndexError: |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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 :/
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
balm/motif_avoidant.py
Outdated
function_expression = network.get_update_function(varID) | ||
function_bdd = expr2bdd(aeon_to_pyeda(function_expression)) | ||
update_functions[var_name] = function_bdd | ||
|
||
# A random generator initialized with a fixed seed. Ensures simulation | ||
# is randomized but deterministic. | ||
generator = random.Random(1234567890) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be passed in (with a default seed of 0 probably), not hard-coded
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I'm referring to generator = random.Random(1234567890)
, in case that wasn't clear)
balm/petri_net_translation.py
Outdated
if bdd.is_false(): | ||
return | ||
if bdd.is_true(): | ||
yield BddPartialValuation(bdd.__ctx__(), {}) # type: ignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was the type error that I ignored
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting... maybe something mypy
added recently and you need to upgrade? It works locally on my computer as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes, that's the reason. This works without the ignore in the latest mypy. Feel free to remove the tag.
result: BooleanSpace = {} | ||
for var, value in percolated.items(): | ||
var_name = stg.get_network_variable_name(var) | ||
result[var_name] = cast(Literal[0, 1], int(value)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be nice to have a type alias like type Bool = Literal[0,1]
in the types file. Not sure how often this comes up, but the overlap in terminology between a Python Literal and a Boolean literal has tripped me up more than once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe BooleanValue
would be a better name, since it would match with BooleanState
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed! But I guess we can add that as a separate fix :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with merging this as-is. The comments I left are very minor. They should be probably addressed eventually, but let's not hold everything up for them. The only one I'm actually kind of worried about is catching the IndexError
vs the AssertionError
. We might want to add something to the unit tests for this.
Ok, I don't know what happened exactly, maybe I screwed up something during testing (I was experimenting with the percolation when I did the preliminary tests for SCC expansion), but the latest commit is actually faster on SCC expansion than the original version, roughly in line with the improvements we got for the "full" expansion. So I guess that issue is closed. Furthermore, I tested control: Anything that was able to finish under 10 minutes on the new version was slower on the older version. So I think we should be fine in terms of performance. I'll be merging this, and we can solve the rest as we go. |
This PR migrates
balm
to the version1.0
ofbiodivine_aeon
. As part of the migration, several major changes are introduced:The dependency on PyEDA is completely removed, because we can now use symbolic representations in AEON. This mainly affects the Petri net translation and motif avoidant detection, where PyEDA BDDs were used extensively. To some extent, this also affects percolation, but not as dramatically.
SuccessionDiagram
now also maintains anAsynchronousGraph
of the underlyingBooleanNetwork
. To avoid any unnecessary overhead, theSuccessionDiagram
first removes any static constraints imposed by the influence graph, so this should really be only building the symbolic context and the function BDDs and nothing else.petri_net_translation
table below).bfs_full_expansion
table). For other models, the impact is negligible, mostly because we are not bottlenecked by the solver very much (or the PN did not improve measurably).For percolation, I observed that it's not particularly critical for performance on the real-world models, but it can be a bottleneck on the large random models. Here, the new implementation should be up to 2x faster than the old one, but we seem to be hitting other bottlenecks for those models as well, so the performance impact is not as drastic.
bfs_full_expansion
table).I also did some non-trivial updates to the SCC expansion, mainly using new features in BN manipulation to avoid the
bnet
translations. In the end, the changes should not impact performance much, but the expansion is still faster compared to the previous result, probably for the same reasons full expansion is faster (Seeattractors_scc_expansion
table)Other miscellaneous changes:
to do this in Python were missing previously.
BooleanNetwork
now inherits from aRegulatoryGraph
, hence type signatures that accepted both can be simplified to justRegulatoryGraph
(for the most part).SymbolicContext
andAsynchronousGraph
instances that we pass around.petri_net_translation.xlsx
bfs_full_expansion.xlsx
attractors_scc_expansion.xlsx