diff --git a/Cargo.lock b/Cargo.lock index d5b7f15d67..1febbb1943 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,9 +216,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" @@ -232,7 +232,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -366,9 +366,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.27" +version = "1.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" dependencies = [ "jobserver", "libc", @@ -611,9 +611,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" @@ -804,9 +804,9 @@ checksum = "97af9b5f014e228b33e77d75ee0e6e87960124f0f4b16337b586a6bec91867b1" [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" @@ -865,12 +865,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1396,9 +1396,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64", "bytes", @@ -1674,6 +1674,17 @@ dependencies = [ "rustversion", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -1784,9 +1795,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.173" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "linux-raw-sys" @@ -2035,7 +2046,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2170,9 +2181,9 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portgraph" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61fb905fbfbc9abf3bd37853bbd4b25d31dffd5631994f8df528f85455085657" +checksum = "654a4341391cb49edc51ea9905b8271e5c2685bb0fdf09eda940f6281fbc95a9" dependencies = [ "bitvec", "delegate", @@ -2180,6 +2191,7 @@ dependencies = [ "num-traits", "petgraph 0.8.2", "serde", + "smallvec", "thiserror 2.0.12", ] @@ -2287,7 +2299,7 @@ dependencies = [ "bitflags", "lazy_static", "num-traits", - "rand 0.9.1", + "rand 0.9.2", "rand_chacha", "rand_xorshift", "regex-syntax", @@ -2419,9 +2431,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", "rand_core 0.9.3", @@ -2495,9 +2507,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags", ] @@ -2588,9 +2600,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.20" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "base64", "bytes", @@ -2652,9 +2664,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -2673,15 +2685,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -2731,9 +2743,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1375ba8ef45a6f15d83fa8748f1079428295d403d6ea991d09ab100155fbc06d" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" dependencies = [ "dyn-clone", "ref-cast", @@ -2812,7 +2824,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.10.0", "schemars 0.9.0", - "schemars 1.0.3", + "schemars 1.0.4", "serde", "serde_derive", "serde_json", @@ -2911,12 +2923,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.10" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2948,22 +2960,21 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", "syn", ] [[package]] name = "syn" -version = "2.0.103" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -3123,16 +3134,18 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.1" +version = "1.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35" dependencies = [ "backtrace", + "io-uring", "libc", "mio", "pin-project-lite", + "slab", "socket2", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3210,9 +3223,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -3643,20 +3656,20 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets", + "windows-targets 0.53.3", ] [[package]] @@ -3668,13 +3681,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -3687,6 +3717,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -3699,6 +3735,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -3711,12 +3753,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -3729,6 +3783,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -3741,6 +3801,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -3753,6 +3819,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -3765,11 +3837,17 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -3839,18 +3917,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 3d81909cd6..fe805da85a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,7 +95,7 @@ wyhash = "0.6.0" # These public dependencies usually require breaking changes downstream, so we # try to be as permissive as possible. pyo3 = ">= 0.23.4, < 0.25" -portgraph = { version = "0.15.1" } +portgraph = { version = "0.15.2" } petgraph = { version = ">= 0.8.1, < 0.9", default-features = false } [profile.dev.package] diff --git a/hugr-core/src/hugr/views/sibling_subgraph.rs b/hugr-core/src/hugr/views/sibling_subgraph.rs index bb7a53ad15..de8e886c4d 100644 --- a/hugr-core/src/hugr/views/sibling_subgraph.rs +++ b/hugr-core/src/hugr/views/sibling_subgraph.rs @@ -10,7 +10,8 @@ use std::mem; use itertools::Itertools; use portgraph::LinkView; -use portgraph::algorithms::ConvexChecker; +use portgraph::algorithms::CreateConvexChecker; +use portgraph::algorithms::convex::{LineIndex, LineIntervals, Position}; use portgraph::boundary::Boundary; use portgraph::{Direction, PortView, view::Subgraph}; use thiserror::Error; @@ -174,6 +175,7 @@ impl SiblingSubgraph { /// /// Refer to [`SiblingSubgraph::try_new`] for the full /// documentation. + // TODO(breaking): generalize to any convex checker pub fn try_new_with_checker>( inputs: IncomingPorts, outputs: OutgoingPorts, @@ -241,6 +243,7 @@ impl SiblingSubgraph { /// /// Refer to [`SiblingSubgraph::try_from_nodes`] for the full /// documentation. + // TODO(breaking): generalize to any convex checker pub fn try_from_nodes_with_checker>( nodes: impl Into>, hugr: &H, @@ -279,6 +282,67 @@ impl SiblingSubgraph { Self::try_new_with_checker(inputs, outputs, hugr, checker) } + /// Create a subgraph from a set of nodes, using a line convexity checker + /// and pre-computed line intervals. + /// + /// Note that passing intervals that do not correspond to the intervals of + /// the nodes is undefined behaviour. + /// + /// This function is equivalent to [`SiblingSubgraph::try_from_nodes`] + /// or [`SiblingSubgraph::try_new_with_checker`] but is provided for + /// performance reasons, in cases where line intervals for the nodes have + /// already been computed. + /// + /// Refer to [`SiblingSubgraph::try_from_nodes`] for the full + /// documentation. + pub fn try_from_nodes_with_intervals( + nodes: impl Into>, + intervals: &LineIntervals, + line_checker: &LineConvexChecker>, + ) -> Result> { + if !line_checker.get_checker().is_convex_by_intervals(intervals) { + return Err(InvalidSubgraph::NotConvex); + } + + let nodes = nodes.into(); + let hugr = line_checker.hugr(); + + if nodes.is_empty() { + return Err(InvalidSubgraph::EmptySubgraph); + } + + let nodes_set = nodes.iter().copied().collect::>(); + let incoming_edges = nodes + .iter() + .flat_map(|&n| hugr.node_inputs(n).map(move |p| (n, p))); + let outgoing_edges = nodes + .iter() + .flat_map(|&n| hugr.node_outputs(n).map(move |p| (n, p))); + let inputs = incoming_edges + .filter(|&(n, p)| { + if !hugr.is_linked(n, p) { + return false; + } + let (out_n, _) = hugr.single_linked_output(n, p).unwrap(); + !nodes_set.contains(&out_n) + }) + // Every incoming edge is its own input. + .map(|p| vec![p]) + .collect_vec(); + let outputs = outgoing_edges + .filter(|&(n, p)| { + hugr.linked_ports(n, p) + .any(|(n1, _)| !nodes_set.contains(&n1)) + }) + .collect_vec(); + + Ok(Self { + nodes, + inputs, + outputs, + }) + } + /// Create a subgraph containing a single node. /// /// The subgraph signature will be given by signature of the node. @@ -598,19 +662,49 @@ type CheckerRegion<'g, Base> = /// /// This can be used when constructing multiple sibling subgraphs to speed up /// convexity checking. -pub struct TopoConvexChecker<'g, Base: 'g + HugrView> { +/// +/// This a good default choice for most convexity checking use cases. +pub type TopoConvexChecker<'g, Base> = + ConvexChecker<'g, Base, portgraph::algorithms::TopoConvexChecker>>; + +/// Precompute convexity information for a HUGR. +/// +/// This can be used when constructing multiple sibling subgraphs to speed up +/// convexity checking. +/// +/// This is a good choice for checking convexity of circuit-like graphs, particularly +/// when many checks must be performed. +pub type LineConvexChecker<'g, Base> = + ConvexChecker<'g, Base, portgraph::algorithms::LineConvexChecker>>; + +/// Precompute convexity information for a HUGR. +/// +/// This can be used when constructing multiple sibling subgraphs to speed up +/// convexity checking. +/// +/// This type is generic over the convexity checker used. If checking convexity +/// for circuit-like graphs, use [`LineConvexChecker`], otherwise use +/// [`TopoConvexChecker`]. +pub struct ConvexChecker<'g, Base: HugrView, Checker> { /// The base HUGR to check convexity on. base: &'g Base, /// The parent of the region where we are checking convexity. region_parent: Base::Node, /// A lazily initialized convexity checker, along with a map from nodes in the region to `Base` nodes. - checker: OnceCell<( - portgraph::algorithms::TopoConvexChecker>, - Base::RegionPortgraphNodes, - )>, + checker: OnceCell<(Checker, Base::RegionPortgraphNodes)>, +} + +impl<'g, Base: HugrView, Checker: Clone> Clone for ConvexChecker<'g, Base, Checker> { + fn clone(&self) -> Self { + Self { + base: self.base, + region_parent: self.region_parent, + checker: self.checker.clone(), + } + } } -impl<'g, Base: HugrView> TopoConvexChecker<'g, Base> { +impl<'g, Base: HugrView, Checker> ConvexChecker<'g, Base, Checker> { /// Create a new convexity checker. pub fn new(base: &'g Base, region_parent: Base::Node) -> Self { Self { @@ -620,31 +714,35 @@ impl<'g, Base: HugrView> TopoConvexChecker<'g, Base> { } } + /// Create a new convexity checker for the region of the entrypoint. + #[inline(always)] + pub fn from_entrypoint(base: &'g Base) -> Self { + let region_parent = base.entrypoint(); + Self::new(base, region_parent) + } + + /// The base HUGR to check convexity on. + pub fn hugr(&self) -> &'g Base { + self.base + } +} + +impl<'g, Base, Checker> ConvexChecker<'g, Base, Checker> +where + Base: HugrView, + Checker: CreateConvexChecker>, +{ /// Returns the portgraph convexity checker, initializing it if necessary. - fn init_checker( - &self, - ) -> &( - portgraph::algorithms::TopoConvexChecker>, - Base::RegionPortgraphNodes, - ) { + fn init_checker(&self) -> &(Checker, Base::RegionPortgraphNodes) { self.checker.get_or_init(|| { let (region, node_map) = self.base.region_portgraph(self.region_parent); - let checker = portgraph::algorithms::TopoConvexChecker::new(region); + let checker = Checker::new_convex_checker(region); (checker, node_map) }) } - /// Returns the portgraph convexity checker, initializing it if necessary. - fn get_checker( - &self, - ) -> &portgraph::algorithms::TopoConvexChecker< - portgraph::view::FlatRegion<'g, Base::RegionPortgraph<'g>>, - > { - &self.init_checker().0 - } - /// Return the portgraph and node map on which convexity queries are performed. - fn region_portgraph(&self) -> (CheckerRegion<'g, Base>, &Base::RegionPortgraphNodes) { + fn region_portgraph(&self) -> (&CheckerRegion<'g, Base>, &Base::RegionPortgraphNodes) { let (checker, node_map) = self.init_checker(); (checker.graph(), node_map) } @@ -654,9 +752,18 @@ impl<'g, Base: HugrView> TopoConvexChecker<'g, Base> { fn get_node_map(&self) -> &Base::RegionPortgraphNodes { &self.init_checker().1 } + + /// Returns the portgraph convexity checker, initializing it if necessary. + fn get_checker(&self) -> &Checker { + &self.init_checker().0 + } } -impl ConvexChecker for TopoConvexChecker<'_, Base> { +impl<'g, Base, Checker> portgraph::algorithms::ConvexChecker for ConvexChecker<'g, Base, Checker> +where + Base: HugrView, + Checker: CreateConvexChecker>, +{ fn is_convex( &self, nodes: impl IntoIterator, @@ -673,6 +780,85 @@ impl ConvexChecker for TopoConvexChecker<'_, Base> { } } +impl<'g, Base: HugrView> LineConvexChecker<'g, Base> { + /// Return the line intervals occupied by the given nodes in the region. + pub fn get_intervals_from_nodes( + &self, + nodes: impl IntoIterator, + ) -> Option { + let (checker, node_map) = self.init_checker(); + let nodes = nodes + .into_iter() + .map(|n| node_map.to_portgraph(n)) + .collect_vec(); + checker.get_intervals_from_nodes(nodes) + } + + /// Return the line intervals defined by the given boundary ports in the + /// region. + /// + /// Incoming ports correspond to boundary inputs, outgoing ports correspond + /// to boundary outputs. + pub fn get_intervals_from_boundary_ports( + &self, + ports: impl IntoIterator, + ) -> Option { + let (checker, node_map) = self.init_checker(); + let ports = ports + .into_iter() + .map(|(n, p)| { + let node = node_map.to_portgraph(n); + checker + .graph() + .port_index(node, p.pg_offset()) + .expect("valid port") + }) + .collect_vec(); + checker.get_intervals_from_boundary_ports(ports) + } + + /// Return the nodes that are within the given line intervals. + pub fn nodes_in_intervals<'a>( + &'a self, + intervals: &'a LineIntervals, + ) -> impl Iterator + 'a { + let (checker, node_map) = self.init_checker(); + checker + .nodes_in_intervals(intervals) + .map(|pg_node| node_map.from_portgraph(pg_node)) + } + + /// Get the lines passing through the given port. + pub fn lines_at_port(&self, node: Base::Node, port: impl Into) -> &[LineIndex] { + let (checker, node_map) = self.init_checker(); + let port = checker + .graph() + .port_index(node_map.to_portgraph(node), port.into().pg_offset()) + .expect("valid port"); + checker.lines_at_port(port) + } + + /// Extend the given intervals to include the given node. + /// + /// Return whether the interval was successfully extended to contain `node`, + /// i.e. whether adding `node` to the subgraph represented by the intervals + /// results in another subgraph that can be expressed as line intervals. + /// + /// If `false` is returned, the `intervals` are left unchanged. + pub fn try_extend_intervals(&self, intervals: &mut LineIntervals, node: Base::Node) -> bool { + let (checker, node_map) = self.init_checker(); + let node = node_map.to_portgraph(node); + checker.try_extend_intervals(intervals, node) + } + + /// Get the position of a node on its lines. + pub fn get_position(&self, node: Base::Node) -> Position { + let (checker, node_map) = self.init_checker(); + let node = node_map.to_portgraph(node); + checker.get_position(node) + } +} + /// The type of all ports in the iterator. /// /// If the array is empty or a port does not exist, returns `None`. @@ -871,6 +1057,14 @@ fn has_other_edge(hugr: &H, node: H::Node, dir: Direction) -> bool op.other_port_kind(dir).is_some() && hugr.is_linked(node, op.other_port(dir).unwrap()) } +impl<'a, 'c, G: HugrView, Checker: Clone> From<&'a ConvexChecker<'c, G, Checker>> + for std::borrow::Cow<'a, ConvexChecker<'c, G, Checker>> +{ + fn from(value: &'a ConvexChecker<'c, G, Checker>) -> Self { + Self::Borrowed(value) + } +} + /// Errors that can occur while constructing a [`SimpleReplacement`]. #[derive(Debug, Clone, PartialEq, Error)] #[non_exhaustive] @@ -1439,4 +1633,39 @@ mod tests { // Should still have one output assert_eq!(subgraph.outgoing_ports().len(), 1); } + + #[test] + fn test_try_from_nodes_with_intervals() { + let (hugr, func_root) = build_3not_hugr().unwrap(); + let line_checker = LineConvexChecker::new(&hugr, func_root); + let [inp, _out] = hugr.get_io(func_root).unwrap(); + let not1 = hugr.output_neighbours(inp).exactly_one().ok().unwrap(); + let not2 = hugr.output_neighbours(not1).exactly_one().ok().unwrap(); + + let intervals = line_checker.get_intervals_from_nodes([not1, not2]).unwrap(); + let subgraph = + SiblingSubgraph::try_from_nodes_with_intervals([not1, not2], &intervals, &line_checker) + .unwrap(); + let exp_subgraph = SiblingSubgraph::try_from_nodes([not1, not2], &hugr).unwrap(); + + assert_eq!(subgraph, exp_subgraph); + assert_eq!( + line_checker.nodes_in_intervals(&intervals).collect_vec(), + [not1, not2] + ); + + let intervals2 = line_checker + .get_intervals_from_boundary_ports([ + (not1, IncomingPort::from(0).into()), + (not2, OutgoingPort::from(0).into()), + ]) + .unwrap(); + let subgraph2 = SiblingSubgraph::try_from_nodes_with_intervals( + [not1, not2], + &intervals2, + &line_checker, + ) + .unwrap(); + assert_eq!(subgraph2, exp_subgraph); + } }