diff --git a/Cargo.lock b/Cargo.lock index 79cc68120..198ffcd7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,6 +261,16 @@ dependencies = [ "clap_derive", ] +[[package]] +name = "clap-verbosity-flag" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63d19864d6b68464c59f7162c9914a0b569ddc2926b4a2d71afe62a9738eff53" +dependencies = [ + "clap", + "log", +] + [[package]] name = "clap_builder" version = "4.5.17" @@ -291,6 +301,21 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +[[package]] +name = "clio" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7fc6734af48458f72f5a3fa7b840903606427d98a710256e808f76a965047d9" +dependencies = [ + "cfg-if", + "clap", + "is-terminal", + "libc", + "tempfile", + "walkdir", + "windows-sys 0.42.0", +] + [[package]] name = "colorchoice" version = "1.0.1" @@ -567,6 +592,22 @@ dependencies = [ "typeid", ] +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "fixedbitset" version = "0.4.2" @@ -760,6 +801,21 @@ dependencies = [ "hugr-passes", ] +[[package]] +name = "hugr-cli" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6df2f78a68df30280cc351522dee4d537707478d216758aa7404cbeb13aa770" +dependencies = [ + "clap", + "clap-verbosity-flag", + "clio", + "hugr-core", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "hugr-core" version = "0.9.1" @@ -955,9 +1011,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -1516,6 +1578,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -1682,6 +1757,19 @@ version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2" +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "1.0.63" @@ -1848,8 +1936,12 @@ dependencies = [ name = "tket2-hseries" version = "0.2.0" dependencies = [ + "clap", + "clap-verbosity-flag", + "clio", "cool_asserts", "hugr", + "hugr-cli", "itertools 0.13.0", "lazy_static", "petgraph", @@ -2191,6 +2283,21 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -2209,6 +2316,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.42.2" diff --git a/ms3.json b/ms3.json new file mode 100644 index 000000000..873d04cd0 --- /dev/null +++ b/ms3.json @@ -0,0 +1 @@ +{"version":"live","nodes":[{"parent":0,"op":"Module"},{"parent":8,"op":"DataflowBlock","inputs":[],"other_outputs":[],"sum_rows":[[]],"extension_delta":[]},{"parent":8,"op":"ExitBlock","cfg_outputs":[]},{"parent":7,"op":"Input","types":[]},{"parent":1,"op":"Input","types":[]},{"parent":1,"op":"Output","types":[{"t":"Sum","s":"Unit","size":1}]},{"parent":7,"op":"Output","types":[]},{"parent":0,"op":"FuncDefn","name":"main","signature":{"params":[],"body":{"t":"G","input":[],"output":[],"extension_reqs":[]}}},{"parent":7,"op":"CFG","signature":{"t":"G","input":[],"output":[],"extension_reqs":[]}},{"parent":1,"op":"CustomOp","extension":"quantum.tket2","name":"QAlloc","signature":{"t":"G","input":[],"output":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"extension_reqs":[]},"description":"","args":[]},{"parent":1,"op":"CustomOp","extension":"quantum.tket2","name":"Reset","signature":{"t":"G","input":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"output":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"extension_reqs":[]},"description":"","args":[]},{"parent":1,"op":"CustomOp","extension":"quantum.tket2","name":"QAlloc","signature":{"t":"G","input":[],"output":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"extension_reqs":[]},"description":"","args":[]},{"parent":1,"op":"CustomOp","extension":"quantum.tket2","name":"Reset","signature":{"t":"G","input":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"output":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"extension_reqs":[]},"description":"","args":[]},{"parent":1,"op":"MakeTuple","tys":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"},{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}]},{"parent":1,"op":"UnpackTuple","tys":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"},{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}]},{"parent":1,"op":"Const","v":{"v":"Extension","extensions":["arithmetic.float.types"],"typ":{"t":"Opaque","extension":"arithmetic.float.types","id":"float64","args":[],"bound":"C"},"value":{"c":"ConstF64","v":{"value":3.1415}}}},{"parent":1,"op":"LoadConstant","datatype":{"t":"Opaque","extension":"arithmetic.float.types","id":"float64","args":[],"bound":"C"}},{"parent":1,"op":"CustomOp","extension":"quantum.tket2","name":"RzF64","signature":{"t":"G","input":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"},{"t":"Opaque","extension":"arithmetic.float.types","id":"float64","args":[],"bound":"C"}],"output":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"extension_reqs":[]},"description":"","args":[]},{"parent":1,"op":"CustomOp","extension":"quantum.tket2","name":"ZZMax","signature":{"t":"G","input":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"},{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"output":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"},{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"extension_reqs":[]},"description":"","args":[]},{"parent":1,"op":"MakeTuple","tys":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"},{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}]},{"parent":1,"op":"UnpackTuple","tys":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"},{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}]},{"parent":1,"op":"CustomOp","extension":"quantum.tket2","name":"Measure","signature":{"t":"G","input":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"output":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"},{"t":"Sum","s":"Unit","size":2}],"extension_reqs":[]},"description":"","args":[]},{"parent":1,"op":"CustomOp","extension":"quantum.tket2","name":"QFree","signature":{"t":"G","input":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"output":[],"extension_reqs":[]},"description":"","args":[]},{"parent":1,"op":"CustomOp","extension":"quantum.tket2","name":"Measure","signature":{"t":"G","input":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"output":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"},{"t":"Sum","s":"Unit","size":2}],"extension_reqs":[]},"description":"","args":[]},{"parent":1,"op":"CustomOp","extension":"quantum.tket2","name":"QFree","signature":{"t":"G","input":[{"t":"Opaque","extension":"prelude","id":"qubit","args":[],"bound":"A"}],"output":[],"extension_reqs":[]},"description":"","args":[]},{"parent":1,"op":"Tag","tag":0,"variants":[[]]}],"edges":[[[1,0],[2,null]],[[9,0],[10,0]],[[10,0],[13,0]],[[11,0],[12,0]],[[12,0],[13,1]],[[13,0],[14,0]],[[14,0],[17,0]],[[14,1],[18,1]],[[15,0],[16,0]],[[16,0],[17,1]],[[17,0],[18,0]],[[18,0],[19,0]],[[18,1],[19,1]],[[19,0],[20,0]],[[20,0],[21,0]],[[20,1],[23,0]],[[21,0],[22,0]],[[23,0],[24,0]],[[25,0],[5,0]]],"metadata":null,"encoder":"hugr-py v0.5.0"} diff --git a/tket2-hseries/Cargo.toml b/tket2-hseries/Cargo.toml index 5534a985a..ad49cd01d 100644 --- a/tket2-hseries/Cargo.toml +++ b/tket2-hseries/Cargo.toml @@ -13,6 +13,14 @@ description = "TKET2 tool for preparing and validating `Hugr`s for compilation t keywords = ["Quantum", "Quantinuum"] categories = ["compilers"] +[features] +default = ["cli"] +cli = ["dep:clap", "dep:hugr-cli", "dep:clap-verbosity-flag", "dep:clio"] + +[[bin]] +name = "tket2-hseries" +required-features = ["cli"] + [dependencies] hugr.workspace = true tket2 = { path = "../tket2", version = "0.2.0" } @@ -24,6 +32,10 @@ strum.workspace = true strum_macros.workspace = true thiserror.workspace = true itertools.workspace = true +clap = { workspace = true, optional = true} +hugr-cli = { version = "*", optional = true } +clap-verbosity-flag = { version = "*", optional = true } +clio = { version = "*", features = ["clap-parse"], optional = true } [dev-dependencies] cool_asserts.workspace = true diff --git a/tket2-hseries/src/bin/tket2-hseries.rs b/tket2-hseries/src/bin/tket2-hseries.rs new file mode 100644 index 000000000..518aeac69 --- /dev/null +++ b/tket2-hseries/src/bin/tket2-hseries.rs @@ -0,0 +1,19 @@ +use clap::Parser as _; +use clap_verbosity_flag::Level; +use tket2_hseries::cli::CliArgs; + +fn main() { + match CliArgs::parse() { + CliArgs::Run(mut args) => { + if let Err(e) = args.run() { + if args.hugr_args.verbosity(Level::Error) { + eprintln!("{}", e); + } + } + } + _ => { + eprintln!("Unknown command"); + std::process::exit(1); + } + }; +} diff --git a/tket2-hseries/src/cli.rs b/tket2-hseries/src/cli.rs new file mode 100644 index 000000000..bef95fcd0 --- /dev/null +++ b/tket2-hseries/src/cli.rs @@ -0,0 +1,107 @@ +use std::error::Error; +use std::io::Write; + +use clap::{Parser, ValueEnum}; +use clio::Output; + +use hugr::{ + algorithms::validation::ValidationLevel, extension::ExtensionRegistry, hugr::hugrmut::HugrMut, + Hugr, +}; +use hugr_cli::HugrArgs; + +use crate::HSeriesPass; + +/// CLI arguments. +#[derive(Parser, Debug)] +#[clap(version = "1.0", long_about = None)] +#[clap(about = "HUGR CLI tools.")] +#[group(id = "hugr")] +#[non_exhaustive] +pub enum CliArgs { + Run(RunArgs), +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, ValueEnum)] +pub enum Pass { + All, + LazifyMeasure, + ForceOrder, +} + +/// Validate and visualise a HUGR file. +#[derive(Parser, Debug)] +#[clap(version = "1.0", long_about = None)] +#[clap(about = "Validate a HUGR.")] +#[group(id = "hugr")] +#[non_exhaustive] +pub struct RunArgs { + #[command(flatten)] + /// common arguments + pub hugr_args: HugrArgs, + + #[arg(value_enum, default_value = "all")] + pub pass: Pass, + + #[arg(long = "validate", default_value = "true")] + pub validate_pass: bool, +} + +#[derive(Parser, Debug)] +#[clap(version = "1.0", long_about = None)] +#[clap(about = "Validate a HUGR.")] +#[group(id = "hugr")] +#[non_exhaustive] +pub struct RunCommand { + #[command(flatten)] + pub run_args: RunArgs, + + /// Output file '-' for stdout + #[clap(long, short, value_parser, default_value = "-")] + output: Output, +} + +impl RunArgs { + pub fn run(&mut self) -> Result> { + let (hugrs, reg) = self.hugr_args.validate()?; + let Ok::<[_; 1], _>([mut hugr]) = hugrs.try_into() else { + Err("Only one module is supported".to_string())? + }; + let validation_level = if self.validate_pass { + ValidationLevel::WithExtensions + } else { + ValidationLevel::WithoutExtensions + }; + + self.pass.run(&mut hugr, ®, validation_level)?; + Ok(hugr) + } +} + +impl RunCommand { + pub fn run(&mut self) -> Result<(), Box> { + write!( + self.output, + "{}", + serde_json::to_string_pretty(&self.run_args.run()?)? + )?; + Ok(()) + } +} + +impl Pass { + pub fn run( + &self, + hugr: &mut impl HugrMut, + registry: &ExtensionRegistry, + level: ValidationLevel, + ) -> Result<(), Box> { + let pass = HSeriesPass::default().with_validation_level(level); + match self { + Pass::All => pass.run(hugr, registry)?, + Pass::LazifyMeasure => pass.lazify_measure(hugr, registry)?, + Pass::ForceOrder => pass.force_order(hugr, registry)?, + }; + Ok(()) + } +} diff --git a/tket2-hseries/src/lib.rs b/tket2-hseries/src/lib.rs index f53fb6433..63dc87d37 100644 --- a/tket2-hseries/src/lib.rs +++ b/tket2-hseries/src/lib.rs @@ -15,6 +15,9 @@ use thiserror::Error; use extension::{futures::FutureOpDef, hseries::HSeriesOp}; use lazify_measure::{LazifyMeasurePass, LazifyMeasurePassError}; +#[cfg(feature = "cli")] +pub mod cli; + pub mod extension; pub mod lazify_measure; @@ -51,7 +54,26 @@ impl HSeriesPass { hugr: &mut impl HugrMut, registry: &ExtensionRegistry, ) -> Result<(), HSeriesPassError> { - self.lazify_measure().run(hugr, registry)?; + self.lazify_measure(hugr, registry)?; + self.force_order(hugr, registry)?; + Ok(()) + } + + pub fn lazify_measure( + &self, + hugr: &mut impl HugrMut, + registry: &ExtensionRegistry, + ) -> Result<(), LazifyMeasurePassError> { + LazifyMeasurePass::default() + .with_validation_level(self.validation_level) + .run(hugr, registry) + } + + pub fn force_order( + &self, + hugr: &mut impl HugrMut, + registry: &ExtensionRegistry, + ) -> Result<(), HSeriesPassError> { self.validation_level .run_validated_pass(hugr, registry, |hugr, _| { force_order(hugr, hugr.root(), |hugr, node| { @@ -65,14 +87,9 @@ impl HSeriesPass { } else { 0 } - })?; - Ok::<_, HSeriesPassError>(()) - })?; - Ok(()) - } - - fn lazify_measure(&self) -> LazifyMeasurePass { - LazifyMeasurePass::default().with_validation_level(self.validation_level) + }) + .map_err(HSeriesPassError::ForceOrderError) + }) } /// Returns a new `HSeriesPass` with the given [ValidationLevel].