From f38dc96248aa5c563b47fb72627656aea52175ae Mon Sep 17 00:00:00 2001 From: Graham Date: Tue, 20 Jun 2023 18:52:14 -0400 Subject: [PATCH] Simplify eggspressions (#210) * feat: use egg crate to simplify expressions * chore: appease clippy in tests * fix: more rules, simpler analysis data * chore: documentation * fix: more proper error handling * ci: run clippy on --all-targets * fix: sidestep simplification if unwarranted * perf: parse sexpr into recexpr directly, no stringification * fix: simplify C, just derive "ordering" * fix: include percent symbol when turning variable into sexp * chore: rename ConstantFold to Arithmetic * fix: better behaved names in proptests * fix!: no need for evaluating in simplifying * fix: appease 1.70 clippy * fix: suggestions from PR review * fix: whitespace * fix: make rules lazy static; add a few more * fix: get poetry run stubtest to pass * fix: add in some fixes from (closed) #219 * fix: better testing * fix: add currently failing test * fix: appease clippy; more failing tests * fix!: all tests pass now! * fix: some rule and arb_name updates * fix: appease clippy * feat!: remove ordered-float dependency, simplify * fix: simpler simplification, avoid some stringification * fix: move expression simplification to its own file * fix! expression <-> recexpr ourselves without symbolic_expressions * fix: appease clippy * chore: remove Cargo.lock --------- Co-authored-by: kalzoo <22137047+kalzoo@users.noreply.github.com> --- .github/workflows/msrv.yml | 2 +- Cargo.lock | 1710 ----------------- quil-py/.stubtest-allowlist | 1 - quil-rs/Cargo.toml | 1 + quil-rs/benches/corpus.rs | 2 +- .../src/{expression.rs => expression/mod.rs} | 194 +- quil-rs/src/expression/simplification.rs | 565 ++++++ quil-rs/src/hash.rs | 12 +- quil-rs/src/instruction/declaration.rs | 4 - quil-rs/src/parser/expression.rs | 7 +- 10 files changed, 702 insertions(+), 1796 deletions(-) delete mode 100644 Cargo.lock rename quil-rs/src/{expression.rs => expression/mod.rs} (87%) create mode 100644 quil-rs/src/expression/simplification.rs diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index 9f667259..fc86ec2c 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -103,7 +103,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: clippy - args: -- -D warnings + args: --all-targets --all-features -- -D warnings deny: name: Deny diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index e3749b4e..00000000 --- a/Cargo.lock +++ /dev/null @@ -1,1710 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bumpalo" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" - -[[package]] -name = "bytecount" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "ciborium" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" - -[[package]] -name = "ciborium-ll" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "clap" -version = "3.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" -dependencies = [ - "bitflags", - "clap_lex", - "indexmap", - "textwrap", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "console" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "windows-sys 0.42.0", -] - -[[package]] -name = "criterion" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" -dependencies = [ - "anes", - "atty", - "cast", - "ciborium", - "clap", - "criterion-plot", - "itertools", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset 0.8.0", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "ctor" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4056f63fce3b82d852c3da92b08ea59959890813a7f4ce9c0ff85b10cf301b" -dependencies = [ - "quote 1.0.26", - "syn 2.0.13", -] - -[[package]] -name = "dot-writer" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1b11bd5e7e98406c6ff39fbc94d6e910a489b978ce7f17c19fce91a1195b7a" - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.13", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-timer" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "ghost" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77ac7b51b8e6313251737fcef4b1c01a2ea102bde68415b62c0ee9268fec357" -dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.13", -] - -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "indoc" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" - -[[package]] -name = "insta" -version = "1.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a28d25139df397cbca21408bb742cf6837e04cdbebf1b07b760caf971d6a972" -dependencies = [ - "console", - "lazy_static", - "linked-hash-map", - "similar", - "yaml-rust", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "inventory" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7741301a6d6a9b28ce77c0fb77a4eb116b6bc8f3bef09923f7743d059c4157d3" -dependencies = [ - "ctor", - "ghost", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" - -[[package]] -name = "js-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lexical" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7aefb36fd43fef7003334742cbf77b243fcd36418a1d1bdd480d613a67968f6" -dependencies = [ - "lexical-core", -] - -[[package]] -name = "lexical-core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" -dependencies = [ - "lexical-parse-float", - "lexical-parse-integer", - "lexical-util", - "lexical-write-float", - "lexical-write-integer", -] - -[[package]] -name = "lexical-parse-float" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" -dependencies = [ - "lexical-parse-integer", - "lexical-util", - "static_assertions", -] - -[[package]] -name = "lexical-parse-integer" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" -dependencies = [ - "lexical-util", - "static_assertions", -] - -[[package]] -name = "lexical-util" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" -dependencies = [ - "static_assertions", -] - -[[package]] -name = "lexical-write-float" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" -dependencies = [ - "lexical-util", - "lexical-write-integer", - "static_assertions", -] - -[[package]] -name = "lexical-write-integer" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" -dependencies = [ - "lexical-util", - "static_assertions", -] - -[[package]] -name = "libc" -version = "0.2.141" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" - -[[package]] -name = "libm" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "nom_locate" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e299bf5ea7b212e811e71174c5d1a5d065c4c0ad0c8691ecb1f97e3e66025e" -dependencies = [ - "bytecount", - "memchr", - "nom", -] - -[[package]] -name = "num-complex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "windows-sys 0.45.0", -] - -[[package]] -name = "paste" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" - -[[package]] -name = "petgraph" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "plotters" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" - -[[package]] -name = "plotters-svg" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "proc-macro2" -version = "1.0.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proptest" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70" -dependencies = [ - "bit-set", - "bitflags", - "byteorder", - "lazy_static", - "num-traits", - "quick-error 2.0.1", - "rand", - "rand_chacha", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "proptest-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b46295382dc76166cb7cf2bb4a97952464e4b7ed5a43e6cd34e1fec3349ddc" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", -] - -[[package]] -name = "pyo3" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "268be0c73583c183f2b14052337465768c07726936a260f480f0857cb95ba543" -dependencies = [ - "cfg-if", - "indoc", - "inventory", - "libc", - "memoffset 0.6.5", - "num-complex", - "parking_lot", - "pyo3-build-config", - "pyo3-ffi", - "pyo3-macros", - "unindent", -] - -[[package]] -name = "pyo3-build-config" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28fcd1e73f06ec85bf3280c48c67e731d8290ad3d730f8be9dc07946923005c8" -dependencies = [ - "once_cell", - "target-lexicon", -] - -[[package]] -name = "pyo3-ffi" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6cb136e222e49115b3c51c32792886defbfb0adead26a688142b346a0b9ffc" -dependencies = [ - "libc", - "pyo3-build-config", -] - -[[package]] -name = "pyo3-macros" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94144a1266e236b1c932682136dc35a9dee8d3589728f68130c7c3861ef96b28" -dependencies = [ - "proc-macro2 1.0.56", - "pyo3-macros-backend", - "quote 1.0.26", - "syn 1.0.109", -] - -[[package]] -name = "pyo3-macros-backend" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8df9be978a2d2f0cdebabb03206ed73b11314701a5bfe71b0d753b81997777f" -dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 1.0.109", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quil-py" -version = "0.2.0-rc.2" -dependencies = [ - "pyo3", - "pyo3-build-config", - "quil-rs", - "rigetti-pyo3", - "strum", -] - -[[package]] -name = "quil-rs" -version = "0.18.0-rc.2" -dependencies = [ - "criterion", - "dot-writer", - "indexmap", - "insta", - "lexical", - "nom", - "nom_locate", - "num-complex", - "once_cell", - "petgraph", - "proptest", - "proptest-derive", - "regex", - "rstest", - "serde", - "strum", - "thiserror", -] - -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", -] - -[[package]] -name = "quote" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" -dependencies = [ - "proc-macro2 1.0.56", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "rigetti-pyo3" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5ebc489b238aa8229066ea79babf773a8206813c25dae17b9b826f9b935832" -dependencies = [ - "num-complex", - "num-traits", - "paste", - "pyo3", - "time", -] - -[[package]] -name = "rstest" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c9dc66cc29792b663ffb5269be669f1613664e69ad56441fdb895c2347b930" -dependencies = [ - "futures", - "futures-timer", - "rstest_macros", - "rustc_version", -] - -[[package]] -name = "rstest_macros" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5015e68a0685a95ade3eee617ff7101ab6a3fc689203101ca16ebc16f2b89c66" -dependencies = [ - "cfg-if", - "proc-macro2 1.0.56", - "quote 1.0.26", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.37.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustversion" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" - -[[package]] -name = "rusty-fork" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" -dependencies = [ - "fnv", - "quick-error 1.2.3", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "ryu" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "semver" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" - -[[package]] -name = "serde" -version = "1.0.159" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.159" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" -dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.13", -] - -[[package]] -name = "serde_json" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "similar" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck", - "proc-macro2 1.0.56", - "quote 1.0.26", - "rustversion", - "syn 1.0.109", -] - -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" -dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "unicode-ident", -] - -[[package]] -name = "target-lexicon" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" - -[[package]] -name = "tempfile" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.45.0", -] - -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - -[[package]] -name = "thiserror" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" -dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.13", -] - -[[package]] -name = "time" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" -dependencies = [ - "serde", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - -[[package]] -name = "unindent" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" - -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - -[[package]] -name = "walkdir" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 1.0.109", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" -dependencies = [ - "quote 1.0.26", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" -dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 1.0.109", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" - -[[package]] -name = "web-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -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-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] diff --git a/quil-py/.stubtest-allowlist b/quil-py/.stubtest-allowlist index 28206eba..5c8526d9 100644 --- a/quil-py/.stubtest-allowlist +++ b/quil-py/.stubtest-allowlist @@ -1,2 +1 @@ quil.quil -quil.validation.quil.validation.identifier diff --git a/quil-rs/Cargo.toml b/quil-rs/Cargo.toml index 045ce6eb..37aff570 100644 --- a/quil-rs/Cargo.toml +++ b/quil-rs/Cargo.toml @@ -21,6 +21,7 @@ serde = { version = "1.0.125", features = ["derive"] } strum.workspace = true thiserror = "1.0.37" once_cell = "1.17.1" +egg = { version = "0.9.4", features = ["deterministic"] } [dev-dependencies] criterion = { version = "0.4.0", features = ["html_reports"] } diff --git a/quil-rs/benches/corpus.rs b/quil-rs/benches/corpus.rs index 8d64ac48..8a990453 100644 --- a/quil-rs/benches/corpus.rs +++ b/quil-rs/benches/corpus.rs @@ -44,7 +44,7 @@ pub fn from_corpus() -> Vec { dir.filter_map(Result::ok) .filter_map(|entry| bench_config_from_file(&entry.path())) - .chain({ bench_config_from_file(&PathBuf::from(SAMPLE_CALIBRATIONS)) }) + .chain(bench_config_from_file(&PathBuf::from(SAMPLE_CALIBRATIONS))) .collect() } diff --git a/quil-rs/src/expression.rs b/quil-rs/src/expression/mod.rs similarity index 87% rename from quil-rs/src/expression.rs rename to quil-rs/src/expression/mod.rs index 9e768ccd..3bc8213a 100644 --- a/quil-rs/src/expression.rs +++ b/quil-rs/src/expression/mod.rs @@ -12,27 +12,30 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::{ + hash::{hash_f64, hash_to_u64}, + parser::{lex, parse_expression, ParseError}, + program::{disallow_leftover, ParseProgramError}, + {imag, instruction::MemoryReference, real}, +}; use lexical::{format, to_string_with_options, WriteFloatOptions}; use nom_locate::LocatedSpan; use num_complex::Complex64; use once_cell::sync::Lazy; -use std::collections::{hash_map::DefaultHasher, HashMap}; -use std::f64::consts::PI; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::num::NonZeroI32; -use std::ops::{ - Add, AddAssign, BitXor, BitXorAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign, +use std::{ + collections::HashMap, + f64::consts::PI, + fmt, + hash::{Hash, Hasher}, + num::NonZeroI32, + ops::{Add, AddAssign, BitXor, BitXorAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}, + str::FromStr, }; -use std::str::FromStr; #[cfg(test)] use proptest_derive::Arbitrary; -use crate::hash::hash_f64; -use crate::parser::{lex, parse_expression, ParseError}; -use crate::program::{disallow_leftover, ParseProgramError}; -use crate::{imag, instruction::MemoryReference, real}; +mod simplification; /// The different possible types of errors that could occur during expression evaluation. #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] @@ -103,13 +106,6 @@ impl PrefixExpression { } } -/// Hash value helper: turn a hashable thing into a u64. -fn hash_to_u64(t: &T) -> u64 { - let mut s = DefaultHasher::new(); - t.hash(&mut s); - s.finish() -} - impl Hash for Expression { // Implemented by hand since we can't derive with f64s hidden inside. // Also to understand when things should be the same, like with commutativity (`1 + 2 == 2 + 1`). @@ -271,47 +267,16 @@ impl Expression { /// assert_eq!(expression, Expression::Number(Complex64::from(3.0))); /// ``` pub fn simplify(&mut self) { - use Expression::*; - match self { - FunctionCall(FunctionCallExpression { - function, - expression, - }) => { - expression.simplify(); - if let Number(number) = expression.as_ref() { - *self = Number(calculate_function(function, number)); - } - } - Infix(InfixExpression { - left, - operator: _, - right, - }) => { - left.simplify(); - right.simplify(); - } - Prefix(PrefixExpression { - operator, - expression, - }) => { - use PrefixOperator::*; - expression.simplify(); - - // Avoid potentially expensive clone - // Cannot directly swap `expression` with `self` because that causes - // a double mutable borrow. - if let Plus = operator { - let mut temp = Expression::PiConstant; - std::mem::swap(expression.as_mut(), &mut temp); - std::mem::swap(self, &mut temp); + Expression::Address(_) + | Expression::Number(_) + | Expression::PiConstant + | Expression::Variable(_) => {} + _ => { + if let Ok(simpler) = simplification::run(self) { + *self = simpler; } } - Variable(_) | Address(_) | PiConstant | Number(_) => {} - }; - - if let Ok(number) = self.evaluate(&HashMap::new(), &HashMap::new()) { - *self = Number(number); } } @@ -651,7 +616,8 @@ impl fmt::Display for PrefixOperator { f, "{}", match self { - Plus => "+", + // NOTE: prefix Plus does nothing but cause parsing issues + Plus => "", Minus => "-", } ) @@ -677,7 +643,8 @@ impl fmt::Display for InfixOperator { match self { Caret => "^", Plus => "+", - Minus => "-", + // NOTE: spaces included to distinguish from hyphenated identifiers + Minus => " - ", Slash => "/", Star => "*", } @@ -687,7 +654,7 @@ impl fmt::Display for InfixOperator { #[cfg(test)] mod tests { - use std::collections::HashSet; + use std::collections::{hash_map::DefaultHasher, HashSet}; use num_complex::Complex64; use proptest::prelude::*; @@ -695,6 +662,7 @@ mod tests { use crate::{ expression::{EvaluationError, Expression, ExpressionFunction}, real, + reserved::ReservedToken, }; use super::*; @@ -786,21 +754,68 @@ mod tests { } } + /// Parenthesized version of [`Expression::to_string()`] + fn parenthesized(expression: &Expression) -> String { + use Expression::*; + match expression { + Address(memory_reference) => format!("({memory_reference})"), + FunctionCall(FunctionCallExpression { + function, + expression, + }) => format!("({function}({}))", parenthesized(expression)), + Infix(InfixExpression { + left, + operator, + right, + }) => format!( + "({}{}{})", + parenthesized(left), + operator, + parenthesized(right) + ), + Number(value) => format!("({})", format_complex(value)), + PiConstant => "pi".to_string(), + Prefix(PrefixExpression { + operator, + expression, + }) => format!("({}{})", operator, parenthesized(expression)), + Variable(identifier) => format!("(%{identifier})"), + } + } + + // Better behaved than the auto-derived version for names + fn arb_name() -> impl Strategy { + r"[a-z][a-zA-Z0-9]{1,10}".prop_filter("Exclude reserved tokens", |t| { + ReservedToken::from_str(t).is_err() + }) + } + + // Better behaved than the auto-derived version re: names & indices + fn arb_memory_reference() -> impl Strategy { + (arb_name(), (u64::MIN..u32::MAX as u64)) + .prop_map(|(name, index)| MemoryReference { name, index }) + } + + // Better behaved than the auto-derived version via arbitrary floats + fn arb_complex64() -> impl Strategy { + let tau = std::f64::consts::TAU; + ((-tau..tau), (-tau..tau)).prop_map(|(re, im)| Complex64 { re, im }) + } + /// Generate an arbitrary Expression for a property test. /// See https://docs.rs/proptest/1.0.0/proptest/prelude/trait.Strategy.html#method.prop_recursive fn arb_expr() -> impl Strategy { use Expression::*; let leaf = prop_oneof![ - any::().prop_map(Address), - (any::(), any::()) - .prop_map(|(re, im)| Number(num_complex::Complex64::new(re, im))), + arb_memory_reference().prop_map(Address), + arb_complex64().prop_map(Number), Just(PiConstant), - ".*".prop_map(Variable), + arb_name().prop_map(Variable), ]; (leaf).prop_recursive( 4, // No more than 4 branch levels deep 64, // Target around 64 total nodes - 2, // Each "collection" is up to 2 elements + 16, // Each "collection" is up to 16 elements |expr| { prop_oneof![ (any::(), expr.clone()).prop_map(|(function, e)| { @@ -827,10 +842,6 @@ mod tests { ) } - fn arb_complex64() -> impl Strategy { - any::<(f64, f64)>().prop_map(|(re, im)| Complex64::new(re, im)) - } - proptest! { #[test] @@ -931,13 +942,19 @@ mod tests { fn complexes_are_parseable_as_expressions(value in arb_complex64()) { let parsed = Expression::from_str(&format_complex(&value)); assert!(parsed.is_ok()); - assert_eq!(Expression::Number(value), parsed.unwrap().into_simplified()); + let simple = parsed.unwrap().into_simplified(); + assert_eq!(Expression::Number(value), simple); } #[test] fn exponentiation_works_as_expected(left in arb_expr(), right in arb_expr()) { let expected = Expression::Infix (InfixExpression { left: Box::new(left.clone()), operator: InfixOperator::Caret, right: Box::new(right.clone()) } ); - prop_assert_eq!(&(left.clone() ^ right.clone()), &expected); + prop_assert_eq!(left ^ right, expected); + } + + #[test] + fn in_place_exponentiation_works_as_expected(left in arb_expr(), right in arb_expr()) { + let expected = Expression::Infix (InfixExpression { left: Box::new(left.clone()), operator: InfixOperator::Caret, right: Box::new(right.clone()) } ); let mut x = left; x ^= right; prop_assert_eq!(x, expected); @@ -946,7 +963,12 @@ mod tests { #[test] fn addition_works_as_expected(left in arb_expr(), right in arb_expr()) { let expected = Expression::Infix (InfixExpression { left: Box::new(left.clone()), operator: InfixOperator::Plus, right: Box::new(right.clone()) } ); - prop_assert_eq!(&(left.clone() + right.clone()), &expected); + prop_assert_eq!(left + right, expected); + } + + #[test] + fn in_place_addition_works_as_expected(left in arb_expr(), right in arb_expr()) { + let expected = Expression::Infix (InfixExpression { left: Box::new(left.clone()), operator: InfixOperator::Plus, right: Box::new(right.clone()) } ); let mut x = left; x += right; prop_assert_eq!(x, expected); @@ -955,7 +977,12 @@ mod tests { #[test] fn subtraction_works_as_expected(left in arb_expr(), right in arb_expr()) { let expected = Expression::Infix (InfixExpression { left: Box::new(left.clone()), operator: InfixOperator::Minus, right: Box::new(right.clone()) } ); - prop_assert_eq!(&(left.clone() - right.clone()), &expected); + prop_assert_eq!(left - right, expected); + } + + #[test] + fn in_place_subtraction_works_as_expected(left in arb_expr(), right in arb_expr()) { + let expected = Expression::Infix (InfixExpression { left: Box::new(left.clone()), operator: InfixOperator::Minus, right: Box::new(right.clone()) } ); let mut x = left; x -= right; prop_assert_eq!(x, expected); @@ -964,7 +991,12 @@ mod tests { #[test] fn multiplication_works_as_expected(left in arb_expr(), right in arb_expr()) { let expected = Expression::Infix (InfixExpression { left: Box::new(left.clone()), operator: InfixOperator::Star, right: Box::new(right.clone()) } ); - prop_assert_eq!(&(left.clone() * right.clone()), &expected); + prop_assert_eq!(left * right, expected); + } + + #[test] + fn in_place_multiplication_works_as_expected(left in arb_expr(), right in arb_expr()) { + let expected = Expression::Infix (InfixExpression { left: Box::new(left.clone()), operator: InfixOperator::Star, right: Box::new(right.clone()) } ); let mut x = left; x *= right; prop_assert_eq!(x, expected); @@ -973,12 +1005,24 @@ mod tests { #[test] fn division_works_as_expected(left in arb_expr(), right in arb_expr()) { let expected = Expression::Infix (InfixExpression { left: Box::new(left.clone()), operator: InfixOperator::Slash, right: Box::new(right.clone()) } ); - prop_assert_eq!(&(left.clone() / right.clone()), &expected); + prop_assert_eq!(left / right, expected); + } + + #[test] + fn in_place_division_works_as_expected(left in arb_expr(), right in arb_expr()) { + let expected = Expression::Infix (InfixExpression { left: Box::new(left.clone()), operator: InfixOperator::Slash, right: Box::new(right.clone()) } ); let mut x = left; x /= right; prop_assert_eq!(x, expected); } + #[test] + fn round_trip(e in arb_expr()) { + let s = parenthesized(&e); + let p = Expression::from_str(&s); + prop_assert!(p.is_ok()); + prop_assert_eq!(p.unwrap().into_simplified(), e.into_simplified()); + } } diff --git a/quil-rs/src/expression/simplification.rs b/quil-rs/src/expression/simplification.rs new file mode 100644 index 00000000..4f4ec196 --- /dev/null +++ b/quil-rs/src/expression/simplification.rs @@ -0,0 +1,565 @@ +/// Complex machinery for simplifying [`Expression`]s. +use crate::{ + expression::{ + format_complex, hash_to_u64, imag, is_small, real, Expression, ExpressionFunction, + FunctionCallExpression, InfixExpression, InfixOperator, MemoryReference, PrefixExpression, + PrefixOperator, + }, + hash::hash_f64, +}; +use egg::{define_language, rewrite as rw, Id, Language, RecExpr}; +use once_cell::sync::Lazy; +use std::{ + cmp::Ordering, + hash::{Hash, Hasher}, + ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, + str::FromStr, +}; + +/// Simplify an [`Expression`]: +/// - turn it into a [`RecExpr`], +/// - let [`egg`] simplify the recursive expression as best as it can, +/// - and turn that back into an [`Expression`] +pub(super) fn run(expression: &Expression) -> Result { + let recexpr = expression_to_recexpr(expression); + let runner = egg::Runner::default().with_expr(&recexpr).run(&(*RULES)); + let root = runner.roots[0]; + let (_, best) = egg::Extractor::new(&runner.egraph, egg::AstSize).find_best(root); + recexpr_to_expression(best) +} + +/// All the myriad ways simplifying an [`Expression`] can fail. +#[derive(Debug, thiserror::Error)] +pub enum SimplificationError { + #[error("Invalid string for a complex number: {0}")] + ComplexParsingError(#[from] num_complex::ParseComplexError), + #[error("Expected a valid index: {0}")] + IndexExpected(#[from] std::num::ParseIntError), + #[error("Invalid string for a memory reference: {0}")] + MemoryReferenceSyntax(#[from] ::Err), +} + +/// An [`egg`]-friendly complex number. +/// We can't just use `num_complex::Complex64`, because we need `Ord` and `Hash`. +/// +/// Fun fact, there is no total ordering on the complex numbers; however, the implementations +/// here are good enough for our purposes. +/// +/// https://en.wikipedia.org/wiki/Complex_number#Ordering +#[derive(Debug, Default, Clone, Copy)] +struct Complex(num_complex::Complex64); + +impl Hash for Complex { + fn hash(&self, state: &mut H) { + // Skip zero values (akin to `format_complex`). + // Also, since f64 isn't hashable, use the u64 binary representation. + // The docs claim this is rather portable: https://doc.rust-lang.org/std/primitive.f64.html#method.to_bits + if self.0.re.abs() > 0f64 { + hash_f64(self.0.re, state) + } + if self.0.im.abs() > 0f64 { + hash_f64(self.0.im, state) + } + } +} + +impl PartialEq for Complex { + // Partial equality by hash value + fn eq(&self, other: &Self) -> bool { + hash_to_u64(self) == hash_to_u64(other) + } +} + +impl Eq for Complex {} + +impl PartialOrd for Complex { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Typical ordering, but with NAN as the biggest value; borrowed the idea from ordered-float +#[inline(always)] +fn _fcmp(x: f64, y: f64) -> Ordering { + if let Some(ordering) = x.partial_cmp(&y) { + ordering + } else { + match (x.is_nan(), y.is_nan()) { + (true, true) => Ordering::Equal, + (false, true) => Ordering::Less, + (true, false) => Ordering::Greater, + (false, false) => unreachable!("These floats should be partially comparable"), + } + } +} + +/// lexicographic ordering with NAN as the biggest value +impl Ord for Complex { + fn cmp(&self, other: &Self) -> Ordering { + match (_fcmp(self.0.re, other.0.re), _fcmp(self.0.im, other.0.im)) { + (Ordering::Less, _) => Ordering::Less, + (Ordering::Greater, _) => Ordering::Greater, + (Ordering::Equal, other) => other, + } + } +} + +impl std::str::FromStr for Complex { + type Err = (); + fn from_str(s: &str) -> Result { + num_complex::Complex64::from_str(s) + .map(Self) + .map_err(|_| ()) + } +} + +impl std::fmt::Display for Complex { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(&format_complex(&self.0)) + } +} + +macro_rules! impl_via_inner { + ($function:ident) => { + fn $function(self) -> Self { + Self(self.0.$function()) + } + }; + ($trait:ident, $function:ident) => { + impl $trait for Complex { + type Output = Self; + fn $function(self) -> Self::Output { + Self(self.0.$function()) + } + } + }; + ($trait:ident, $trait_assign:ident, $function:ident, $function_assign:ident) => { + impl $trait for Complex { + type Output = Self; + fn $function(self, other: Self) -> Self { + Self(self.0.$function(other.0)) + } + } + impl $trait_assign for Complex { + fn $function_assign(&mut self, other: Self) { + *self = Self(self.0.$function(other.0)) + } + } + }; +} + +impl_via_inner!(Neg, neg); +impl_via_inner!(Add, AddAssign, add, add_assign); +impl_via_inner!(Sub, SubAssign, sub, sub_assign); +impl_via_inner!(Mul, MulAssign, mul, mul_assign); +impl_via_inner!(Div, DivAssign, div, div_assign); + +impl Complex { + const ZERO: Self = Self(real!(0.0)); + const PI: Self = Self(real!(std::f64::consts::PI)); + fn close(&self, other: Self) -> bool { + is_small((*self - other).abs()) + } + fn abs(self) -> f64 { + self.0.norm() + } + fn cis(self) -> Self { + // num_complex::Complex64::cis takes a float :-( + Self(self.0.cos() + imag!(1.0) * self.0.sin()) + } + fn pow(self, other: Self) -> Self { + Self(self.0.powc(other.0)) + } + impl_via_inner!(cos); + impl_via_inner!(exp); + impl_via_inner!(sin); + impl_via_inner!(sqrt); +} + +define_language! { + /// An [`egg`]-friendly version of [`Expression`]s, this language allows us to manipulate + /// and simplify terms. + enum Expr { + // Numbers + "pi" = Pi, + Number(Complex), + // Functions + "cis" = Cis(Id), + "cos" = Cos(Id), + "exp" = Exp(Id), + "sin" = Sin(Id), + "sqrt" = Sqrt(Id), + // Prefix arithmetic + "pos" = Pos(Id), + "neg" = Neg(Id), + // Infix arithmetic + "^" = Pow([Id; 2]), + "*" = Mul([Id; 2]), + "/" = Div([Id; 2]), + "+" = Add([Id; 2]), + "-" = Sub([Id; 2]), + // Variables and Addresses + Symbol(egg::Symbol), + } +} + +/// Parse the [`Expression`] into a [`RecExpr`] +fn expression_to_recexpr(expression: &Expression) -> RecExpr { + fn helper(e: &Expression, r: &mut RecExpr) -> Id { + match e { + Expression::Address(m) => { + let expr = Expr::Symbol(m.to_string().into()); + r.add(expr) + } + Expression::FunctionCall(FunctionCallExpression { + function, + expression, + }) => { + let id = helper(expression, r); + let expr = match function { + ExpressionFunction::Cis => Expr::Cis(id), + ExpressionFunction::Cosine => Expr::Cos(id), + ExpressionFunction::Exponent => Expr::Exp(id), + ExpressionFunction::Sine => Expr::Sin(id), + ExpressionFunction::SquareRoot => Expr::Sqrt(id), + }; + r.add(expr) + } + Expression::Infix(InfixExpression { + left, + operator, + right, + }) => { + let ids = [helper(left, r), helper(right, r)]; + let expr = match operator { + InfixOperator::Caret => Expr::Pow(ids), + InfixOperator::Plus => Expr::Add(ids), + InfixOperator::Minus => Expr::Sub(ids), + InfixOperator::Slash => Expr::Div(ids), + InfixOperator::Star => Expr::Mul(ids), + }; + r.add(expr) + } + Expression::Number(x) => r.add(Expr::Number(Complex(*x))), + Expression::Prefix(PrefixExpression { + operator, + expression, + }) => { + let id = helper(expression, r); + let expr = match operator { + PrefixOperator::Plus => Expr::Pos(id), + PrefixOperator::Minus => Expr::Neg(id), + }; + r.add(expr) + } + Expression::PiConstant => r.add(Expr::Pi), + Expression::Variable(v) => r.add(Expr::Symbol(format!("%{v}").into())), + } + } + let mut r = RecExpr::default(); + helper(expression, &mut r); + r +} + +/// Parse the [`RecExpr`] back into an [`Expression`] +/// +/// This returns a [`Result`] rather than just the expression due to some `FromStr` usage in the +/// very last case. +fn recexpr_to_expression(recexpr: RecExpr) -> Result { + fn helper(nodes: &[Expr], i: usize) -> Result { + match nodes[i] { + Expr::Pi => Ok(Expression::PiConstant), + Expr::Number(x) => Ok(Expression::Number(x.0)), + Expr::Cis(id) => { + let expression = Box::new(helper(nodes, id.into())?); + Ok(Expression::FunctionCall(FunctionCallExpression { + function: ExpressionFunction::Cis, + expression, + })) + } + Expr::Cos(id) => { + let expression = Box::new(helper(nodes, id.into())?); + Ok(Expression::FunctionCall(FunctionCallExpression { + function: ExpressionFunction::Cosine, + expression, + })) + } + Expr::Exp(id) => { + let expression = Box::new(helper(nodes, id.into())?); + Ok(Expression::FunctionCall(FunctionCallExpression { + function: ExpressionFunction::Exponent, + expression, + })) + } + Expr::Sin(id) => { + let expression = Box::new(helper(nodes, id.into())?); + Ok(Expression::FunctionCall(FunctionCallExpression { + function: ExpressionFunction::Sine, + expression, + })) + } + Expr::Sqrt(id) => { + let expression = Box::new(helper(nodes, id.into())?); + Ok(Expression::FunctionCall(FunctionCallExpression { + function: ExpressionFunction::SquareRoot, + expression, + })) + } + Expr::Pos(id) => { + let expression = Box::new(helper(nodes, id.into())?); + Ok(Expression::Prefix(PrefixExpression { + operator: PrefixOperator::Plus, + expression, + })) + } + Expr::Neg(id) => { + let expression = Box::new(helper(nodes, id.into())?); + Ok(Expression::Prefix(PrefixExpression { + operator: PrefixOperator::Minus, + expression, + })) + } + Expr::Pow([left_id, right_id]) => { + let left = Box::new(helper(nodes, left_id.into())?); + let right = Box::new(helper(nodes, right_id.into())?); + Ok(Expression::Infix(InfixExpression { + operator: InfixOperator::Caret, + left, + right, + })) + } + Expr::Mul([left_id, right_id]) => { + let left = Box::new(helper(nodes, left_id.into())?); + let right = Box::new(helper(nodes, right_id.into())?); + Ok(Expression::Infix(InfixExpression { + operator: InfixOperator::Star, + left, + right, + })) + } + Expr::Div([left_id, right_id]) => { + let left = Box::new(helper(nodes, left_id.into())?); + let right = Box::new(helper(nodes, right_id.into())?); + Ok(Expression::Infix(InfixExpression { + operator: InfixOperator::Slash, + left, + right, + })) + } + Expr::Add([left_id, right_id]) => { + let left = Box::new(helper(nodes, left_id.into())?); + let right = Box::new(helper(nodes, right_id.into())?); + Ok(Expression::Infix(InfixExpression { + operator: InfixOperator::Plus, + left, + right, + })) + } + Expr::Sub([left_id, right_id]) => { + let left = Box::new(helper(nodes, left_id.into())?); + let right = Box::new(helper(nodes, right_id.into())?); + Ok(Expression::Infix(InfixExpression { + operator: InfixOperator::Minus, + left, + right, + })) + } + Expr::Symbol(sym) => { + let s = sym.to_string(); + match s { + ref x if x.starts_with('%') => Ok(Expression::Variable(s[1..].to_string())), + ref x if x.contains('[') => { + Ok(Expression::Address(MemoryReference::from_str(x)?)) + } + _ => num_complex::Complex64::from_str(&s) + .map(Expression::Number) + .map_err(SimplificationError::ComplexParsingError), + } + } + } + } + let nodes = recexpr.as_ref(); + helper(nodes, nodes.len() - 1) +} + +/// Our analysis will perform arithmetic simplification (largely, constant folding) on our +/// language. +#[derive(Default)] +struct Arithmetic; +type EGraph = egg::EGraph; + +/// Our analysis will perform constant folding on our language. +impl egg::Analysis for Arithmetic { + /// Constant values + type Data = Option; + + /// Pull the (possible) [`Self::Data`] from the given expression. + fn make(egraph: &EGraph, enode: &Expr) -> Self::Data { + let x = |id: &Id| egraph[*id].data.as_ref(); + match enode { + Expr::Pi => Some(Complex::PI), + Expr::Number(c) => Some(*c), + Expr::Cis(id) => Some(x(id)?.cis()), + Expr::Cos(id) => Some(x(id)?.cos()), + Expr::Exp(id) => Some(x(id)?.exp()), + Expr::Sin(id) => Some(x(id)?.sin()), + Expr::Sqrt(id) => Some(x(id)?.sqrt()), + Expr::Pos(id) => Some(*x(id)?), + Expr::Neg(id) => Some(-*x(id)?), + Expr::Pow([base, power]) => Some(x(base)?.pow(*x(power)?)), + Expr::Mul([left, right]) => Some(*x(left)? * *x(right)?), + Expr::Div([left, right]) => Some(*x(left)? / *x(right)?), + Expr::Add([left, right]) => Some(*x(left)? + *x(right)?), + Expr::Sub([left, right]) => Some(*x(left)? - *x(right)?), + Expr::Symbol(_) => None, + } + } + + /// Merge two pieces of data with the same value. + fn merge(&mut self, to: &mut Self::Data, from: Self::Data) -> egg::DidMerge { + egg::merge_option(to, from, |_a, _b| egg::DidMerge(false, false)) + } + + /// Update the graph to equate and simplify constant values. + fn modify(egraph: &mut EGraph, id: Id) { + if let Some(c) = egraph[id].data { + let value = if c.close(Complex::PI) { + Expr::Pi + } else { + Expr::Number(c) + }; + let added = egraph.add(value); + egraph.union(id, added); + egraph[id].nodes.retain(|n| n.is_leaf()); + } + } +} + +/// Is the variable equivalent to zero in the given circumstances? +fn is_not_zero(var: &str) -> impl Fn(&mut EGraph, Id, &egg::Subst) -> bool { + let key = var.parse().unwrap(); + move |egraph, _, subst| { + egraph[subst[key]] + .data + .as_ref() + .map(|value| !value.close(Complex::ZERO)) + .unwrap_or(false) + } +} + +fn is_number(var: &str) -> impl Fn(&mut EGraph, Id, &egg::Subst) -> bool { + let key = var.parse().unwrap(); + move |egraph, _, subst| egraph[subst[key]].data.as_ref().is_some() +} + +/// Rewrite terms of our [`Expr`] language by reducing with our [`Arithmetic`] analysis. +type Rewrite = egg::Rewrite; + +/// Instantiate our rewrite rules for simplifying [`Expr`] terms. +static RULES: Lazy> = Lazy::new(|| { + vec![ + // Largely copied from https://github.com/egraphs-good/egg/blob/82c00e970f0bc1fbfe90ce6dc3c3c79ee919c933/tests/math.rs + // and https://github.com/herbie-fp/herbie/blob/2052806f2ffe0d46bc2e151dd096b127c39e12bd/egg-herbie/src/rules.rs + + // addition & subtraction + rw!("add-zero" ; "(+ ?a 0)" => "?a"), + rw!("zero-add" ; "(+ 0 ?a)" => "?a"), + rw!("cancel-sub" ; "(- ?a ?a)" => "0"), + // multiplication & division + rw!("mul-zero" ; "(* ?a 0)" => "0"), + rw!("zero-mul" ; "(* 0 ?a)" => "0"), + rw!("one-mul" ; "(* 1 ?a)" => "?a"), + rw!("mul-one" ; "(* ?a 1)" => "?a"), + rw!("one-div" ; "(/ ?a 1)" => "?a"), + rw!("cancel-div" ; "(/ ?a ?a)" => "1" if is_not_zero("?a")), + // + & * + rw!("distribute" ; "(* ?a (+ ?b ?c))" => "(+ (* ?a ?b) (* ?a ?c))"), + rw!("factor" ; "(+ (* ?a ?b) (* ?a ?c))" => "(* ?a (+ ?b ?c))"), + // pow & sqrt + rw!("pow0" ; "(^ ?a 0)" => "1" if is_not_zero("?a")), + rw!("pow1" ; "(^ ?a 1)" => "?a"), + rw!("pow2" ; "(^ ?a 2)" => "(* ?a ?a)"), + rw!("pow2-neg" ; "(^ (neg ?a) 2)" => "(* ?a ?a)"), + rw!("pow2-sqrt" ; "(^ (sqrt ?a) 2)" => "?a"), + rw!("sqrt-pow2" ; "(sqrt (^ ?a 2))" => "?a"), + rw!("pow1/2" ; "(^ ?a 0.5)" => "(sqrt ?a)"), + rw!("div-mul" ; "(/ ?a (* ?b ?a))" => "?b" if is_not_zero("?a")), + rw!("div-mul-2" ; "(/ (* ?b ?a) ?a)" => "?b" if is_not_zero("?a")), + rw!("mul-div" ; "(* ?a (/ ?b ?a))" => "?b" if is_not_zero("?a")), + rw!("mul-div-2" ; "(* (/ ?b ?a) ?a)" => "?b" if is_not_zero("?a")), + rw!("pow-mul" ; "(* (^ ?a ?b) (^ ?a ?c))" => "(^ ?a (+ ?b ?c))"), + rw!("mul-pow" ; "(^ ?a (+ ?b ?c))" => "(* (^ ?a ?b) (^ ?a ?c))"), + // pos and neg + rw!("pos-canon" ; "(pos ?a)" => "?a"), + rw!("sub-neg" ; "(- ?a (neg ?b))" => "(+ ?a ?b)"), + rw!("neg-canon" ; "(neg ?a)" => "-?a" if is_number("?a")), + // exp + rw!("exp-zero" ; "(exp 0)" => "1"), + rw!("exp-neg" ; "(exp (neg ?a))" => "(/ 1 (exp ?a))"), + ] +}); + +#[cfg(test)] +mod tests { + use super::*; + + egg::test_fn! { + docstring_example, + &RULES, + "(+ (cos (* 2 pi)) 2)" => "3" + } + + egg::test_fn! { + issue_208_1, + &RULES, + "(* 0 theta)" => "0" + } + + egg::test_fn! { + issue_208_2, + &RULES, + "(/ theta 1)" => "theta" + } + + egg::test_fn! { + issue_208_3, + &RULES, + "(/ (* theta 5) 5)" => "theta" + } + + egg::test_fn! { + memory_ref, + &RULES, + "theta[0]" => "theta[0]" + } + + egg::test_fn! { + var, + &RULES, + "%foo" => "%foo" + } + + egg::test_fn! { + prefix_neg, + &RULES, + "(neg -1)" => "1" + } + + egg::test_fn! { + neg_sub, + &RULES, + "(neg (- 1 2))" => "1" + } + + egg::test_fn! { + neg_imag, + &RULES, + "(neg 9.48e42i)" => "-9.48e42i" + } + + egg::test_fn! { + pow_neg_address, + &RULES, + "(^ (neg 9.48e42i) A[9])" => "(^ -9.48e42i A[9]))" + } +} diff --git a/quil-rs/src/hash.rs b/quil-rs/src/hash.rs index 2a8221ad..7fbea4e3 100644 --- a/quil-rs/src/hash.rs +++ b/quil-rs/src/hash.rs @@ -1,5 +1,15 @@ -use std::hash::{Hash, Hasher}; +use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, +}; +/// Hash value helper: turn a hashable thing into a u64. +#[inline] +pub(crate) fn hash_to_u64(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} /// Hashes a f64 using its u64 representation. /// /// Notes: diff --git a/quil-rs/src/instruction/declaration.rs b/quil-rs/src/instruction/declaration.rs index 39cd006d..c7eadbae 100644 --- a/quil-rs/src/instruction/declaration.rs +++ b/quil-rs/src/instruction/declaration.rs @@ -2,9 +2,6 @@ use std::{fmt, str::FromStr}; use nom_locate::LocatedSpan; -#[cfg(test)] -use proptest_derive::Arbitrary; - use crate::{ parser::{common::parse_memory_reference, lex, ParseError}, program::{disallow_leftover, SyntaxError}, @@ -164,7 +161,6 @@ mod test_declaration { } #[derive(Clone, Debug, Hash, PartialEq)] -#[cfg_attr(test, derive(Arbitrary))] pub struct MemoryReference { pub name: String, pub index: u64, diff --git a/quil-rs/src/parser/expression.rs b/quil-rs/src/parser/expression.rs index e2995b2f..cae8ef4a 100644 --- a/quil-rs/src/parser/expression.rs +++ b/quil-rs/src/parser/expression.rs @@ -33,6 +33,7 @@ enum Precedence { Lowest, Sum, Product, + Exponentiation, Call, } @@ -52,7 +53,7 @@ impl From<&Operator> for Precedence { match operator { Operator::Plus | Operator::Minus => Precedence::Sum, Operator::Star | Operator::Slash => Precedence::Product, - _ => Precedence::Lowest, + Operator::Caret => Precedence::Exponentiation, } } } @@ -283,7 +284,7 @@ mod tests { "cis(%theta)", "%a+%b", "(pi/2)+(1*theta[0])", - "3--2", + "3 - -2", ]; for case in cases { @@ -399,7 +400,7 @@ mod tests { operator: InfixOperator::Minus, right: Box::new(Expression::Prefix(PrefixExpression { operator: PrefixOperator::Minus, - expression: Box::new(Expression::Number(real!(2.0))) + expression: Box::new(Expression::Number(real!(2f64))) })) }) );