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

refactor: Nonempty #902

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions quantinuum-hugr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ delegate = "0.12.0"
paste = "1.0"
strum = "0.26.1"
strum_macros = "0.26.1"
nonempty = "0.10.0"

[dev-dependencies]
criterion = { version = "0.5.1", features = ["html_reports"] }
Expand Down
3 changes: 2 additions & 1 deletion quantinuum-hugr/src/hugr/hugrmut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use core::panic;
use std::collections::HashMap;
use std::ops::Range;

use nonempty::NonEmpty;
use portgraph::view::{NodeFilter, NodeFiltered};
use portgraph::{LinkMut, NodeIndex, PortMut, PortView, SecondaryMap};

Expand Down Expand Up @@ -365,7 +366,7 @@ impl<T: RootTagged<RootHandle = Node> + AsMut<Hugr>> HugrMut for T {
subgraph: &SiblingSubgraph,
) -> HashMap<Node, Node> {
// Create a portgraph view with the explicit list of nodes defined by the subgraph.
let portgraph: NodeFiltered<_, NodeFilter<&[Node]>, &[Node]> =
let portgraph: NodeFiltered<_, NodeFilter<&NonEmpty<Node>>, &NonEmpty<Node>> =
NodeFiltered::new_node_filtered(
other.portgraph(),
|node, ctx| ctx.contains(&node.into()),
Expand Down
15 changes: 8 additions & 7 deletions quantinuum-hugr/src/hugr/rewrite/replace.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Implementation of the `Replace` operation.

use std::collections::{HashMap, HashSet, VecDeque};

use itertools::Itertools;
use nonempty::NonEmpty;
use std::collections::{HashMap, HashSet, VecDeque};
use thiserror::Error;

use crate::hugr::hugrmut::InsertionResult;
Expand Down Expand Up @@ -59,7 +59,7 @@ pub struct Replacement {
/// These must all have a common parent (i.e. be siblings). Called "S" in the spec.
/// Must be non-empty - otherwise there is no parent under which to place [Self::replacement],
/// and there would be no possible [Self::mu_inp], [Self::mu_out] or [Self::adoptions].
pub removal: Vec<Node>,
pub removal: NonEmpty<Node>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

This requires every dependent to add a direct dependency on NonEmpty. Although it's nice to statically check this internally, i'd prefer the external interface be left more generic.

Adding a Replacement::try_new(removel: Vec<Node>, ...) could help with this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fair enough

/// A hugr (not necessarily valid, as it may be missing edges and/or nodes), whose root
/// is the same type as the root of [Self::replacement]. "G" in the spec.
pub replacement: Hugr,
Expand Down Expand Up @@ -440,6 +440,7 @@ mod test {

use cool_asserts::assert_matches;
use itertools::Itertools;
use nonempty::nonempty;

use crate::algorithm::nest_cfgs::test::depth;
use crate::builder::{
Expand Down Expand Up @@ -571,7 +572,7 @@ mod test {
}

h.apply_rewrite(Replacement {
removal: vec![entry.node(), bb2.node()],
removal: nonempty![entry.node(), bb2.node()],
replacement,
adoptions: HashMap::from([(r_df1.node(), entry.node()), (r_df2.node(), bb2.node())]),
mu_inp: vec![],
Expand Down Expand Up @@ -700,7 +701,7 @@ mod test {
},
);
let mut r = Replacement {
removal: vec![case1, case2],
removal: nonempty![case1, case2],
replacement: rep1,
adoptions: HashMap::from_iter([(r1, case1), (r2, baz_dfg.node())]),
mu_inp: vec![],
Expand All @@ -724,14 +725,14 @@ mod test {
// First, removed nodes...
assert_eq!(
verify_apply(Replacement {
removal: vec![h.root()],
removal: nonempty![h.root()],
..r.clone()
}),
Err(ReplaceError::CantReplaceRoot)
);
assert_eq!(
verify_apply(Replacement {
removal: vec![case1, baz_dfg.node()],
removal: nonempty![case1, baz_dfg.node()],
..r.clone()
}),
Err(ReplaceError::MultipleParents(vec![cond.node(), case2]))
Expand Down
2 changes: 1 addition & 1 deletion quantinuum-hugr/src/hugr/rewrite/simple_replace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ pub(in crate::hugr::rewrite) mod test {
replacement.remove_node(in_);
replacement.remove_node(out);
Replacement {
removal: s.subgraph.nodes().to_vec(),
removal: s.subgraph.nodes().clone(),
replacement,
adoptions: HashMap::new(),
mu_inp,
Expand Down
38 changes: 17 additions & 21 deletions quantinuum-hugr/src/hugr/views/sibling_subgraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::collections::HashSet;
use std::mem;

use itertools::Itertools;
use nonempty::NonEmpty;
use portgraph::algorithms::ConvexChecker;
use portgraph::{view::Subgraph, Direction, PortView};
use thiserror::Error;
Expand Down Expand Up @@ -54,7 +55,7 @@ use crate::{Hugr, IncomingPort, Node, OutgoingPort, Port, SimpleReplacement};
#[derive(Clone, Debug)]
pub struct SiblingSubgraph {
/// The nodes of the induced subgraph.
nodes: Vec<Node>,
nodes: NonEmpty<Node>,
/// The input ports of the subgraph.
///
/// Grouped by input parameter. Each port must be unique and belong to a
Expand Down Expand Up @@ -101,16 +102,12 @@ impl SiblingSubgraph {
let (inputs, outputs) = get_input_output_ports(dfg_graph);

validate_subgraph(dfg_graph, &nodes, &inputs, &outputs)?;

if nodes.is_empty() {
Err(InvalidSubgraph::EmptySubgraph)
} else {
Ok(Self {
nodes,
inputs,
outputs,
})
}
let nodes = NonEmpty::from_vec(nodes).ok_or(InvalidSubgraph::EmptySubgraph)?;
Ok(Self {
nodes,
inputs,
outputs,
})
}

/// Create a new convex sibling subgraph from input and output boundaries.
Expand Down Expand Up @@ -192,6 +189,8 @@ impl SiblingSubgraph {
return Err(InvalidSubgraph::NotConvex);
}

let nodes = NonEmpty::from_vec(nodes).ok_or(InvalidSubgraph::EmptySubgraph)?;

Ok(Self {
nodes,
inputs,
Expand Down Expand Up @@ -269,7 +268,7 @@ impl SiblingSubgraph {
}

/// An iterator over the nodes in the subgraph.
pub fn nodes(&self) -> &[Node] {
pub fn nodes(&self) -> &NonEmpty<Node> {
&self.nodes
Comment on lines +271 to 272
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd prefer to leave the NonEmpty out of the public interface here,

Suggested change
pub fn nodes(&self) -> &NonEmpty<Node> {
&self.nodes
pub fn nodes<'a>(&'a self) -> impl Iterator<Item = &'a Node> + &'a {
self.nodes.iter()

that breaks some code, you'll need some collect_vecs around (which implies allocating new arrays :/).

So something like

let portgraph: NodeFiltered<_, NodeFilter<Vec<Node>>, Vec<Node>> =
    NodeFiltered::new_node_filtered(
        other.portgraph(),
        |node, ctx| ctx.contains(&node.into()),
        subgraph.nodes().cloned().collect_vec(),
    );

in HugrMut::insert_subgraph.

}

Expand Down Expand Up @@ -715,15 +714,12 @@ mod tests {
fn from_sibling_graph(sibling_graph: &impl HugrView) -> Result<Self, InvalidSubgraph> {
let root = sibling_graph.root();
let nodes = sibling_graph.children(root).collect_vec();
if nodes.is_empty() {
Err(InvalidSubgraph::EmptySubgraph)
} else {
Ok(Self {
nodes,
inputs: Vec::new(),
outputs: Vec::new(),
})
}
let nodes = NonEmpty::from_vec(nodes).ok_or(InvalidSubgraph::EmptySubgraph)?;
Ok(Self {
nodes,
inputs: Vec::new(),
outputs: Vec::new(),
})
}
}

Expand Down