-
Notifications
You must be signed in to change notification settings - Fork 13
feat(py): ComposablePass protocol and ComposedPass for hugr-py
#2636
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
Changes from all commits
eeb4c50
c5cf1d7
8a3d6ba
7b58c05
33e325b
5e31a3d
c52de85
6d7afc2
89fabf0
91879cd
4878d5f
7045270
d20aaaa
954730f
448b5cf
17d1dc8
b6fa9a5
cc9c151
2d10e87
45bd86a
6ca653e
9171dba
c6fbfc0
fcf00c0
3ae525c
78e7522
af225a3
92964a8
f139d7c
df0ab15
ac63dd5
9a157b8
3db5bdd
67d7d38
975ddbb
0165ae7
2def5fc
9673dc2
01b7126
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| """A hugr-py passes module for hugr transformations.""" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| """A Protocol for a composable pass.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from dataclasses import dataclass | ||
| from typing import TYPE_CHECKING, Protocol, runtime_checkable | ||
|
|
||
| if TYPE_CHECKING: | ||
| from hugr.hugr.base import Hugr | ||
|
|
||
|
|
||
| @runtime_checkable | ||
| class ComposablePass(Protocol): | ||
| """A Protocol which represents a composable Hugr transformation.""" | ||
|
|
||
| def __call__(self, hugr: Hugr) -> None: | ||
| """Call the pass to transform a HUGR.""" | ||
| ... | ||
|
|
||
| @property | ||
| def name(self) -> str: | ||
| """Returns the name of the pass.""" | ||
| return self.__class__.__name__ | ||
|
|
||
| def then(self, other: ComposablePass) -> ComposablePass: | ||
| """Perform another composable pass after this pass.""" | ||
| # Provide a default implementation for composing passes. | ||
| pass_list = [] | ||
| if isinstance(self, ComposedPass): | ||
| pass_list.extend(self.passes) | ||
| else: | ||
| pass_list.append(self) | ||
|
|
||
| if isinstance(other, ComposedPass): | ||
| pass_list.extend(other.passes) | ||
| else: | ||
| pass_list.append(other) | ||
|
|
||
| return ComposedPass(pass_list) | ||
|
|
||
|
|
||
| @dataclass | ||
| class ComposedPass(ComposablePass): | ||
| """A sequence of composable passes.""" | ||
|
|
||
| passes: list[ComposablePass] | ||
|
|
||
| def __call__(self, hugr: Hugr): | ||
| """Call all of the passes in sequence.""" | ||
| for comp_pass in self.passes: | ||
| comp_pass(hugr) |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||
| from hugr.hugr.base import Hugr | ||||||
| from hugr.passes.composable_pass import ComposablePass, ComposedPass | ||||||
|
|
||||||
|
|
||||||
| def test_composable_pass() -> None: | ||||||
| class MyDummyPass(ComposablePass): | ||||||
| def __call__(self, hugr: Hugr) -> None: | ||||||
| return self(hugr) | ||||||
|
|
||||||
| def then(self, other: ComposablePass) -> ComposablePass: | ||||||
| return ComposedPass([self, other]) | ||||||
|
|
||||||
|
Comment on lines
+10
to
+12
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let it use the default implementation (we want to test that rather than test code)
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This suggestion wasn't implemented?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| @property | ||||||
| def name(self) -> str: | ||||||
| return "Dummy" | ||||||
|
|
||||||
| dummy = MyDummyPass() | ||||||
|
|
||||||
| composed = dummy.then(dummy) | ||||||
|
|
||||||
| my_composed_pass = ComposedPass([dummy, dummy]) | ||||||
|
|
||||||
| assert my_composed_pass.passes == [dummy, dummy] | ||||||
| assert isinstance(my_composed_pass, ComposablePass) | ||||||
| assert isinstance(composed, ComposablePass) | ||||||
| assert dummy.name == "Dummy" | ||||||
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, it's complaining that there are no tests here.
You can see the codecov report linked from the message.
We could add a test with a dummy no-op pass
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.
Done in 01b7126
Some maybe the
isinstancechecks are not needed. I also madeComposablePassruntime checkable.