-
Notifications
You must be signed in to change notification settings - Fork 13
feat: add new Subcircuit #1289
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
base: main
Are you sure you want to change the base?
feat: add new Subcircuit #1289
Changes from all commits
1471c0b
159af20
9d543f0
17f2870
c17d23f
b6add8e
47d167e
f888d39
63ea8c8
6ca3c4a
fb7b5ad
23ff42f
474abdf
4aa5db0
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 |
|---|---|---|
|
|
@@ -160,14 +160,6 @@ impl<H: HugrView> ResourceScope<H> { | |
| Some(*port_map.get(port)) | ||
| } | ||
|
|
||
| /// Get the [`ResourceId`] for a given port. | ||
| /// | ||
| /// Return None if the port is not a resource port. | ||
| pub fn get_resource_id(&self, node: H::Node, port: impl Into<Port>) -> Option<ResourceId> { | ||
| let unit = self.get_circuit_unit(node, port)?; | ||
| unit.as_resource() | ||
| } | ||
|
|
||
| /// Get all [`CircuitUnit`]s for either the incoming or outgoing ports of a | ||
| /// node. | ||
| pub fn get_circuit_units_slice( | ||
|
|
@@ -179,15 +171,47 @@ impl<H: HugrView> ResourceScope<H> { | |
| Some(port_map.get_slice(direction)) | ||
| } | ||
|
|
||
| /// Get the port of node on the given resource path. | ||
| /// Get the ports of node with the given opvalue in the given direction. | ||
|
Contributor
Author
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.
So I presume this means you might have multiple classical ports with the same unit....ah, if you are asking for inputs, and multiple inputs are wired from the same classical port? (But it'll never be >1 for Direction::Outgoing?) Is that right? If so I can just add a comment
Contributor
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. Yes, that is exactly the point. For copyable types, you may have more than one incoming port connected to the same value (identified by (Node, OutgoingPort)) |
||
| /// | ||
| /// The returned port will have the direction `dir`. | ||
| pub fn get_port(&self, node: H::Node, resource_id: ResourceId, dir: Direction) -> Option<Port> { | ||
| let units = self.get_circuit_units_slice(node, dir)?; | ||
| let offset = units | ||
| .iter() | ||
| .position(|unit| unit.as_resource() == Some(resource_id))?; | ||
| Some(Port::new(dir, offset)) | ||
| pub fn get_ports( | ||
| &self, | ||
| node: H::Node, | ||
| unit: impl Into<CircuitUnit<H::Node>>, | ||
| dir: Direction, | ||
| ) -> impl Iterator<Item = Port> + '_ { | ||
| let exp_unit = unit.into(); | ||
| let units = self.get_circuit_units_slice(node, dir); | ||
| let offsets = units | ||
| .into_iter() | ||
| .flatten() | ||
| .positions(move |unit| unit == &exp_unit); | ||
| offsets.map(move |offset| Port::new(dir, offset)) | ||
| } | ||
|
|
||
| /// Get the port of node with the given resource in the given direction. | ||
| pub fn get_resource_port( | ||
| &self, | ||
| node: H::Node, | ||
| resource_id: ResourceId, | ||
| dir: Direction, | ||
| ) -> Option<Port> { | ||
| self.get_ports(node, resource_id, dir) | ||
| .at_most_one() | ||
| .ok() | ||
| .expect("linear resource") | ||
| } | ||
|
|
||
| /// Get the resource ID at the given port of the given node. | ||
|
Contributor
Author
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. Note to self - so this has just moved |
||
| pub fn get_resource_id(&self, node: H::Node, port: impl Into<Port>) -> Option<ResourceId> { | ||
| let unit = self.get_circuit_unit(node, port)?; | ||
| unit.as_resource() | ||
| } | ||
|
|
||
| /// Get the copyable wire at the given port of the given node. | ||
| pub fn get_copyable_wire(&self, node: H::Node, port: impl Into<Port>) -> Option<Wire<H::Node>> { | ||
| let unit = self.get_circuit_unit(node, port)?; | ||
| unit.as_copyable_wire() | ||
| } | ||
|
|
||
| /// Get the position of the given node. | ||
|
|
@@ -215,24 +239,21 @@ impl<H: HugrView> ResourceScope<H> { | |
| .filter_map(|unit| unit.as_resource()) | ||
| } | ||
|
|
||
| /// All resource IDs on the ports of `node`, in both directions. | ||
| pub fn get_all_resources(&self, node: H::Node) -> Vec<ResourceId> { | ||
| /// All resource IDs on the ports of `node`, in both directions, in the | ||
| /// order that they appear along the ports of `node`. | ||
| pub fn get_all_resources(&self, node: H::Node) -> impl Iterator<Item = ResourceId> + '_ { | ||
| let in_resources = self.get_resources(node, Direction::Incoming); | ||
| let out_resources = self.get_resources(node, Direction::Outgoing); | ||
| let mut all_resources = in_resources.chain(out_resources).collect_vec(); | ||
| all_resources.sort_unstable(); | ||
| all_resources.dedup(); | ||
| all_resources.shrink_to_fit(); | ||
| all_resources | ||
| in_resources.chain(out_resources).unique() | ||
| } | ||
|
|
||
| /// Whether the given node is the first node on the path of the given | ||
| /// resource. | ||
| pub fn is_resource_start(&self, node: H::Node, resource_id: ResourceId) -> bool { | ||
| self.get_port(node, resource_id, Direction::Outgoing) | ||
| self.get_resource_port(node, resource_id, Direction::Outgoing) | ||
| .is_some() | ||
| && self | ||
| .get_port(node, resource_id, Direction::Incoming) | ||
| .get_resource_port(node, resource_id, Direction::Incoming) | ||
| .is_none() | ||
| } | ||
|
|
||
|
|
@@ -246,7 +267,7 @@ impl<H: HugrView> ResourceScope<H> { | |
| } | ||
|
|
||
| /// All copyable wires on the ports of `node` in the given direction. | ||
| pub fn get_copyable_wires( | ||
| pub fn all_copyable_wires( | ||
|
Contributor
Author
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. I note this is called only once, with Direction::Outgoing, for which case it is exactly the same as Whereas
Contributor
Author
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. So I suggest either dropping this one (have just (There is only one case where you pass a non-constant direction to
Contributor
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. Yes good spot, this is redundant. I agree that |
||
| &self, | ||
| node: H::Node, | ||
| dir: Direction, | ||
|
|
@@ -258,6 +279,18 @@ impl<H: HugrView> ResourceScope<H> { | |
| }) | ||
| } | ||
|
|
||
| /// All ports of `node` in the given direction that are copyable. | ||
| pub fn get_copyable_ports( | ||
| &self, | ||
| node: H::Node, | ||
| dir: Direction, | ||
| ) -> impl Iterator<Item = Port> + '_ { | ||
| let units = self.get_circuit_units_slice(node, dir); | ||
| let ports = self.hugr().node_ports(node, dir); | ||
| let units_ports = units.into_iter().flatten().zip(ports); | ||
| units_ports.filter_map(|(unit, port)| unit.is_copyable().then_some(port)) | ||
| } | ||
|
|
||
| /// Iterate over the nodes on the resource path starting from the given | ||
| /// node in the given direction. | ||
| pub fn resource_path_iter( | ||
|
|
@@ -267,7 +300,7 @@ impl<H: HugrView> ResourceScope<H> { | |
| direction: Direction, | ||
| ) -> impl Iterator<Item = H::Node> + '_ { | ||
| iter::successors(Some(start_node), move |&curr_node| { | ||
| let port = self.get_port(curr_node, resource_id, direction)?; | ||
| let port = self.get_resource_port(curr_node, resource_id, direction)?; | ||
| let (next_node, _) = self | ||
| .hugr() | ||
| .single_linked_port(curr_node, port) | ||
|
|
@@ -734,11 +767,7 @@ pub(crate) mod tests { | |
| .map(|(n, _)| n); | ||
|
|
||
| for h in first_hadamards { | ||
| let res = scope | ||
| .get_all_resources(h) | ||
| .into_iter() | ||
| .exactly_one() | ||
| .unwrap(); | ||
| let res = scope.get_all_resources(h).exactly_one().ok().unwrap(); | ||
| let nodes_on_path = scope.resource_path_iter(res, h, Direction::Outgoing); | ||
| let pos_on_path = nodes_on_path.map(|n| scope.get_position(n).unwrap()); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| //! copyable values throughout a HUGR circuit, including resource identifiers, | ||
| //! positions, and the mapping structures that associate them with operations. | ||
|
|
||
| use derive_more::From; | ||
| use hugr::{ | ||
| core::HugrNode, types::Signature, Direction, IncomingPort, OutgoingPort, Port, PortIndex, Wire, | ||
| }; | ||
|
|
@@ -21,7 +22,8 @@ pub struct ResourceId(usize); | |
| impl ResourceId { | ||
| /// Create a new ResourceId. | ||
| /// | ||
| /// This method should only be called by ResourceAllocator and tests. | ||
| /// ResourceIds should typically be obtained from [`ResourceAllocator`]. | ||
|
Contributor
Author
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. I'll put this in some
Contributor
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. Thank you! |
||
| /// Only use this in testing. | ||
| pub(crate) fn new(id: usize) -> Self { | ||
| Self(id) | ||
| } | ||
|
|
@@ -38,7 +40,7 @@ impl ResourceId { | |
| /// Initially assigned as contiguous integers, they may become non-integer | ||
| /// when operations are inserted or removed. | ||
| #[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
| pub struct Position(Rational64); | ||
| pub struct Position(pub(crate) Rational64); | ||
|
|
||
| impl std::fmt::Debug for Position { | ||
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
|
|
@@ -51,8 +53,8 @@ impl Position { | |
| /// | ||
| /// This method should only be called by allocators and tests. | ||
| #[allow(unused)] | ||
| pub(super) fn new_integer(i: i64) -> Self { | ||
| Self(Rational64::from_integer(i)) | ||
| pub(super) fn new_integer(numer: i64) -> Self { | ||
| Self(Rational64::from_integer(numer)) | ||
| } | ||
|
|
||
| /// Get position as f64, rounded to the given precision. | ||
|
|
@@ -69,12 +71,19 @@ impl Position { | |
|
|
||
| /// A value associated with a dataflow port, identified either by a resource ID | ||
| /// (for linear values) or by its wire (for copyable values). | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
| /// | ||
| /// This can currently be converted to and from [`hugr::CircuitUnit`], but | ||
| /// linear wires are assigned to resources with typed resource IDs instead of | ||
| /// integers. | ||
| /// | ||
| /// Equivalence with [`hugr::CircuitUnit`] is not guaranteed in the future: we | ||
| /// may expand expressivity, e.g. identifying copyable units by their ASTs. | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, From)] | ||
| pub enum CircuitUnit<N: HugrNode> { | ||
| /// A linear resource. | ||
| Resource(ResourceId), | ||
| Resource(#[from] ResourceId), | ||
| /// A copyable value. | ||
| Copyable(Wire<N>), | ||
| Copyable(#[from] Wire<N>), | ||
| } | ||
|
|
||
| impl<N: HugrNode> CircuitUnit<N> { | ||
|
|
||
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.
Is there good reason to drop this public export?
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.
No, feel free to leave it :)