diff --git a/.gitignore b/.gitignore index 224fdc61..ce00a049 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ /target node_modules Cargo.lock +.venv +.DS_Store # JetBrains Editors -.idea/ \ No newline at end of file +.idea/ diff --git a/.gitmodules b/.gitmodules index 4769fdae..c9d91faf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "benches/quilc"] - path = benches/quilc +[submodule "quil-rs/benches/quilc"] + path = quil-rs/benches/quilc url = https://github.com/quil-lang/quilc.git diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..e7a124db --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,34 @@ +# Contributing to quil-rs and quil-py + +Welcome to the `quil-rs` project, and thanks for contributing! + +This guide is to help walk you through contributing in different ways to `quil-rs` and `quil-py`, +as well as share some general how-tos for development, testing, and maintenance. + +## Table of Contents + +[Developer Guidelines](#developer-guidelines) + +- [Instruction Module](#instruction-module) + +## Developer Guidelines + +### Instruction Module + +The `instruction` module is large, so we follow a few guidelines when adding or changing instructions to help keep +them organized and consistent in both the `quil-rs` and `quil-py` crates. + +All instruction definitions live inside of the `instruction` module. For ease of development, +instruction types are grouped into submodules so that similar instructions can be modified and viewed together +rather than having all instructions in one large file. However, to avoid imposing potentially arbritrary +instruction categories onto the user, the `instruction` module publically re-exports all of its types from +its root. + +This module structure should remain consistent between both `quil-rs` and `quil-py`. For example, the file +`src/instruction/gate.rs` exists in both `quil-rs` and `quil-py` and contains the same set of instruction +definitions (for their respective target language, of course). + +In short, an instruction should: + 1. Be organized in the equivalent `instruction` submodule for both `quil-rs` and `quil-py` crates + 2. Only be re-exported publicly from the root of the `Instruction` module + diff --git a/Cargo.lock b/Cargo.lock index 13364582..c6c81798 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # 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" @@ -14,7 +23,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -48,9 +57,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "bytecount" @@ -126,15 +135,14 @@ dependencies = [ [[package]] name = "console" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c" +checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" dependencies = [ "encode_unicode", "lazy_static", "libc", - "terminal_size", - "winapi", + "windows-sys 0.42.0", ] [[package]] @@ -196,37 +204,47 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.11" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset", + "memoffset 0.7.1", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", ] +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote 1.0.23", + "syn 1.0.108", +] + [[package]] name = "dot-writer" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1defb949b123415131ecae40ab5dca70e9f39eedda3de2cebb03a8a98fdf5894" +checksum = "3d1b11bd5e7e98406c6ff39fbc94d6e910a489b978ce7f17c19fce91a1195b7a" [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "encode_unicode" @@ -236,9 +254,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -257,9 +275,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "futures" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ "futures-channel", "futures-core", @@ -272,9 +290,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -282,15 +300,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -299,32 +317,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.108", ] [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-timer" @@ -334,9 +352,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-channel", "futures-core", @@ -361,6 +379,17 @@ dependencies = [ "wasi", ] +[[package]] +name = "ghost" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41973d4c45f7a35af8753ba3457cc99d406d863941fd7f52663cff54a5ab99b3" +dependencies = [ + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.108", +] + [[package]] name = "half" version = "1.8.2" @@ -375,9 +404,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -388,21 +417,36 @@ 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 = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" 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.21.0" +version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581d4e3314cae4536e5d22ffd23189d4a374696c5ef733eadafae0ed273fd303" +checksum = "fea5b3894afe466b4bcf0388630fc15e11938a6074af0cd637c825ba2ec8a099" dependencies = [ "console", "lazy_static", @@ -420,6 +464,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "inventory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16fe3b35d64bd1f72917f06425e7573a2f63f74f42c8f56e53ea6826dde3a2b5" +dependencies = [ + "ctor", + "ghost", +] + [[package]] name = "itertools" version = "0.10.5" @@ -431,15 +485,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -525,9 +579,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.137" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "libm" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" [[package]] name = "linked-hash-map" @@ -535,6 +595,16 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[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" @@ -559,6 +629,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -567,9 +646,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", @@ -577,9 +656,9 @@ dependencies = [ [[package]] name = "nom_locate" -version = "4.0.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37794436ca3029a3089e0b95d42da1f0b565ad271e4d3bb4bad0c7bb70b10605" +checksum = "b1e299bf5ea7b212e811e71174c5d1a5d065c4c0ad0c8691ecb1f97e3e66025e" dependencies = [ "bytecount", "memchr", @@ -588,9 +667,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ "num-traits", ] @@ -602,23 +681,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", + "libm", ] [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "once_cell" -version = "1.15.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" @@ -628,15 +708,44 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[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", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "paste" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" [[package]] name = "petgraph" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", "indexmap", @@ -684,9 +793,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" @@ -699,18 +808,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" +checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70" dependencies = [ "bit-set", "bitflags", @@ -724,6 +833,7 @@ dependencies = [ "regex-syntax", "rusty-fork", "tempfile", + "unarray", ] [[package]] @@ -737,6 +847,68 @@ dependencies = [ "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.51", + "pyo3-macros-backend", + "quote 1.0.23", + "syn 1.0.108", +] + +[[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.51", + "quote 1.0.23", + "syn 1.0.108", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -749,6 +921,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +[[package]] +name = "quil-py" +version = "0.1.0" +dependencies = [ + "pyo3", + "pyo3-build-config", + "quil-rs", + "rigetti-pyo3", +] + [[package]] name = "quil-rs" version = "0.16.0-rc.0" @@ -761,9 +943,11 @@ dependencies = [ "nom", "nom_locate", "num-complex", + "once_cell", "petgraph", "proptest", "proptest-derive", + "regex", "rstest", "serde", "strum", @@ -781,11 +965,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ - "proc-macro2 1.0.47", + "proc-macro2 1.0.51", ] [[package]] @@ -829,21 +1013,19 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.3" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -862,18 +1044,20 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "remove_dir_all" @@ -884,6 +1068,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "rigetti-pyo3" +version = "0.1.0-rc.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1278482dd3684b2f648ce34eb2a9183eece8b6660398e2df40fdb3b7fca782a0" +dependencies = [ + "num-complex", + "num-traits", + "paste", + "pyo3", + "time", +] + [[package]] name = "rstest" version = "0.15.0" @@ -903,10 +1100,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5015e68a0685a95ade3eee617ff7101ab6a3fc689203101ca16ebc16f2b89c66" dependencies = [ "cfg-if", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "rustc_version", - "syn 1.0.103", + "syn 1.0.108", ] [[package]] @@ -920,9 +1117,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] name = "rusty-fork" @@ -938,9 +1135,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "same-file" @@ -959,35 +1156,35 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "serde" -version = "1.0.147" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.108", ] [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "itoa", "ryu", @@ -996,19 +1193,25 @@ dependencies = [ [[package]] name = "similar" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803" +checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +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" @@ -1031,10 +1234,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "rustversion", - "syn 1.0.103", + "syn 1.0.108", ] [[package]] @@ -1050,15 +1253,21 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.103" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "d56e159d99e6c2b93995d171050271edb50ecc5288fbc7cc17de8fdce4e58c14" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "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.3.0" @@ -1073,16 +1282,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "terminal_size" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "textwrap" version = "0.16.0" @@ -1091,24 +1290,40 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.108", +] + +[[package]] +name = "time" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2" +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" @@ -1119,11 +1334,17 @@ dependencies = [ "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.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-xid" @@ -1131,6 +1352,12 @@ 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" @@ -1159,9 +1386,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1169,53 +1396,53 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.108", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ - "quote 1.0.21", + "quote 1.0.23", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.108", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -1252,6 +1479,87 @@ 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", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index bf6299fe..b31413fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,35 +1,6 @@ -[package] -name = "quil-rs" -description = "Rust tooling for Quil (Quantum Instruction Language)" -version ="0.16.0-rc.0" -edition = "2021" -license = "Apache-2.0" -repository = "https://github.com/rigetti/quil-rust" -keywords = ["Quil", "Quantum", "Rigetti"] -categories = ["parser-implementations", "science", "compilers", "emulators"] +[workspace] -[dependencies] -dot-writer = { version = "0.1.2", optional = true } -indexmap = "1.6.1" -lexical = "6.1.1" -nom = "7.1.1" -nom_locate = "4.0.0" -num-complex = "0.4.0" -petgraph = "0.6.2" -serde = { version = "1.0.125", features = ["derive"] } -strum = { version = "0.24.1", features = ["derive"] } -thiserror = "1.0.37" - -[dev-dependencies] -criterion = { version = "0.4.0", features = ["html_reports"] } -insta = "1.7.1" -proptest = "1.0.0" -proptest-derive = "0.3.0" -rstest = "0.15.0" - -[features] -graphviz-dot = ["dot-writer"] - -[[bench]] -name = "parser" -harness = false +members = [ + "quil-rs", + "quil-py" +] diff --git a/benches/quilc b/benches/quilc deleted file mode 160000 index a2925c5d..00000000 --- a/benches/quilc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a2925c5dd1764534ef8ea4e66b051cc43cecea30 diff --git a/deny.toml b/deny.toml index 33cdbddc..0716e12d 100644 --- a/deny.toml +++ b/deny.toml @@ -24,14 +24,16 @@ yanked = "deny" notice = "deny" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. -ignore = [] +ignore = [ + "RUSTSEC-2023-0018" # remove_dir_all is used by proptest, a dev dependency +] # This section is considered when running `cargo deny check licenses` # More documentation for the licenses section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html [licenses] unlicensed = "deny" -allow = ["Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "MIT", "Unicode-DFS-2016"] +allow = ["Apache-2.0", "Apache-2.0 WITH LLVM-exception", "BSD-2-Clause", "BSD-3-Clause", "MIT", "Unicode-DFS-2016"] # List of explictly disallowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. @@ -72,6 +74,9 @@ skip-tree = [ { name = "syn", version = "*", depth = 5 }, # Used in both serde_derive and proptest-derive { name = "quick-error", version = "*" }, # proptest relies on two versions of this { name = "itoa", version = "*" }, # various dependencies rely on two versions of this + { name = "hermit-abi", version = "*" }, # various dependencies rely on two versions of this + { name = "memoffset", version = "*" }, # various dependencies rely on two versions of this + { name = "windows-sys", version = "*" }, # various dependencies rely on two versions of this ] # This section is considered when running `cargo deny check sources`. diff --git a/quil-py/Cargo.toml b/quil-py/Cargo.toml new file mode 100644 index 00000000..380d9b29 --- /dev/null +++ b/quil-py/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "quil-py" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +# The name of the native library. This is the name which will be used in Python to import the +# library (i.e. `import quil`). If you change this, you must also change the name of the +# `#[pymodule]` in `src/lib.rs`. +name = "quil" +# "cdylib" is necessary to produce a shared library for Python to import from. +# +# Downstream Rust code (including code in `bin/`, `examples/`, and `tests/`) will not be able +# to `use quil;` unless the "lib" and "rlib" crate type is also included: +crate-type = ["cdylib", "rlib"] + +[dependencies] +quil-rs = { path = "../quil-rs", version="0.16.0-rc.0" } +# pyo3 dependencies should be updated together +pyo3 = { version = "0.17" } +rigetti-pyo3 = "0.1.0-rc.6" + +[build-dependencies] +pyo3-build-config = { version = "0.17" } diff --git a/quil-py/README.md b/quil-py/README.md new file mode 100644 index 00000000..0ca772da --- /dev/null +++ b/quil-py/README.md @@ -0,0 +1,7 @@ +# Quil + +⚠️ In Development + +The `quil` package provides tools for constructing, manipulating, parsing, and printing [Quil](https://github.com/quil-lang/quil) programs. Internally, it is powered by [quil-rs](https://github.com/rigetti/quil-rs). + +This package is still in early development and breaking changes should be expected between minor versions. diff --git a/quil-py/build.rs b/quil-py/build.rs new file mode 100644 index 00000000..dace4a9b --- /dev/null +++ b/quil-py/build.rs @@ -0,0 +1,3 @@ +fn main() { + pyo3_build_config::add_extension_module_link_args(); +} diff --git a/quil-py/pyproject.toml b/quil-py/pyproject.toml new file mode 100644 index 00000000..6c94f4b4 --- /dev/null +++ b/quil-py/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "quil" +requires-python = ">=3.8" +readme = "README.md" +license = { text = "Apache-2.0" } +authors = [ + { name = "Rigetti Computing", email = "softapps@rigetti.com" }, +] +classifiers = [ + "Development Status :: 3 - Alpha", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Operating System :: OS Independent", +] + +[tool.maturin] +features = ["pyo3/extension-module"] +bindings = "pyo3" +compatibility = "linux" +sdist-include = ["README.md"] + +[build-system] +requires = ["maturin>=0.13,<0.14"] +build-backend = "maturin" diff --git a/quil-py/src/instruction/arithmetic.rs b/quil-py/src/instruction/arithmetic.rs new file mode 100644 index 00000000..74a1b2c2 --- /dev/null +++ b/quil-py/src/instruction/arithmetic.rs @@ -0,0 +1,77 @@ +use quil_rs::instruction::{ + Arithmetic, ArithmeticOperand, ArithmeticOperator, BinaryLogic, BinaryOperand, BinaryOperands, + BinaryOperator, +}; + +use rigetti_pyo3::{ + impl_repr, impl_str, py_wrap_data_struct, py_wrap_simple_enum, py_wrap_type, + py_wrap_union_enum, + pyo3::{ + types::{PyFloat, PyInt}, + Py, + }, +}; + +use super::PyMemoryReference; + +py_wrap_simple_enum! { + PyArithmeticOperator(ArithmeticOperator) as "ArithmeticOperator" { + Add, + Subtract, + Divide, + Multiply + } +} +impl_repr!(PyArithmeticOperator); +impl_str!(PyArithmeticOperator); + +py_wrap_union_enum! { + PyArithmeticOperand(ArithmeticOperand) as "ArithmeticOperand" { + literal_integer: LiteralInteger => Py, + literal_real: LiteralReal => Py, + memory_reference: MemoryReference => PyMemoryReference + } +} +impl_repr!(PyArithmeticOperand); +impl_str!(PyArithmeticOperand); + +py_wrap_data_struct! { + PyArithmetic(Arithmetic) as "Arithmetic" { + operator: ArithmeticOperator => PyArithmeticOperator, + destination: ArithmeticOperand => PyArithmeticOperand, + source: ArithmeticOperand => PyArithmeticOperand + } +} +impl_repr!(PyArithmetic); + +py_wrap_union_enum! { + PyBinaryOperator(BinaryOperator) as "BinaryOperator" { + and: And, + ior: Ior, + xor: Xor + } +} +impl_repr!(PyBinaryOperator); +impl_str!(PyBinaryOperator); + +py_wrap_union_enum! { + PyBinaryOperand(BinaryOperand) as "BinaryOperand" { + literal_integer: LiteralInteger => Py, + memory_reference: MemoryReference => PyMemoryReference + } +} +impl_repr!(PyBinaryOperand); +impl_str!(PyBinaryOperand); + +py_wrap_type! { + #[derive(Debug)] + PyBinaryOperands(BinaryOperands) as "BinaryOperands"; +} + +py_wrap_data_struct! { + PyBinaryLogic(BinaryLogic) as "BinaryLogic" { + operator: BinaryOperator => PyBinaryOperator, + operands: BinaryOperands => PyBinaryOperands + } +} +impl_repr!(PyBinaryLogic); diff --git a/quil-py/src/instruction/calibration.rs b/quil-py/src/instruction/calibration.rs new file mode 100644 index 00000000..505a6421 --- /dev/null +++ b/quil-py/src/instruction/calibration.rs @@ -0,0 +1,33 @@ +use quil_rs::{ + expression::Expression, + instruction::{Calibration, GateModifier, Instruction, MeasureCalibrationDefinition, Qubit}, +}; + +use rigetti_pyo3::{ + impl_repr, impl_str, py_wrap_data_struct, + pyo3::{types::PyString, Py}, +}; + +use crate::instruction::{PyExpression, PyGateModifier, PyInstruction, PyQubit}; + +py_wrap_data_struct! { + PyCalibration(Calibration) as "Calibration" { + instructions: Vec => Vec, + modifiers: Vec => Vec, + name: String => Py, + parameters: Vec => Vec, + qubits: Vec => Vec + } +} +impl_repr!(PyCalibration); +impl_str!(PyCalibration); + +py_wrap_data_struct! { + PyMeasureCalibrationDefinition(MeasureCalibrationDefinition) as "MeasureCalibrationDefinition" { + qubit: Option => Option, + parameter: String => Py, + instructions: Vec => Vec + } +} +impl_repr!(PyMeasureCalibrationDefinition); +impl_str!(PyMeasureCalibrationDefinition); diff --git a/quil-py/src/instruction/declaration.rs b/quil-py/src/instruction/declaration.rs new file mode 100644 index 00000000..2aed5fd1 --- /dev/null +++ b/quil-py/src/instruction/declaration.rs @@ -0,0 +1,39 @@ +use quil_rs::instruction::{Declaration, ScalarType, Vector}; + +use rigetti_pyo3::{ + impl_repr, impl_str, py_wrap_data_struct, py_wrap_simple_enum, + pyo3::{ + types::{PyInt, PyString}, + Py, + }, +}; + +py_wrap_simple_enum! { + PyScalarType(ScalarType) as "ScalarType" { + Bit, + Integer, + Octet, + Real + } +} +impl_repr!(PyScalarType); +impl_str!(PyScalarType); + +py_wrap_data_struct! { + PyVector(Vector) as "Vector" { + data_type: ScalarType => PyScalarType, + length: u64 => Py + } +} +impl_repr!(PyVector); +impl_str!(PyVector); + +py_wrap_data_struct! { + PyDeclaration(Declaration) as "Declaration" { + name: String => Py, + size: Vector => PyVector, + sharing: Option => Option> + } +} +impl_repr!(PyDeclaration); +impl_str!(PyDeclaration); diff --git a/quil-py/src/instruction/expression.rs b/quil-py/src/instruction/expression.rs new file mode 100644 index 00000000..6445ce9d --- /dev/null +++ b/quil-py/src/instruction/expression.rs @@ -0,0 +1,94 @@ +use std::str::FromStr; + +use pyo3::types::PyComplex; +use quil_rs::expression::{ + Expression, ExpressionFunction, FunctionCallExpression, InfixExpression, InfixOperator, + PrefixExpression, PrefixOperator, +}; + +use rigetti_pyo3::{ + impl_repr, impl_str, py_wrap_data_struct, py_wrap_simple_enum, py_wrap_union_enum, + pyo3::{exceptions::PyValueError, pymethods, types::PyString, Py, PyResult, Python}, + ToPython, +}; + +use super::PyMemoryReference; + +py_wrap_union_enum! { + PyExpression(Expression) as "Expression" { + address: Address => PyMemoryReference, + function_call: FunctionCall => PyFunctionCallExpression, + infix: Infix => PyInfixExpression, + number: Number => Py, + pi: PiConstant, + prefix: Prefix => PyPrefixExpression, + variable: Variable => Py + } +} +impl_repr!(PyExpression); +impl_str!(PyExpression); + +py_wrap_simple_enum! { + PyExpressionFunction(ExpressionFunction) as "ExpressionFunction" { + Cis, + Cosine, + Exponent, + Sine, + SquareRoot + } +} +impl_repr!(PyExpressionFunction); +impl_str!(PyExpressionFunction); + +py_wrap_data_struct! { + PyFunctionCallExpression(FunctionCallExpression) as "FunctionCallExpression" { + function: ExpressionFunction => PyExpressionFunction, + expression: Box => PyExpression + } +} +impl_repr!(PyFunctionCallExpression); + +#[pymethods] +impl PyExpression { + #[staticmethod] + fn parse_from_str(py: Python<'_>, expression: String) -> PyResult { + Expression::from_str(&expression) + .map_err(|e| PyValueError::new_err(e.to_string()))? + .to_python(py) + } +} + +py_wrap_simple_enum! { + PyInfixOperator(InfixOperator) as "InfixOperator" { + Caret, + Plus, + Minus, + Slash, + Star + } +} +impl_repr!(PyInfixOperator); +impl_str!(PyInfixOperator); + +py_wrap_data_struct! { + PyInfixExpression(InfixExpression) as "InfixExpression" { + left: Box => PyExpression, + operator: InfixOperator => PyInfixOperator, + right: Box => PyExpression + } +} +impl_repr!(PyInfixExpression); + +py_wrap_simple_enum! { + PyPrefixOperator(PrefixOperator) as "PrefixOperator" { + Plus, + Minus + } +} + +py_wrap_data_struct! { + PyPrefixExpression(PrefixExpression) as "PrefixExpression" { + operator: PrefixOperator => PyPrefixOperator, + expression: Box => PyExpression + } +} diff --git a/quil-py/src/instruction/frame.rs b/quil-py/src/instruction/frame.rs new file mode 100644 index 00000000..b5dba0b2 --- /dev/null +++ b/quil-py/src/instruction/frame.rs @@ -0,0 +1,38 @@ +use std::collections::HashMap; +use std::hash::Hash; + +use quil_rs::instruction::{AttributeValue, FrameDefinition, FrameIdentifier, Qubit}; +use rigetti_pyo3::{ + impl_hash, impl_repr, impl_str, py_wrap_data_struct, py_wrap_union_enum, + pyo3::{types::PyString, Py}, +}; + +use super::{PyExpression, PyQubit}; + +py_wrap_data_struct! { + #[derive(PartialEq, Eq, Hash)] + PyFrameIdentifier(FrameIdentifier) as "FrameIdentifier" { + name: String => Py, + qubits: Vec => Vec + } +} +impl_repr!(PyFrameIdentifier); +impl_str!(PyFrameIdentifier); +impl_hash!(PyFrameIdentifier); + +py_wrap_union_enum! { + PyAttributeValue(AttributeValue) as "AttributeValue" { + string: String => Py, + expression: Expression => PyExpression + } +} + +pub type PyFrameAttributes = HashMap; + +py_wrap_data_struct! { + PyFrameDefinition(FrameDefinition) as "FrameDefinition" { + identifier: FrameIdentifier => PyFrameIdentifier, + attributes: HashMap => PyFrameAttributes + } +} +impl_repr!(PyFrameDefinition); diff --git a/quil-py/src/instruction/gate.rs b/quil-py/src/instruction/gate.rs new file mode 100644 index 00000000..8887ee24 --- /dev/null +++ b/quil-py/src/instruction/gate.rs @@ -0,0 +1,112 @@ +use quil_rs::{ + expression::Expression, + instruction::{Gate, GateDefinition, GateModifier, GateSpecification, Qubit}, +}; + +use rigetti_pyo3::{ + impl_repr, impl_str, py_wrap_data_struct, py_wrap_error, py_wrap_simple_enum, + py_wrap_union_enum, + pyo3::{ + exceptions::PyValueError, + pymethods, + types::{PyInt, PyString}, + Py, PyResult, Python, + }, + wrap_error, PyTryFrom, PyWrapper, ToPython, ToPythonError, +}; + +use super::{PyExpression, PyQubit}; + +wrap_error!(GateError(quil_rs::instruction::GateError)); +py_wrap_error!(quil, GateError, PyGateError, PyValueError); + +py_wrap_data_struct! { + #[pyo3(subclass)] + PyGate(Gate) as "Gate" { + name: String => Py, + parameters: Vec => Vec, + qubits: Vec => Vec, + modifiers: Vec => Vec + } +} +impl_repr!(PyGate); +impl_str!(PyGate); + +#[pymethods] +impl PyGate { + #[new] + fn new( + py: Python<'_>, + name: String, + parameters: Vec, + qubits: Vec, + modifiers: Vec, + ) -> PyResult { + Ok(Self( + Gate::new( + &name, + Vec::::py_try_from(py, ¶meters)?, + Vec::::py_try_from(py, &qubits)?, + Vec::::py_try_from(py, &modifiers)?, + ) + .map_err(GateError::from) + .map_err(GateError::to_py_err)?, + )) + } + + fn dagger(&self, py: Python<'_>) -> PyResult { + self.as_inner().clone().dagger().to_python(py) + } + + fn controlled(&self, py: Python<'_>, control_qubit: PyQubit) -> PyResult { + self.as_inner() + .clone() + .controlled(Qubit::py_try_from(py, &control_qubit)?) + .to_python(py) + } + + fn forked( + &self, + py: Python<'_>, + fork_qubit: PyQubit, + params: Vec, + ) -> PyResult { + self.as_inner() + .clone() + .forked( + Qubit::py_try_from(py, &fork_qubit)?, + Vec::::py_try_from(py, ¶ms)?, + ) + .map_err(GateError::from) + .map_err(GateError::to_py_err)? + .to_python(py) + } +} + +py_wrap_union_enum! { + PyGateSpecification(GateSpecification) as "GateSpecification" { + matrix: Matrix => Vec>, + permutation: Permutation => Vec> + } +} +impl_repr!(PyGateSpecification); + +py_wrap_data_struct! { + PyGateDefinition(GateDefinition) as "GateDefinition" { + name: String => Py, + parameters: Vec => Vec>, + specification: GateSpecification => PyGateSpecification + } +} +impl_repr!(PyGateDefinition); +impl_str!(PyGateDefinition); + +py_wrap_simple_enum! { + PyGateModifier(GateModifier) as "GateModifier" { + Controlled, + Dagger, + Forked + } +} +impl_repr!(PyGateModifier); +impl_str!(PyGateModifier); diff --git a/quil-py/src/instruction/memory_region.rs b/quil-py/src/instruction/memory_region.rs new file mode 100644 index 00000000..7ba9ef6d --- /dev/null +++ b/quil-py/src/instruction/memory_region.rs @@ -0,0 +1,49 @@ +use quil_rs::{ + instruction::{MemoryReference, ScalarType, Vector}, + program::MemoryRegion, +}; + +use rigetti_pyo3::{ + impl_repr, impl_str, py_wrap_data_struct, py_wrap_simple_enum, + pyo3::{ + types::{PyInt, PyString}, + Py, + }, +}; + +py_wrap_simple_enum! { + PyScalarType(ScalarType) as "ScalarType" { + Bit, + Integer, + Octet, + Real + } +} +impl_repr!(PyScalarType); +impl_str!(PyScalarType); + +py_wrap_data_struct! { + PyVector(Vector) as "Vector" { + data_type: ScalarType => PyScalarType, + length: u64 => Py + } +} +impl_repr!(PyVector); +impl_str!(PyVector); + +py_wrap_data_struct! { + PyMemoryRegion(MemoryRegion) as "MemoryRegion" { + size: Vector => PyVector, + sharing: Option => Option> + } +} +impl_repr!(PyMemoryRegion); + +py_wrap_data_struct! { + PyMemoryReference(MemoryReference) as "MemoryReference" { + name: String => Py, + index: u64 => Py + } +} +impl_repr!(PyMemoryReference); +impl_str!(PyMemoryReference); diff --git a/quil-py/src/instruction/mod.rs b/quil-py/src/instruction/mod.rs new file mode 100644 index 00000000..6c009422 --- /dev/null +++ b/quil-py/src/instruction/mod.rs @@ -0,0 +1,79 @@ +use quil_rs::instruction::Instruction; +use rigetti_pyo3::{create_init_submodule, impl_repr, impl_str, py_wrap_union_enum}; + +pub use self::{ + arithmetic::{ + PyArithmetic, PyArithmeticOperand, PyArithmeticOperator, PyBinaryLogic, PyBinaryOperand, + PyBinaryOperands, PyBinaryOperator, + }, + calibration::{PyCalibration, PyMeasureCalibrationDefinition}, + declaration::{PyDeclaration, PyScalarType, PyVector}, + expression::{ + PyExpression, PyExpressionFunction, PyFunctionCallExpression, PyInfixExpression, + PyInfixOperator, + }, + frame::{PyAttributeValue, PyFrameAttributes, PyFrameDefinition, PyFrameIdentifier}, + gate::{PyGate, PyGateDefinition, PyGateError, PyGateModifier, PyGateSpecification}, + memory_region::{PyMemoryReference, PyMemoryRegion}, + qubit::PyQubit, + waveform::{PyWaveform, PyWaveformDefinition}, +}; + +mod arithmetic; +mod calibration; +mod declaration; +mod expression; +mod frame; +mod gate; +mod memory_region; +mod qubit; +mod waveform; + +py_wrap_union_enum! { + #[derive(Debug)] + PyInstruction(Instruction) as "Instruction" { + halt: Halt, + nop: Nop, + arithmetic: Arithmetic => PyArithmetic, + gate: Gate => PyGate, + declaration: Declaration => PyDeclaration + } +} +impl_repr!(PyInstruction); +impl_str!(PyInstruction); + +create_init_submodule! { + classes: [ + PyInstruction, + PyArithmetic, + PyArithmeticOperand, + PyArithmeticOperator, + PyBinaryLogic, + PyBinaryOperand, + PyBinaryOperands, + PyBinaryOperator, + PyCalibration, + PyMeasureCalibrationDefinition, + PyDeclaration, + PyScalarType, + PyVector, + PyExpression, + PyExpressionFunction, + PyFunctionCallExpression, + PyInfixExpression, + PyInfixOperator, + PyAttributeValue, + PyFrameDefinition, + PyFrameIdentifier, + PyGate, + PyGateDefinition, + PyGateModifier, + PyGateSpecification, + PyMemoryReference, + PyMemoryRegion, + PyQubit, + PyWaveform, + PyWaveformDefinition + ], + errors: [ PyGateError ], +} diff --git a/quil-py/src/instruction/qubit.rs b/quil-py/src/instruction/qubit.rs new file mode 100644 index 00000000..c475e4c1 --- /dev/null +++ b/quil-py/src/instruction/qubit.rs @@ -0,0 +1,33 @@ +use quil_rs::instruction::Qubit; + +use rigetti_pyo3::{ + impl_hash, impl_repr, impl_str, py_wrap_union_enum, + pyo3::{ + pyclass::CompareOp, + pymethods, + types::{PyLong, PyString}, + IntoPy, Py, PyObject, Python, + }, + PyWrapper, +}; + +py_wrap_union_enum! { + #[derive(Eq, Hash, PartialEq)] + PyQubit(Qubit) as "Qubit" { + fixed: Fixed => Py, + variable: Variable => Py + } +} +impl_repr!(PyQubit); +impl_str!(PyQubit); +impl_hash!(PyQubit); + +#[pymethods] +impl PyQubit { + fn __richcmp__(&self, py: Python<'_>, other: &Self, op: CompareOp) -> PyObject { + match op { + CompareOp::Eq => (self.as_inner() == other.as_inner()).into_py(py), + _ => py.NotImplemented(), + } + } +} diff --git a/quil-py/src/instruction/waveform.rs b/quil-py/src/instruction/waveform.rs new file mode 100644 index 00000000..64bd8717 --- /dev/null +++ b/quil-py/src/instruction/waveform.rs @@ -0,0 +1,25 @@ +use pyo3::{types::PyString, Py}; +use quil_rs::{ + expression::Expression, + instruction::{Waveform, WaveformDefinition}, +}; +use rigetti_pyo3::{impl_repr, impl_str, py_wrap_data_struct}; + +use crate::instruction::expression::PyExpression; + +py_wrap_data_struct! { + PyWaveform(Waveform) as "Waveform" { + matrix: Vec => Vec, + parameters: Vec => Vec> + } +} +impl_repr!(PyWaveform); + +py_wrap_data_struct! { + PyWaveformDefinition(WaveformDefinition) as "WaveformDef" { + name: String => Py, + definition: Waveform => PyWaveform + } +} +impl_repr!(PyWaveformDefinition); +impl_str!(PyWaveformDefinition); diff --git a/quil-py/src/lib.rs b/quil-py/src/lib.rs new file mode 100644 index 00000000..c5666a21 --- /dev/null +++ b/quil-py/src/lib.rs @@ -0,0 +1,20 @@ +use pyo3::prelude::*; +use rigetti_pyo3::create_init_submodule; + +pub mod instruction; +pub mod program; + +create_init_submodule! { + submodules: [ "instructions": instruction::init_submodule, "program": program::init_submodule ], +} + +#[pymodule] +fn quil(py: Python<'_>, m: &PyModule) -> PyResult<()> { + init_submodule("quil", py, m)?; + Ok(()) +} + +pub fn init_quil_submodule(name: &str, py: Python<'_>, m: &PyModule) -> PyResult<()> { + init_submodule(name, py, m)?; + Ok(()) +} diff --git a/quil-py/src/program/calibration_set.rs b/quil-py/src/program/calibration_set.rs new file mode 100644 index 00000000..9b2b4f47 --- /dev/null +++ b/quil-py/src/program/calibration_set.rs @@ -0,0 +1,26 @@ +use pyo3::prelude::*; +use quil_rs::program::CalibrationSet; +use rigetti_pyo3::{impl_repr, py_wrap_type, PyTryFrom}; + +use crate::instruction::{PyCalibration, PyMeasureCalibrationDefinition}; + +py_wrap_type! { + PyCalibrationSet(CalibrationSet) as "CalibrationSet" +} + +#[pymethods] +impl PyCalibrationSet { + #[new] + pub fn new( + py: Python<'_>, + calibrations: Vec, + measure_calibrations: Vec, + ) -> PyResult { + Ok(Self(CalibrationSet { + calibrations: Vec::<_>::py_try_from(py, &calibrations)?, + measure_calibrations: Vec::<_>::py_try_from(py, &measure_calibrations)?, + })) + } +} + +impl_repr!(PyCalibrationSet); diff --git a/quil-py/src/program/frame.rs b/quil-py/src/program/frame.rs new file mode 100644 index 00000000..2cba9475 --- /dev/null +++ b/quil-py/src/program/frame.rs @@ -0,0 +1,25 @@ +use pyo3::{pymethods, PyResult, Python}; +use std::collections::HashMap; + +use quil_rs::program::FrameSet; +use rigetti_pyo3::{impl_repr, py_wrap_type, PyWrapper, ToPython}; + +use crate::instruction::{PyFrameAttributes, PyFrameIdentifier}; + +py_wrap_type! { + PyFrameSet(FrameSet) as "FrameSet" +} +impl_repr!(PyFrameSet); + +#[pymethods] +impl PyFrameSet { + fn get_all_frames( + &self, + py: Python<'_>, + ) -> PyResult> { + self.as_inner() + .iter() + .map(|(ident, attribs)| Ok((ident.to_python(py)?, attribs.to_python(py)?))) + .collect() + } +} diff --git a/quil-py/src/program/mod.rs b/quil-py/src/program/mod.rs new file mode 100644 index 00000000..7e4f646f --- /dev/null +++ b/quil-py/src/program/mod.rs @@ -0,0 +1,197 @@ +use std::{ + collections::{BTreeMap, HashMap, HashSet}, + str::FromStr, +}; + +use quil_rs::{instruction::Instruction, Program}; + +use rigetti_pyo3::{ + create_init_submodule, impl_as_mut_for_wrapper, impl_repr, py_wrap_type, + pyo3::{ + create_exception, exceptions::PyValueError, prelude::*, pyclass::CompareOp, types::PyList, + }, + PyWrapper, PyWrapperMut, ToPython, +}; + +use crate::instruction::{ + PyDeclaration, PyGateDefinition, PyInstruction, PyMemoryRegion, PyQubit, PyWaveform, +}; + +pub use self::{calibration_set::PyCalibrationSet, frame::PyFrameSet}; + +mod calibration_set; +mod frame; + +// The quil-rs counterparts to these errors use generics which cause complications trying to map +// them directly to an error type here. +create_exception!(quil, ProgramError, PyValueError); +create_exception!(quil, ParseError, PyValueError); + +py_wrap_type! { + #[derive(Debug, PartialEq)] + PyProgram(Program) as "Program" +} +impl_as_mut_for_wrapper!(PyProgram); +impl_repr!(PyProgram); + +impl Default for PyProgram { + fn default() -> Self { + Self::new() + } +} + +#[pymethods] +impl PyProgram { + #[new] + pub fn new() -> Self { + Self(Program::default()) + } + + #[staticmethod] + pub fn from_string(program: &str) -> PyResult { + Ok(Self( + Program::from_str(program).map_err(|e| ParseError::new_err(e.to_string()))?, + )) + } + + #[getter] + pub fn instructions<'a>(&self, py: Python<'a>) -> PyResult<&'a PyList> { + Ok(PyList::new( + py, + self.as_inner() + .instructions + .iter() + .map(|i| i.to_python(py)) + .collect::>>()?, + )) + } + + #[getter] + pub fn calibrations(&self, py: Python<'_>) -> PyResult { + self.as_inner().calibrations.to_python(py) + } + + #[getter] + pub fn waveforms(&self, py: Python<'_>) -> PyResult> { + self.as_inner().waveforms.to_python(py) + } + + #[getter] + pub fn frames(&self, py: Python<'_>) -> PyResult { + self.as_inner().frames.to_python(py) + } + + #[getter] + pub fn memory_regions(&self, py: Python<'_>) -> PyResult> { + self.as_inner() + .memory_regions + .iter() + .map(|(name, memory_region)| Ok((name.to_python(py)?, memory_region.to_python(py)?))) + .collect() + } + + #[getter] + // TODO: Should this filtering move to Program? Should we assume memory_regions will always make up all + // declarations and simplify this? + pub fn declarations(&self, py: Python<'_>) -> PyResult> { + self.as_inner() + .to_instructions(true) + .iter() + .filter_map(|inst| match inst { + Instruction::Declaration(declaration) => Some(declaration), + _ => None, + }) + .map(|declaration| Ok((declaration.name.clone(), declaration.to_python(py)?))) + .collect() + } + + #[getter] + pub fn defined_gates(&self, py: Python<'_>) -> PyResult> { + self.as_inner() + .to_instructions(true) + .iter() + .filter_map(|inst| match inst { + Instruction::GateDefinition(gate_def) => Some(gate_def.to_python(py)), + _ => None, + }) + .collect() + } + + pub fn dagger(&self) -> PyResult { + self.as_inner() + .dagger() + .map_err(|e| ProgramError::new_err(e.to_string())) + .map(PyProgram::from) + } + + pub fn expand_calibrations(&self) -> PyResult { + self.as_inner() + .expand_calibrations() + .map_err(|e| ParseError::new_err(e.to_string())) + .map(PyProgram::from) + } + + pub fn into_simplified(&self) -> PyResult { + self.as_inner() + .into_simplified() + .map_err(|e| ParseError::new_err(e.to_string())) + .map(PyProgram::from) + } + + pub fn get_used_qubits(&self, py: Python<'_>) -> PyResult> { + self.as_inner() + .get_used_qubits() + .iter() + .map(|q| q.to_python(py)) + .collect() + } + + pub fn add_instruction(&mut self, instruction: PyInstruction) { + self.as_inner_mut().add_instruction(instruction.into()) + } + + pub fn add_instructions(&mut self, instructions: Vec) { + self.as_inner_mut() + .add_instructions(instructions.into_iter().map(Into::into).collect()) + } + + pub fn to_instructions( + &self, + include_headers: bool, + py: Python<'_>, + ) -> PyResult> { + self.as_inner() + .to_instructions(include_headers) + .iter() + .map(|i| i.to_python(py)) + .collect() + } + + pub fn to_headers(&self, py: Python<'_>) -> PyResult> { + self.as_inner() + .to_headers() + .iter() + .map(|h| h.to_python(py)) + .collect() + } + + pub fn __str__(&self) -> String { + self.as_inner().to_string(true) + } + + pub fn __add__(&self, py: Python<'_>, rhs: Self) -> PyResult { + let new = self.as_inner() + rhs.as_inner(); + new.to_python(py) + } + + fn __richcmp__(&self, py: Python<'_>, other: &Self, op: CompareOp) -> PyObject { + match op { + CompareOp::Eq => (self.as_inner() == other.as_inner()).into_py(py), + _ => py.NotImplemented(), + } + } +} + +create_init_submodule! { + classes: [ PyFrameSet, PyProgram, PyCalibrationSet ], +} diff --git a/CHANGELOG.md b/quil-rs/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to quil-rs/CHANGELOG.md diff --git a/quil-rs/Cargo.toml b/quil-rs/Cargo.toml new file mode 100644 index 00000000..1c9c3ff6 --- /dev/null +++ b/quil-rs/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "quil-rs" +description = "Rust tooling for Quil (Quantum Instruction Language)" +version ="0.16.0-rc.0" +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/rigetti/quil-rust" +keywords = ["Quil", "Quantum", "Rigetti"] +categories = ["parser-implementations", "science", "compilers", "emulators"] + +[dependencies] +dot-writer = { version = "0.1.2", optional = true } +regex = "1.7.1" +indexmap = "1.6.1" +lexical = "6.1.1" +nom = "7.1.1" +nom_locate = "4.0.0" +num-complex = "0.4.0" +petgraph = "0.6.2" +serde = { version = "1.0.125", features = ["derive"] } +strum = { version = "0.24.1", features = ["derive"] } +thiserror = "1.0.37" +once_cell = "1.17.1" + +[dev-dependencies] +criterion = { version = "0.4.0", features = ["html_reports"] } +insta = "1.7.1" +proptest = "1.0.0" +proptest-derive = "0.3.0" +rstest = "0.15.0" + +[features] +graphviz-dot = ["dot-writer"] + +[[bench]] +name = "parser" +harness = false diff --git a/quil-rs/README.md b/quil-rs/README.md new file mode 100644 index 00000000..f503b71b --- /dev/null +++ b/quil-rs/README.md @@ -0,0 +1,20 @@ +# Quil Parser & Program Builder + +This library is the implementation of the [Quil spec](https://github.com/quil-lang/quil) in Rust. + +It serves three purposes: + +1. Parse Quil programs from strings, and output programs to strings +2. Manipulate Quil programs within Rust +3. Construct a dependency graph among program instructions + +It should be considered unstable until the release of v1.0. + + +## Testing + +When testing this crate, you should run with the `--all-features` flag to ensure all tests are executed. + +```sh +cargo test --all-features +``` \ No newline at end of file diff --git a/benches/parser.rs b/quil-rs/benches/parser.rs similarity index 99% rename from benches/parser.rs rename to quil-rs/benches/parser.rs index c966b1ca..432d849c 100644 --- a/benches/parser.rs +++ b/quil-rs/benches/parser.rs @@ -43,6 +43,8 @@ fn from_corpus() -> Vec { init_submodules() } + dbg!(&corpus_dir); + let dir = fs::read_dir(corpus_dir).expect("failed to locate quil corpus directory"); dir.filter_map(Result::ok) diff --git a/quil-rs/benches/quilc b/quil-rs/benches/quilc new file mode 160000 index 00000000..5c114260 --- /dev/null +++ b/quil-rs/benches/quilc @@ -0,0 +1 @@ +Subproject commit 5c1142609691a817ec70442c103df3693b29d51a diff --git a/benches/sample-calibrations.quil b/quil-rs/benches/sample-calibrations.quil similarity index 100% rename from benches/sample-calibrations.quil rename to quil-rs/benches/sample-calibrations.quil diff --git a/src/expression.rs b/quil-rs/src/expression.rs similarity index 86% rename from src/expression.rs rename to quil-rs/src/expression.rs index ee86c679..d4af1df5 100644 --- a/src/expression.rs +++ b/quil-rs/src/expression.rs @@ -46,24 +46,33 @@ pub enum EvaluationError { #[derive(Clone, Debug)] pub enum Expression { Address(MemoryReference), - FunctionCall { - function: ExpressionFunction, - expression: Box, - }, - Infix { - left: Box, - operator: InfixOperator, - right: Box, - }, + FunctionCall(FunctionCallExpression), + Infix(InfixExpression), Number(num_complex::Complex64), PiConstant, - Prefix { - operator: PrefixOperator, - expression: Box, - }, + Prefix(PrefixExpression), Variable(String), } +#[derive(Clone, Debug)] +pub struct FunctionCallExpression { + pub function: ExpressionFunction, + pub expression: Box, +} + +#[derive(Clone, Debug)] +pub struct InfixExpression { + pub left: Box, + pub operator: InfixOperator, + pub right: Box, +} + +#[derive(Clone, Debug)] +pub struct PrefixExpression { + pub operator: PrefixOperator, + pub expression: Box, +} + /// Hash value helper: turn a hashable thing into a u64. fn hash_to_u64(t: &T) -> u64 { let mut s = DefaultHasher::new(); @@ -83,27 +92,27 @@ impl Hash for Expression { "Address".hash(state); m.hash(state); } - FunctionCall { + FunctionCall(FunctionCallExpression { function, expression, - } => { + }) => { "FunctionCall".hash(state); function.hash(state); expression.hash(state); } - Infix { + Infix(InfixExpression { left, operator, right, - } => { + }) => { "Infix".hash(state); operator.hash(state); match operator { InfixOperator::Plus | InfixOperator::Star => { // commutative, so put left & right in decreasing order by hash value let (a, b) = ( - min_by_key(left, right, hash_to_u64), - max_by_key(left, right, hash_to_u64), + min_by_key(&left, &right, hash_to_u64), + max_by_key(&left, &right, hash_to_u64), ); a.hash(state); b.hash(state); @@ -129,13 +138,10 @@ impl Hash for Expression { PiConstant => { "PiConstant".hash(state); } - Prefix { - operator, - expression, - } => { + Prefix(p) => { "Prefix".hash(state); - operator.hash(state); - expression.hash(state); + p.operator.hash(state); + p.expression.hash(state); } Variable(v) => { "Variable".hash(state); @@ -159,11 +165,11 @@ macro_rules! impl_expr_op { impl $name for Expression { type Output = Self; fn $function(self, other: Self) -> Self { - Expression::Infix { + Expression::Infix(InfixExpression { left: Box::new(self), operator: InfixOperator::$operator, right: Box::new(other), - } + }) } } impl $name_assign for Expression { @@ -237,27 +243,27 @@ impl Expression { use Expression::*; match self { - FunctionCall { + FunctionCall(FunctionCallExpression { function, expression, - } => { + }) => { expression.simplify(); if let Number(number) = expression.as_ref() { *self = Number(calculate_function(function, number)); } } - Infix { + Infix(InfixExpression { left, operator: _, right, - } => { + }) => { left.simplify(); right.simplify(); } - Prefix { + Prefix(PrefixExpression { operator, expression, - } => { + }) => { use PrefixOperator::*; expression.simplify(); @@ -327,26 +333,26 @@ impl Expression { use Expression::*; match self { - FunctionCall { + FunctionCall(FunctionCallExpression { function, expression, - } => { + }) => { let evaluated = expression.evaluate(variables, memory_references)?; Ok(calculate_function(function, &evaluated)) } - Infix { + Infix(InfixExpression { left, operator, right, - } => { + }) => { let left_evaluated = left.evaluate(variables, memory_references)?; let right_evaluated = right.evaluate(variables, memory_references)?; Ok(calculate_infix(&left_evaluated, operator, &right_evaluated)) } - Prefix { + Prefix(PrefixExpression { operator, expression, - } => { + }) => { use PrefixOperator::*; let value = expression.evaluate(variables, memory_references)?; if matches!(operator, Minus) { @@ -395,33 +401,33 @@ impl Expression { use Expression::*; match self { - FunctionCall { + FunctionCall(FunctionCallExpression { function, expression, - } => FunctionCall { + }) => Expression::FunctionCall(FunctionCallExpression { function, expression: expression.substitute_variables(variable_values).into(), - }, - Infix { + }), + Infix(InfixExpression { left, operator, right, - } => { + }) => { let left = left.substitute_variables(variable_values).into(); let right = right.substitute_variables(variable_values).into(); - Infix { + Infix(InfixExpression { left, operator, right, - } + }) } - Prefix { + Prefix(PrefixExpression { operator, expression, - } => Prefix { + }) => Prefix(PrefixExpression { operator, expression: expression.substitute_variables(variable_values).into(), - }, + }), Variable(identifier) => match variable_values.get(identifier.as_str()) { Some(value) => value.clone(), None => Variable(identifier), @@ -505,29 +511,29 @@ impl fmt::Display for Expression { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Expression::*; match self { - Address(memory_reference) => write!(f, "{}", memory_reference), - FunctionCall { + Address(memory_reference) => write!(f, "{memory_reference}"), + FunctionCall(FunctionCallExpression { function, expression, - } => write!(f, "{}({})", function, expression), - Infix { + }) => write!(f, "{function}({expression})"), + Infix(InfixExpression { left, operator, right, - } => write!(f, "({}{}{})", left, operator, right), + }) => write!(f, "({left}{operator}{right})"), Number(value) => write!(f, "{}", format_complex(value)), PiConstant => write!(f, "pi"), - Prefix { + Prefix(PrefixExpression { operator, expression, - } => write!(f, "({}{})", operator, expression), - Variable(identifier) => write!(f, "%{}", identifier), + }) => write!(f, "({operator}{expression})"), + Variable(identifier) => write!(f, "%{identifier}"), } } } /// A function defined within Quil syntax. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(test, derive(Arbitrary))] pub enum ExpressionFunction { Cis, @@ -554,7 +560,7 @@ impl fmt::Display for ExpressionFunction { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(test, derive(Arbitrary))] pub enum PrefixOperator { Plus, @@ -575,7 +581,7 @@ impl fmt::Display for PrefixOperator { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(test, derive(Arbitrary))] pub enum InfixOperator { Caret, @@ -650,10 +656,10 @@ mod tests { evaluated: Ok(one), }, TestCase { - expression: Expression::Prefix { + expression: Expression::Prefix(PrefixExpression { operator: PrefixOperator::Minus, expression: Box::new(Number(real!(1f64))), - }, + }), variables: &empty_variables, memory_references: &empty_memory, simplified: Number(real!(-1f64)), @@ -674,10 +680,10 @@ mod tests { evaluated: Ok(real!(110f64)), }, TestCase { - expression: Expression::FunctionCall { + expression: Expression::FunctionCall(FunctionCallExpression { function: ExpressionFunction::Sine, expression: Box::new(Expression::Number(real!(PI / 2f64))), - }, + }), variables: &variables, memory_references: &empty_memory, simplified: Number(real!(1f64)), @@ -721,22 +727,24 @@ mod tests { |expr| { prop_oneof![ (any::(), expr.clone()).prop_map(|(function, e)| { - FunctionCall { + Expression::FunctionCall(FunctionCallExpression { function, expression: Box::new(e), - } + }) }), (expr.clone(), any::(), expr.clone()).prop_map( - |(l, operator, r)| Infix { + |(l, operator, r)| Infix(InfixExpression { left: Box::new(l), operator, right: Box::new(r) - } + }) ), - (any::(), expr).prop_map(|(operator, e)| Prefix { - operator, - expression: Box::new(e) - }) + (any::(), expr).prop_map(|(operator, e)| Prefix( + PrefixExpression { + operator, + expression: Box::new(e) + } + )) ] }, ) @@ -750,11 +758,11 @@ mod tests { #[test] fn eq(a in any::(), b in any::()) { - let first = Expression::Infix { + let first = Expression::Infix (InfixExpression { left: Box::new(Expression::Number(real!(a))), operator: InfixOperator::Plus, right: Box::new(Expression::Number(real!(b))), - }; + } ); let matching = first.clone(); let differing = Expression::Number(real!(a + b)); prop_assert_eq!(&first, &matching); @@ -763,26 +771,26 @@ mod tests { #[test] fn eq_commutative(a in any::(), b in any::()) { - let first = Expression::Infix{ + let first = Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(a))), operator: InfixOperator::Plus, right: Box::new(Expression::Number(real!(b))), - }; - let second = Expression::Infix{ + } ); + let second = Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(b))), operator: InfixOperator::Plus, right: Box::new(Expression::Number(real!(a))), - }; + }); prop_assert_eq!(first, second); } #[test] fn hash(a in any::(), b in any::()) { - let first = Expression::Infix { + let first = Expression::Infix (InfixExpression { left: Box::new(Expression::Number(real!(a))), operator: InfixOperator::Plus, right: Box::new(Expression::Number(real!(b))), - }; + }); let matching = first.clone(); let differing = Expression::Number(real!(a + b)); let mut set = HashSet::new(); @@ -793,16 +801,16 @@ mod tests { #[test] fn hash_commutative(a in any::(), b in any::()) { - let first = Expression::Infix{ + let first = Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(a))), operator: InfixOperator::Plus, right: Box::new(Expression::Number(real!(b))), - }; - let second = Expression::Infix{ + } ); + let second = Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(b))), operator: InfixOperator::Plus, right: Box::new(Expression::Number(real!(a))), - }; + } ); let mut set = HashSet::new(); set.insert(first); assert!(set.contains(&second)); @@ -852,46 +860,46 @@ mod tests { #[test] fn exponentiation_works_as_expected(left in arb_expr(), right in arb_expr()) { - let expected = Expression::Infix { left: Box::new(left.clone()), operator: InfixOperator::Caret, right: Box::new(right.clone()) }; + 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.clone()); - let mut x = left.clone(); - x ^= right.clone(); + let mut x = left; + x ^= right; prop_assert_eq!(x, expected); } #[test] fn addition_works_as_expected(left in arb_expr(), right in arb_expr()) { - let expected = Expression::Infix { left: Box::new(left.clone()), operator: InfixOperator::Plus, right: Box::new(right.clone()) }; + 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.clone()); - let mut x = left.clone(); - x += right.clone(); + let mut x = left; + x += right; prop_assert_eq!(x, expected); } #[test] fn subtraction_works_as_expected(left in arb_expr(), right in arb_expr()) { - let expected = Expression::Infix { left: Box::new(left.clone()), operator: InfixOperator::Minus, right: Box::new(right.clone()) }; + 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.clone()); - let mut x = left.clone(); - x -= right.clone(); + let mut x = left; + x -= right; prop_assert_eq!(x, expected); } #[test] fn multiplication_works_as_expected(left in arb_expr(), right in arb_expr()) { - let expected = Expression::Infix { left: Box::new(left.clone()), operator: InfixOperator::Star, right: Box::new(right.clone()) }; + 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.clone()); - let mut x = left.clone(); - x *= right.clone(); + let mut x = left; + x *= right; prop_assert_eq!(x, expected); } #[test] fn division_works_as_expected(left in arb_expr(), right in arb_expr()) { - let expected = Expression::Infix { left: Box::new(left.clone()), operator: InfixOperator::Slash, right: Box::new(right.clone()) }; + 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.clone()); - let mut x = left.clone(); - x /= right.clone(); + let mut x = left; + x /= right; prop_assert_eq!(x, expected); } diff --git a/quil-rs/src/instruction/gate.rs b/quil-rs/src/instruction/gate.rs new file mode 100644 index 00000000..24cc2872 --- /dev/null +++ b/quil-rs/src/instruction/gate.rs @@ -0,0 +1,217 @@ +use std::fmt; + +use crate::{ + expression::Expression, + validation::identifier::{validate_identifier, IdentifierValidationError}, +}; + +use super::{format_qubits, get_expression_parameter_string, Qubit}; + +/// A struct encapsulating all the properties of a Quil Quantum Gate. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Gate { + pub name: String, + pub parameters: Vec, + pub qubits: Vec, + pub modifiers: Vec, +} + +/// An enum of all the possible modifiers on a quil [`Gate`] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum GateModifier { + /// The `CONTROLLED` modifier makes the gate take an extra [`Qubit`] parameter as a control + /// qubit. + Controlled, + /// The `DAGGER` modifier does a complex-conjugate transpose on the [`Gate`]. + Dagger, + /// The `FORKED` modifier allows an alternate set of parameters to be used based on the state + /// of a qubit. + Forked, +} + +#[derive(Debug, thiserror::Error)] +pub enum GateError { + #[error("invalid name: {0}")] + InvalidIdentifier(#[from] IdentifierValidationError), + + #[error("a gate must operate on 1 or more qubits")] + EmptyQubits, + + #[error("expected {expected} parameters, but got {actual}")] + ForkedParameterLength { expected: usize, actual: usize }, +} + +impl Gate { + /// Build a new gate + /// + /// # Errors + /// + /// Returns an error if the given name isn't a valid Quil identifier or if no qubits are given. + pub fn new( + name: &str, + parameters: Vec, + qubits: Vec, + modifiers: Vec, + ) -> Result { + if qubits.is_empty() { + return Err(GateError::EmptyQubits); + } + + validate_identifier(name).map_err(GateError::InvalidIdentifier)?; + + Ok(Self { + name: name.to_string(), + parameters, + qubits, + modifiers, + }) + } + + /// Apply a DAGGER modifier to the gate + pub fn dagger(mut self) -> Self { + self.modifiers.insert(0, GateModifier::Dagger); + self + } + + /// Apply a CONTROLLED modifier to the gate + pub fn controlled(mut self, control_qubit: Qubit) -> Self { + self.qubits.insert(0, control_qubit); + self.modifiers.insert(0, GateModifier::Controlled); + self + } + + /// Apply a FORKED modifier to the gate + /// + /// # Errors + /// + /// Returns an error if the number of provided alternate parameters don't + /// equal the number of existing parameters. + pub fn forked( + mut self, + fork_qubit: Qubit, + alt_params: Vec, + ) -> Result { + if alt_params.len() != self.parameters.len() { + return Err(GateError::ForkedParameterLength { + expected: self.parameters.len(), + actual: alt_params.len(), + }); + } + self.modifiers.insert(0, GateModifier::Forked); + self.qubits.insert(0, fork_qubit); + self.parameters.extend(alt_params); + Ok(self) + } +} + +impl fmt::Display for Gate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let parameter_str = get_expression_parameter_string(&self.parameters); + + let qubit_str = format_qubits(&self.qubits); + for modifier in &self.modifiers { + write!(f, "{modifier} ")?; + } + + write!(f, "{}{} {}", self.name, parameter_str, qubit_str) + } +} + +impl fmt::Display for GateModifier { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Controlled => write!(f, "CONTROLLED"), + Self::Dagger => write!(f, "DAGGER"), + Self::Forked => write!(f, "FORKED"), + } + } +} + +/// An enum representing a the specification of a [`GateDefinition`] for a given [`GateType`] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum GateSpecification { + /// A matrix of [`Expression`]s representing a unitary operation for a [`GateType::Matrix`]. + Matrix(Vec>), + /// A vector of integers that defines the permutation used for a [`GateType::Permutation`] + Permutation(Vec), +} + +impl fmt::Display for GateSpecification { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + GateSpecification::Matrix(matrix) => { + for row in matrix { + write!(f, "\t")?; + if let Some(first) = row.first() { + write!(f, "{first}")?; + } + for cell in row.iter().skip(1) { + write!(f, ", {cell}")?; + } + writeln!(f)?; + } + } + GateSpecification::Permutation(permutation) => { + write!(f, "\t")?; + if let Some(i) = permutation.first() { + write!(f, "{i}")?; + } + for i in permutation.iter().skip(1) { + write!(f, ", {i}")?; + } + writeln!(f)?; + } + } + Ok(()) + } +} + +/// A struct encapsulating a quil Gate Definition +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct GateDefinition { + pub name: String, + pub parameters: Vec, + pub specification: GateSpecification, +} + +impl fmt::Display for GateDefinition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let parameter_str = match self.parameters.is_empty() { + true => String::new(), + false => format!( + "({})", + self.parameters + .iter() + .map(|p| p.to_string()) + .collect::() + ), + }; + writeln!( + f, + "DEFGATE {}{} AS {}:", + self.name, + parameter_str, + match self.specification { + GateSpecification::Matrix(_) => "MATRIX", + GateSpecification::Permutation(_) => "PERMUTATION", + } + )?; + write!(f, "{}", self.specification) + } +} + +/// The type of a [`GateDefinition`] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum GateType { + Matrix, + Permutation, +} + +impl fmt::Display for GateType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Matrix => write!(f, "MATRIX"), + Self::Permutation => write!(f, "PERMUTATION"), + } + } +} diff --git a/src/instruction.rs b/quil-rs/src/instruction/mod.rs similarity index 79% rename from src/instruction.rs rename to quil-rs/src/instruction/mod.rs index e9171167..32ffd17f 100644 --- a/src/instruction.rs +++ b/quil-rs/src/instruction/mod.rs @@ -23,9 +23,13 @@ use crate::expression::Expression; use crate::parser::{common::parse_memory_reference, lex, ParseError}; use crate::program::{disallow_leftover, frame::FrameMatchCondition, SyntaxError}; +mod gate; + #[cfg(test)] use proptest_derive::Arbitrary; +pub use self::gate::{Gate, GateDefinition, GateError, GateModifier, GateSpecification, GateType}; + #[derive(Clone, Debug, PartialEq)] pub enum ArithmeticOperand { LiteralInteger(i64), @@ -36,14 +40,14 @@ pub enum ArithmeticOperand { impl fmt::Display for ArithmeticOperand { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self { - ArithmeticOperand::LiteralInteger(value) => write!(f, "{}", value), - ArithmeticOperand::LiteralReal(value) => write!(f, "{}", value), - ArithmeticOperand::MemoryReference(value) => write!(f, "{}", value), + ArithmeticOperand::LiteralInteger(value) => write!(f, "{value}"), + ArithmeticOperand::LiteralReal(value) => write!(f, "{value}"), + ArithmeticOperand::MemoryReference(value) => write!(f, "{value}"), } } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ArithmeticOperator { Add, Subtract, @@ -71,8 +75,8 @@ pub enum BinaryOperand { impl fmt::Display for BinaryOperand { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self { - BinaryOperand::LiteralInteger(value) => write!(f, "{}", value), - BinaryOperand::MemoryReference(value) => write!(f, "{}", value), + BinaryOperand::LiteralInteger(value) => write!(f, "{value}"), + BinaryOperand::MemoryReference(value) => write!(f, "{value}"), } } } @@ -118,9 +122,9 @@ pub enum ComparisonOperand { impl fmt::Display for ComparisonOperand { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self { - ComparisonOperand::LiteralInteger(value) => write!(f, "{}", value), - ComparisonOperand::LiteralReal(value) => write!(f, "{}", value), - ComparisonOperand::MemoryReference(value) => write!(f, "{}", value), + ComparisonOperand::LiteralInteger(value) => write!(f, "{value}"), + ComparisonOperand::LiteralReal(value) => write!(f, "{value}"), + ComparisonOperand::MemoryReference(value) => write!(f, "{value}"), } } } @@ -156,8 +160,8 @@ impl fmt::Display for AttributeValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use AttributeValue::*; match self { - String(value) => write!(f, "\"{}\"", value), - Expression(value) => write!(f, "{}", value), + String(value) => write!(f, "\"{value}\""), + Expression(value) => write!(f, "{value}"), } } } @@ -173,6 +177,23 @@ pub struct Calibration { pub qubits: Vec, } +impl fmt::Display for Calibration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let parameter_str = get_expression_parameter_string(&self.parameters); + write!( + f, + "DEFCAL {}{} {}:", + self.name, + parameter_str, + format_qubits(&self.qubits) + )?; + for instruction in &self.instructions { + write!(f, "\n\t{instruction}")?; + } + Ok(()) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Convert { pub from: MemoryReference, @@ -196,49 +217,7 @@ pub struct Include { pub filename: String, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum GateModifier { - Controlled, - Dagger, - Forked, -} - -impl fmt::Display for GateModifier { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use GateModifier::*; - write!( - f, - "{}", - match self { - Controlled => "CONTROLLED", - Dagger => "DAGGER", - Forked => "FORKED", - } - ) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum GateType { - Matrix, - Permutation, -} - -impl fmt::Display for GateType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use GateType::*; - write!( - f, - "{}", - match self { - Matrix => "MATRIX", - Permutation => "PERMUTATION", - } - ) - } -} - -#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub enum ScalarType { Bit, Integer, @@ -298,7 +277,7 @@ impl fmt::Display for WaveformInvocation { self.name, key_value_pairs .iter() - .map(|(k, v)| format!("{}: {}", k, v)) + .map(|(k, v)| format!("{k}: {v}")) .collect::>() .join(", ") ) @@ -318,7 +297,7 @@ mod waveform_invocation_tests { name: "CZ".into(), parameters: HashMap::new(), }; - assert_eq!(format!("{}", wfi), "CZ".to_string()); + assert_eq!(format!("{wfi}"), "CZ".to_string()); } } @@ -349,14 +328,6 @@ impl FromStr for MemoryReference { } } -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Gate { - pub name: String, - pub parameters: Vec, - pub qubits: Vec, - pub modifiers: Vec, -} - #[derive(Clone, Debug, PartialEq)] pub struct CircuitDefinition { pub name: String, @@ -366,19 +337,6 @@ pub struct CircuitDefinition { pub instructions: Vec, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum GateSpecification { - Matrix(Vec>), - Permutation(Vec), -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct GateDefinition { - pub name: String, - pub parameters: Vec, - pub specification: GateSpecification, -} - #[derive(Clone, Debug, PartialEq, Eq)] pub struct Declaration { pub name: String, @@ -386,6 +344,16 @@ pub struct Declaration { pub sharing: Option, } +impl fmt::Display for Declaration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DECLARE {} {}", self.name, self.size)?; + if let Some(shared) = &self.sharing { + write!(f, "SHARING {shared}")? + } + Ok(()) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Measurement { pub qubit: Qubit, @@ -430,6 +398,22 @@ pub struct MeasureCalibrationDefinition { pub instructions: Vec, } +impl fmt::Display for MeasureCalibrationDefinition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DEFCAL MEASURE")?; + if let Some(qubit) = &self.qubit { + write!(f, " {qubit}")?; + } + + writeln!( + f, + " {}:\n\t{}", + self.parameter, + format_instructions(&self.instructions) + ) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Pragma { pub name: String, @@ -446,8 +430,8 @@ pub enum PragmaArgument { impl fmt::Display for PragmaArgument { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - PragmaArgument::Identifier(i) => write!(f, "{}", i), - PragmaArgument::Integer(i) => write!(f, "{}", i), + PragmaArgument::Identifier(i) => write!(f, "{i}"), + PragmaArgument::Integer(i) => write!(f, "{i}"), } } } @@ -509,6 +493,23 @@ pub struct WaveformDefinition { pub definition: Waveform, } +impl fmt::Display for WaveformDefinition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "DEFWAVEFORM {}{}:\n\t{}", + self.name, + get_string_parameter_string(&self.definition.parameters), + self.definition + .matrix + .iter() + .map(|e| format!("{e}")) + .collect::>() + .join(", ") + ) + } +} + #[derive(Clone, Debug, PartialEq)] pub struct Arithmetic { pub operator: ArithmeticOperator, @@ -522,10 +523,12 @@ pub struct Comparison { pub operands: (MemoryReference, MemoryReference, ComparisonOperand), } +pub type BinaryOperands = (MemoryReference, BinaryOperand); + #[derive(Clone, Debug, PartialEq, Eq)] pub struct BinaryLogic { pub operator: BinaryOperator, - pub operands: (MemoryReference, BinaryOperand), + pub operands: BinaryOperands, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -620,6 +623,7 @@ pub enum Instruction { SwapPhases(SwapPhases), UnaryLogic(UnaryLogic), WaveformDefinition(WaveformDefinition), + Wait, } #[derive(Clone, Debug)] @@ -670,7 +674,8 @@ impl From<&Instruction> for InstructionRole { Instruction::Halt | Instruction::Jump(_) | Instruction::JumpWhen(_) - | Instruction::JumpUnless(_) => InstructionRole::ControlFlow, + | Instruction::JumpUnless(_) + | Instruction::Wait => InstructionRole::ControlFlow, } } } @@ -678,7 +683,7 @@ impl From<&Instruction> for InstructionRole { pub fn format_instructions(values: &[Instruction]) -> String { values .iter() - .map(|i| format!("{}", i)) + .map(|i| format!("{i}")) .collect::>() .join("\n\t") } @@ -686,7 +691,7 @@ pub fn format_instructions(values: &[Instruction]) -> String { pub fn format_integer_vector(values: &[u64]) -> String { values .iter() - .map(|q| format!("{}", q)) + .map(|q| format!("{q}")) .collect::>() .join(" ") } @@ -696,7 +701,7 @@ pub fn format_matrix(matrix: &[Vec]) -> String { .iter() .map(|row| { row.iter() - .map(|cell| format!("{}", cell)) + .map(|cell| format!("{cell}")) .collect::>() .join(", ") }) @@ -707,7 +712,7 @@ pub fn format_matrix(matrix: &[Vec]) -> String { pub fn format_qubits(qubits: &[Qubit]) -> String { qubits .iter() - .map(|q| format!("{}", q)) + .map(|q| format!("{q}")) .collect::>() .join(" ") } @@ -717,8 +722,8 @@ pub fn get_expression_parameter_string(parameters: &[Expression]) -> String { return String::from(""); } - let parameter_str: String = parameters.iter().map(|e| format!("{}", e)).collect(); - format!("({})", parameter_str) + let parameter_str: String = parameters.iter().map(|e| format!("{e}")).collect(); + format!("({parameter_str})") } pub fn get_string_parameter_string(parameters: &[String]) -> String { @@ -727,7 +732,7 @@ pub fn get_string_parameter_string(parameters: &[String]) -> String { } let parameter_str: String = parameters.join(","); - format!("({})", parameter_str) + format!("({parameter_str})") } impl fmt::Display for Instruction { @@ -737,20 +742,9 @@ impl fmt::Display for Instruction { operator, destination, source, - }) => write!(f, "{} {} {}", operator, destination, source), + }) => write!(f, "{operator} {destination} {source}"), Instruction::CalibrationDefinition(calibration) => { - let parameter_str = get_expression_parameter_string(&calibration.parameters); - write!( - f, - "DEFCAL {}{} {}:", - calibration.name, - parameter_str, - format_qubits(&calibration.qubits) - )?; - for instruction in &calibration.instructions { - write!(f, "\n\t{}", instruction)?; - } - Ok(()) + write!(f, "{calibration}") } Instruction::Capture(Capture { blocking, @@ -761,7 +755,7 @@ impl fmt::Display for Instruction { if !blocking { write!(f, "NONBLOCKING ")?; } - write!(f, "CAPTURE {} {} {}", frame, waveform, memory_reference) + write!(f, "CAPTURE {frame} {waveform} {memory_reference}") } Instruction::CircuitDefinition(CircuitDefinition { name, @@ -771,37 +765,28 @@ impl fmt::Display for Instruction { }) => { let mut parameter_str: String = parameters .iter() - .map(|p| format!("%{}", p)) + .map(|p| format!("%{p}")) .collect::>() .join(", "); if !parameter_str.is_empty() { - parameter_str = format!("({})", parameter_str); + parameter_str = format!("({parameter_str})"); } - write!(f, "DEFCIRCUIT {}{}", name, parameter_str)?; + write!(f, "DEFCIRCUIT {name}{parameter_str}")?; for qubit_variable in qubit_variables { - write!(f, " {}", qubit_variable)?; + write!(f, " {qubit_variable}")?; } writeln!(f, ":")?; for instruction in &**instructions { - writeln!(f, "\t{}", instruction)?; + writeln!(f, "\t{instruction}")?; } Ok(()) } Instruction::Convert(Convert { from, to }) => { - write!(f, "CONVERT {} {}", to, from)?; + write!(f, "CONVERT {to} {from}")?; Ok(()) } - Instruction::Declaration(Declaration { - name, - size, - sharing, - }) => { - write!(f, "DECLARE {} {}", name, size)?; - match sharing { - Some(shared) => write!(f, "SHARING {}", shared)?, - None => {} - } - Ok(()) + Instruction::Declaration(declaration) => { + write!(f, "{declaration}") } Instruction::Delay(Delay { qubits, @@ -810,9 +795,9 @@ impl fmt::Display for Instruction { }) => { write!(f, "DELAY {}", format_qubits(qubits))?; for frame_name in frame_names { - write!(f, " \"{}\"", frame_name)?; + write!(f, " \"{frame_name}\"")?; } - write!(f, " {}", duration) + write!(f, " {duration}") } Instruction::Fence(Fence { qubits }) => { if qubits.is_empty() { @@ -830,116 +815,45 @@ impl fmt::Display for Instruction { identifier, attributes .iter() - .map(|(k, v)| format!("\n\t{}: {}", k, v)) + .map(|(k, v)| format!("\n\t{k}: {v}")) .collect::() ), - Instruction::Gate(Gate { - name, - parameters, - qubits, - modifiers, - }) => { - let parameter_str = get_expression_parameter_string(parameters); - - let qubit_str = format_qubits(qubits); - let modifier_str = modifiers - .iter() - .map(|m| format!("{} ", m)) - .collect::>() - .join(""); - write!(f, "{}{}{} {}", modifier_str, name, parameter_str, qubit_str) + Instruction::Gate(gate) => { + write!(f, "{gate}") } - Instruction::GateDefinition(GateDefinition { - name, - parameters, - specification, - }) => { - let parameter_str: String = parameters.iter().map(|p| p.to_string()).collect(); - writeln!( - f, - "DEFGATE {}{} AS {}:", - name, - parameter_str, - match specification { - GateSpecification::Matrix(_) => "MATRIX", - GateSpecification::Permutation(_) => "PERMUTATION", - } - )?; - match specification { - GateSpecification::Matrix(matrix) => { - for row in matrix { - writeln!( - f, - "\t{}", - row.iter() - .map(|cell| format!("{}", cell)) - .collect::>() - .join(",") - )?; - } - } - GateSpecification::Permutation(permutation) => { - writeln!( - f, - "\t{}", - permutation - .iter() - .map(|i| format!("{}", i)) - .collect::>() - .join(", ") - )?; - } - } - Ok(()) + Instruction::GateDefinition(gate_definition) => { + write!(f, "{gate_definition}") } Instruction::Include(Include { filename }) => { - write!(f, r#"INCLUDE {:?}"#, filename)?; - Ok(()) + write!(f, r#"INCLUDE {filename:?}"#) } - Instruction::MeasureCalibrationDefinition(MeasureCalibrationDefinition { - qubit, - parameter, - instructions, - }) => { - write!(f, "DEFCAL MEASURE")?; - match qubit { - Some(qubit) => { - write!(f, " {}", qubit)?; - } - None => {} - } - - writeln!( - f, - " {}:\n\t{}", - parameter, - format_instructions(instructions) - ) + Instruction::MeasureCalibrationDefinition(measure_calibration) => { + write!(f, "{measure_calibration}") } Instruction::Measurement(Measurement { qubit, target }) => match target { - Some(reference) => write!(f, "MEASURE {} {}", qubit, reference), - None => write!(f, "MEASURE {}", qubit), + Some(reference) => write!(f, "MEASURE {qubit} {reference}"), + None => write!(f, "MEASURE {qubit}"), }, Instruction::Move(Move { destination, source, - }) => write!(f, "MOVE {} {}", destination, source), + }) => write!(f, "MOVE {destination} {source}"), Instruction::Exchange(Exchange { left, right }) => { - write!(f, "EXCHANGE {} {}", left, right) + write!(f, "EXCHANGE {left} {right}") } Instruction::Load(Load { destination, source, offset, }) => { - write!(f, "LOAD {} {} {}", destination, source, offset) + write!(f, "LOAD {destination} {source} {offset}") } Instruction::Store(Store { destination, offset, source, }) => { - write!(f, "STORE {} {} {}", destination, offset, source) + write!(f, "STORE {destination} {offset} {source}") } Instruction::Pulse(Pulse { blocking, @@ -949,21 +863,21 @@ impl fmt::Display for Instruction { if !blocking { write!(f, "NONBLOCKING ")? } - write!(f, "PULSE {} {}", frame, waveform) + write!(f, "PULSE {frame} {waveform}") } Instruction::Pragma(Pragma { name, arguments, data, }) => { - write!(f, "PRAGMA {}", name)?; + write!(f, "PRAGMA {name}")?; if !arguments.is_empty() { for arg in arguments { - write!(f, " {}", arg)?; + write!(f, " {arg}")?; } } if let Some(data) = data { - write!(f, " \"{}\"", data)?; + write!(f, " \"{data}\"")?; } Ok(()) } @@ -976,52 +890,44 @@ impl fmt::Display for Instruction { if !blocking { write!(f, "NONBLOCKING ")? } - write!(f, "RAW-CAPTURE {} {} {}", frame, duration, memory_reference) + write!(f, "RAW-CAPTURE {frame} {duration} {memory_reference}") } Instruction::Reset(Reset { qubit }) => match qubit { - Some(qubit) => write!(f, "RESET {}", qubit), + Some(qubit) => write!(f, "RESET {qubit}"), None => write!(f, "RESET"), }, Instruction::SetFrequency(SetFrequency { frame, frequency }) => { - write!(f, "SET-FREQUENCY {} {}", frame, frequency) + write!(f, "SET-FREQUENCY {frame} {frequency}") } Instruction::SetPhase(SetPhase { frame, phase }) => { - write!(f, "SET-PHASE {} {}", frame, phase) + write!(f, "SET-PHASE {frame} {phase}") } Instruction::SetScale(SetScale { frame, scale }) => { - write!(f, "SET-SCALE {} {}", frame, scale) + write!(f, "SET-SCALE {frame} {scale}") } Instruction::ShiftFrequency(ShiftFrequency { frame, frequency }) => { - write!(f, "SHIFT-FREQUENCY {} {}", frame, frequency) + write!(f, "SHIFT-FREQUENCY {frame} {frequency}") } Instruction::ShiftPhase(ShiftPhase { frame, phase }) => { - write!(f, "SHIFT-PHASE {} {}", frame, phase) + write!(f, "SHIFT-PHASE {frame} {phase}") } Instruction::SwapPhases(SwapPhases { frame_1, frame_2 }) => { - write!(f, "SWAP-PHASES {} {}", frame_1, frame_2) + write!(f, "SWAP-PHASES {frame_1} {frame_2}") + } + Instruction::WaveformDefinition(waveform_definition) => { + write!(f, "{waveform_definition}") } - Instruction::WaveformDefinition(WaveformDefinition { name, definition }) => write!( - f, - "DEFWAVEFORM {}{}:\n\t{}", - name, - get_string_parameter_string(&definition.parameters), - definition - .matrix - .iter() - .map(|e| format!("{}", e)) - .collect::>() - .join(", ") - ), Instruction::Halt => write!(f, "HALT"), Instruction::Nop => write!(f, "NOP"), - Instruction::Jump(Jump { target }) => write!(f, "JUMP @{}", target), + Instruction::Wait => write!(f, "WAIT"), + Instruction::Jump(Jump { target }) => write!(f, "JUMP @{target}"), Instruction::JumpUnless(JumpUnless { condition, target }) => { - write!(f, "JUMP-UNLESS @{} {}", target, condition) + write!(f, "JUMP-UNLESS @{target} {condition}") } Instruction::JumpWhen(JumpWhen { condition, target }) => { - write!(f, "JUMP-WHEN @{} {}", target, condition) + write!(f, "JUMP-WHEN @{target} {condition}") } - Instruction::Label(Label(label)) => write!(f, "LABEL @{}", label), + Instruction::Label(Label(label)) => write!(f, "LABEL @{label}"), Instruction::Comparison(Comparison { operator, operands }) => { write!( f, @@ -1033,7 +939,7 @@ impl fmt::Display for Instruction { write!(f, "{} {} {}", operator, operands.0, operands.1) } Instruction::UnaryLogic(UnaryLogic { operator, operand }) => { - write!(f, "{} {}", operator, operand) + write!(f, "{operator} {operand}") } } } @@ -1087,8 +993,8 @@ impl fmt::Display for Qubit { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Qubit::*; match self { - Fixed(value) => write!(f, "{}", value), - Variable(value) => write!(f, "{}", value), + Fixed(value) => write!(f, "{value}"), + Variable(value) => write!(f, "{value}"), } } } @@ -1255,6 +1161,7 @@ impl Instruction { | Instruction::Gate(_) | Instruction::GateDefinition(_) | Instruction::Halt + | Instruction::Wait | Instruction::Include(_) | Instruction::Jump(_) | Instruction::JumpUnless(_) diff --git a/src/lib.rs b/quil-rs/src/lib.rs similarity index 97% rename from src/lib.rs rename to quil-rs/src/lib.rs index a60ee34f..c6ce884c 100644 --- a/src/lib.rs +++ b/quil-rs/src/lib.rs @@ -40,5 +40,7 @@ pub mod instruction; mod macros; pub(crate) mod parser; pub mod program; +pub mod reserved; +pub mod validation; pub use program::Program; diff --git a/src/macros.rs b/quil-rs/src/macros.rs similarity index 100% rename from src/macros.rs rename to quil-rs/src/macros.rs diff --git a/src/parser/command.rs b/quil-rs/src/parser/command.rs similarity index 94% rename from src/parser/command.rs rename to quil-rs/src/parser/command.rs index 5b740212..fc670ad7 100644 --- a/src/parser/command.rs +++ b/quil-rs/src/parser/command.rs @@ -559,7 +559,10 @@ pub(crate) fn parse_include<'a>(input: ParserInput<'a>) -> InternalParserResult< #[cfg(test)] mod tests { - use crate::expression::{Expression, ExpressionFunction, InfixOperator, PrefixOperator}; + use crate::expression::{ + Expression, ExpressionFunction, FunctionCallExpression, InfixExpression, InfixOperator, + PrefixExpression, PrefixOperator, + }; use crate::instruction::{GateDefinition, GateSpecification, PragmaArgument}; use crate::parser::lexer::lex; use crate::{imag, real}; @@ -776,27 +779,27 @@ mod tests { 1/sqrt(2), -1/sqrt(2)"#, { // 1/sqrt(2) - let expression = Expression::Infix { + let expression = Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(1.0))), operator: InfixOperator::Slash, - right: Box::new(Expression::FunctionCall { + right: Box::new(Expression::FunctionCall(FunctionCallExpression { function: crate::expression::ExpressionFunction::SquareRoot, expression: Box::new(Expression::Number(real!(2.0))), - }), - }; + })), + }); // -1/sqrt(2) - let negative_expression = Expression::Infix { - left: Box::new(Expression::Prefix { + let negative_expression = Expression::Infix(InfixExpression { + left: Box::new(Expression::Prefix(PrefixExpression { operator: PrefixOperator::Minus, expression: Box::new(Expression::Number(real!(1.0))), - }), + })), operator: InfixOperator::Slash, - right: Box::new(Expression::FunctionCall { + right: Box::new(Expression::FunctionCall(FunctionCallExpression { function: crate::expression::ExpressionFunction::SquareRoot, expression: Box::new(Expression::Number(real!(2.0))), - }), - }; + })), + }); Instruction::GateDefinition(GateDefinition { name: "H".to_string(), @@ -820,54 +823,54 @@ mod tests { parameters: vec!["theta".to_string()], specification: GateSpecification::Matrix(vec![ vec![ - Expression::FunctionCall { + Expression::FunctionCall(FunctionCallExpression { function: crate::expression::ExpressionFunction::Cosine, - expression: Box::new(Expression::Infix { + expression: Box::new(Expression::Infix(InfixExpression { left: Box::new(Expression::Variable("theta".to_string())), operator: InfixOperator::Slash, right: Box::new(Expression::Number(real!(2.0))), - }), - }, - Expression::Infix { - left: Box::new(Expression::Prefix { + })), + }), + Expression::Infix(InfixExpression { + left: Box::new(Expression::Prefix(PrefixExpression { operator: PrefixOperator::Minus, expression: Box::new(Expression::Number(imag!(1f64))) - }), + })), operator: InfixOperator::Star, - right: Box::new(Expression::FunctionCall { + right: Box::new(Expression::FunctionCall(FunctionCallExpression { function: ExpressionFunction::Sine, - expression: Box::new(Expression::Infix { + expression: Box::new(Expression::Infix(InfixExpression { left: Box::new(Expression::Variable("theta".to_string())), operator: InfixOperator::Slash, right: Box::new(Expression::Number(real!(2.0))), - }), - }), - } + })), + })), + }) ], vec![ - Expression::Infix { - left: Box::new(Expression::Prefix { + Expression::Infix(InfixExpression { + left: Box::new(Expression::Prefix(PrefixExpression { operator: PrefixOperator::Minus, expression: Box::new(Expression::Number(imag!(1f64))) - }), + })), operator: InfixOperator::Star, - right: Box::new(Expression::FunctionCall { + right: Box::new(Expression::FunctionCall(FunctionCallExpression { function: ExpressionFunction::Sine, - expression: Box::new(Expression::Infix { + expression: Box::new(Expression::Infix(InfixExpression { left: Box::new(Expression::Variable("theta".to_string())), operator: InfixOperator::Slash, right: Box::new(Expression::Number(real!(2.0))), - }), - }), - }, - Expression::FunctionCall { + })), + })), + }), + Expression::FunctionCall(FunctionCallExpression { function: crate::expression::ExpressionFunction::Cosine, - expression: Box::new(Expression::Infix { + expression: Box::new(Expression::Infix(InfixExpression { left: Box::new(Expression::Variable("theta".to_string())), operator: InfixOperator::Slash, right: Box::new(Expression::Number(real!(2.0))), - }), - }, + })), + }), ], ]), }) diff --git a/src/parser/common.rs b/quil-rs/src/parser/common.rs similarity index 98% rename from src/parser/common.rs rename to quil-rs/src/parser/common.rs index 8fc583f0..3798ef9d 100644 --- a/src/parser/common.rs +++ b/quil-rs/src/parser/common.rs @@ -308,7 +308,7 @@ pub(crate) fn parse_waveform_name<'a>(input: ParserInput<'a>) -> InternalParserR let (input, name_extension) = opt(tuple((token!(Operator(Slash)), token!(Identifier(v)))))(input)?; if let Some((_, extension)) = name_extension { - name = format!("{}/{}", name, extension); + name = format!("{name}/{extension}"); } Ok((input, name)) } @@ -391,8 +391,7 @@ mod tests { let (remainder, waveform) = parse_waveform_invocation(&lexed).unwrap(); assert!( remainder.is_empty(), - "expected remainder to be empty, got {:?}", - remainder + "expected remainder to be empty, got {remainder:?}" ); assert_eq!( waveform.parameters, @@ -419,8 +418,7 @@ mod tests { let (remainder, matrix) = parse_matrix(&lexed).unwrap(); assert!( remainder.is_empty(), - "expected remainder to be empty, got {:?}", - remainder + "expected remainder to be empty, got {remainder:?}" ); assert_eq!(matrix.len(), 2); } @@ -432,8 +430,7 @@ mod tests { let (remainder, permutation) = parse_permutation(&lexed).unwrap(); assert!( remainder.is_empty(), - "expected remainder to be empty, got {:?}", - remainder + "expected remainder to be empty, got {remainder:?}" ); assert_eq!(permutation, vec![0, 1, 2, 3, 4, 5, 7, 6]); diff --git a/src/parser/error/error.rs b/quil-rs/src/parser/error/error.rs similarity index 99% rename from src/parser/error/error.rs rename to quil-rs/src/parser/error/error.rs index 6b56bdc9..5e4d22b0 100644 --- a/src/parser/error/error.rs +++ b/quil-rs/src/parser/error/error.rs @@ -147,7 +147,7 @@ where )?; if f.alternate() { if let Some(previous) = &self.previous { - write!(f, "\n\tcause: {}", previous)?; + write!(f, "\n\tcause: {previous}")?; } } Ok(()) diff --git a/src/parser/error/input.rs b/quil-rs/src/parser/error/input.rs similarity index 96% rename from src/parser/error/input.rs rename to quil-rs/src/parser/error/input.rs index eb559323..4e54bea3 100644 --- a/src/parser/error/input.rs +++ b/quil-rs/src/parser/error/input.rs @@ -40,9 +40,9 @@ impl ErrorInput for LexInput<'_> { std::str::from_utf8(self.get_line_beginning()) .map(|s| { if s.len() == self.len() { - format!("\"{}\"", s) + format!("\"{s}\"") } else { - format!("\"{}\"...", s) + format!("\"{s}\"...") } }) .unwrap_or_default() diff --git a/src/parser/error/internal.rs b/quil-rs/src/parser/error/internal.rs similarity index 100% rename from src/parser/error/internal.rs rename to quil-rs/src/parser/error/internal.rs diff --git a/src/parser/error/kind.rs b/quil-rs/src/parser/error/kind.rs similarity index 100% rename from src/parser/error/kind.rs rename to quil-rs/src/parser/error/kind.rs diff --git a/src/parser/error/mod.rs b/quil-rs/src/parser/error/mod.rs similarity index 90% rename from src/parser/error/mod.rs rename to quil-rs/src/parser/error/mod.rs index 7c7a7a04..2a5dfdc2 100644 --- a/src/parser/error/mod.rs +++ b/quil-rs/src/parser/error/mod.rs @@ -62,4 +62,11 @@ pub enum ParserErrorKind { /// Literals specified in the input cannot be supported without loss of precision #[error("using this literal will result in loss of precision")] UnsupportedPrecision, + + /// Encountered an error building a valid instruction from the parsed input + #[error("invalid {instruction_type}: {reason}")] + InvalidInstruction { + instruction_type: String, + reason: String, + }, } diff --git a/src/parser/expression.rs b/quil-rs/src/parser/expression.rs similarity index 89% rename from src/parser/expression.rs rename to quil-rs/src/parser/expression.rs index 0129d066..261f9a43 100644 --- a/src/parser/expression.rs +++ b/quil-rs/src/parser/expression.rs @@ -14,6 +14,7 @@ use nom::combinator::opt; +use crate::expression::{FunctionCallExpression, InfixExpression, PrefixExpression}; use crate::parser::InternalParserResult; use crate::{ expected_token, @@ -92,10 +93,10 @@ fn parse(input: ParserInput, precedence: Precedence) -> InternalParserResult precedence { @@ -132,10 +133,10 @@ fn parse_function_call<'a>( let (input, _) = token!(RParenthesis)(input)?; Ok(( input, - Expression::FunctionCall { + Expression::FunctionCall(FunctionCallExpression { function, expression: Box::new(expression), - }, + }), )) } @@ -202,11 +203,11 @@ fn parse_infix(input: ParserInput, left: Expression) -> InternalParserResult expected_token!(input, other_token, "infix operator".to_owned()), @@ -226,6 +227,7 @@ fn parse_prefix(input: ParserInput) -> InternalParserResult { #[cfg(test)] mod tests { + use crate::expression::{FunctionCallExpression, InfixExpression, PrefixExpression}; use crate::{expression::PrefixOperator, parser::lexer::lex}; use crate::{ expression::{Expression, ExpressionFunction, InfixOperator}, @@ -287,70 +289,70 @@ mod tests { function_call, parse_expression, "sin(1)", - Expression::FunctionCall { + Expression::FunctionCall(FunctionCallExpression { function: ExpressionFunction::Sine, expression: Box::new(Expression::Number(real!(1f64))), - } + }) ); test!( nested_function_call, parse_expression, "sin(sin(1))", - Expression::FunctionCall { + Expression::FunctionCall(FunctionCallExpression { function: ExpressionFunction::Sine, - expression: Box::new(Expression::FunctionCall { + expression: Box::new(Expression::FunctionCall(FunctionCallExpression { function: ExpressionFunction::Sine, expression: Box::new(Expression::Number(real!(1f64))), - }), - } + })), + }) ); test!( simple_infix, parse_expression, "1+2", - Expression::Infix { + Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(1f64))), operator: InfixOperator::Plus, right: Box::new(Expression::Number(real!(2f64))), - } + }) ); test!( infix_with_function_call, parse_expression, "-i*sin(%theta/2)", - Expression::Infix { - left: Box::new(Expression::Prefix { + Expression::Infix(InfixExpression { + left: Box::new(Expression::Prefix(PrefixExpression { operator: PrefixOperator::Minus, expression: Box::new(Expression::Number(imag!(1f64))), - }), + })), operator: InfixOperator::Star, - right: Box::new(Expression::FunctionCall { + right: Box::new(Expression::FunctionCall(FunctionCallExpression { function: ExpressionFunction::Sine, - expression: Box::new(Expression::Infix { + expression: Box::new(Expression::Infix(InfixExpression { left: Box::new(Expression::Variable("theta".to_owned())), operator: InfixOperator::Slash, right: Box::new(Expression::Number(real!(2f64))), - }), - }), - } + })), + })), + }) ); test!( infix_parenthesized, parse_expression, "(1+2i)*%a", - Expression::Infix { - left: Box::new(Expression::Infix { + Expression::Infix(InfixExpression { + left: Box::new(Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(1f64))), operator: InfixOperator::Plus, right: Box::new(Expression::Number(imag!(2f64))), - }), + })), operator: InfixOperator::Star, right: Box::new(Expression::Variable("a".to_owned())), - } + }) ); #[test] @@ -358,46 +360,46 @@ mod tests { let cases = vec![ ( "1 + ( 2 + 3 )", - Expression::Infix { + Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(1f64))), operator: InfixOperator::Plus, - right: Box::new(Expression::Infix { + right: Box::new(Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(2f64))), operator: InfixOperator::Plus, right: Box::new(Expression::Number(real!(3f64))), - }), - }, + })), + }), ), ( "1+(2+3)", - Expression::Infix { + Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(1f64))), operator: InfixOperator::Plus, - right: Box::new(Expression::Infix { + right: Box::new(Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(2f64))), operator: InfixOperator::Plus, right: Box::new(Expression::Number(real!(3f64))), - }), - }, + })), + }), ), ( "(1+2)+3", - Expression::Infix { - left: Box::new(Expression::Infix { + Expression::Infix(InfixExpression { + left: Box::new(Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(1f64))), operator: InfixOperator::Plus, right: Box::new(Expression::Number(real!(2f64))), - }), + })), operator: InfixOperator::Plus, right: Box::new(Expression::Number(real!(3f64))), - }, + }), ), ( "(((cos(((pi))))))", - Expression::FunctionCall { + Expression::FunctionCall(FunctionCallExpression { function: ExpressionFunction::Cosine, expression: Box::new(Expression::PiConstant), - }, + }), ), ]; diff --git a/src/parser/gate.rs b/quil-rs/src/parser/gate.rs similarity index 82% rename from src/parser/gate.rs rename to quil-rs/src/parser/gate.rs index cf63079a..5ecffa5b 100644 --- a/src/parser/gate.rs +++ b/quil-rs/src/parser/gate.rs @@ -18,8 +18,9 @@ use crate::{instruction::Instruction, token}; use super::{ common::{self, parse_gate_modifier}, + error::InternalError, expression::parse_expression, - ParserInput, + ParserErrorKind, ParserInput, }; use crate::instruction::Gate; use crate::parser::InternalParserResult; @@ -35,15 +36,16 @@ pub(crate) fn parse_gate<'a>(input: ParserInput<'a>) -> InternalParserResult<'a, ))(input)?; let parameters = parameters.unwrap_or_default(); let (input, qubits) = many0(common::parse_qubit)(input)?; - Ok(( - input, - Instruction::Gate(Gate { - name, - parameters, - qubits, - modifiers, - }), - )) + let gate = Gate::new(name.as_str(), parameters, qubits, modifiers).map_err(|e| { + nom::Err::Error(InternalError::from_kind( + input, + ParserErrorKind::InvalidInstruction { + instruction_type: "Gate".to_string(), + reason: e.to_string(), + }, + )) + })?; + Ok((input, Instruction::Gate(gate))) } #[cfg(test)] diff --git a/src/parser/instruction.rs b/quil-rs/src/parser/instruction.rs similarity index 97% rename from src/parser/instruction.rs rename to quil-rs/src/parser/instruction.rs index 3f72ab78..60d8b409 100644 --- a/src/parser/instruction.rs +++ b/quil-rs/src/parser/instruction.rs @@ -91,6 +91,7 @@ pub(crate) fn parse_instruction(input: ParserInput) -> InternalParserResult command::parse_shift_phase(remainder), Command::Store => command::parse_store(remainder), Command::Sub => command::parse_arithmetic(ArithmeticOperator::Subtract, remainder), + Command::Wait => Ok((remainder, Instruction::Wait)), Command::Xor => command::parse_logical_binary(BinaryOperator::Xor, remainder), } .map_err(|err| { @@ -151,7 +152,10 @@ mod tests { use nom_locate::LocatedSpan; - use crate::expression::{Expression, InfixOperator, PrefixOperator}; + use crate::expression::{ + Expression, FunctionCallExpression, InfixExpression, InfixOperator, PrefixExpression, + PrefixOperator, + }; use crate::instruction::{ Arithmetic, ArithmeticOperand, ArithmeticOperator, AttributeValue, BinaryLogic, BinaryOperand, BinaryOperator, Calibration, Capture, Comparison, ComparisonOperand, @@ -717,43 +721,43 @@ mod tests { parameters: vec![], specification: GateSpecification::Matrix(vec![ vec![ - Expression::Infix { + Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(1.0))), operator: InfixOperator::Slash, - right: Box::new(Expression::FunctionCall { + right: Box::new(Expression::FunctionCall(FunctionCallExpression { function: crate::expression::ExpressionFunction::SquareRoot, expression: Box::new(Expression::Number(real!(2.0))), - }), - }, - Expression::Infix { + })), + }), + Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(1.0))), operator: InfixOperator::Slash, - right: Box::new(Expression::FunctionCall { + right: Box::new(Expression::FunctionCall(FunctionCallExpression { function: crate::expression::ExpressionFunction::SquareRoot, expression: Box::new(Expression::Number(real!(2.0))), - }), - }, + })), + }), ], vec![ - Expression::Infix { + Expression::Infix(InfixExpression { left: Box::new(Expression::Number(real!(1.0))), operator: InfixOperator::Slash, - right: Box::new(Expression::FunctionCall { + right: Box::new(Expression::FunctionCall(FunctionCallExpression { function: crate::expression::ExpressionFunction::SquareRoot, expression: Box::new(Expression::Number(real!(2.0))), - }), - }, - Expression::Infix { - left: Box::new(Expression::Prefix { + })), + }), + Expression::Infix(InfixExpression { + left: Box::new(Expression::Prefix(PrefixExpression { operator: PrefixOperator::Minus, expression: Box::new(Expression::Number(real!(1.0))), - }), + })), operator: InfixOperator::Slash, - right: Box::new(Expression::FunctionCall { + right: Box::new(Expression::FunctionCall(FunctionCallExpression { function: crate::expression::ExpressionFunction::SquareRoot, expression: Box::new(Expression::Number(real!(2.0))), - }), - }, + })), + }), ], ]), })] diff --git a/src/parser/lexer/error.rs b/quil-rs/src/parser/lexer/error.rs similarity index 100% rename from src/parser/lexer/error.rs rename to quil-rs/src/parser/lexer/error.rs diff --git a/src/parser/lexer/mod.rs b/quil-rs/src/parser/lexer/mod.rs similarity index 98% rename from src/parser/lexer/mod.rs rename to quil-rs/src/parser/lexer/mod.rs index 078fad54..d75a2cab 100644 --- a/src/parser/lexer/mod.rs +++ b/quil-rs/src/parser/lexer/mod.rs @@ -34,9 +34,7 @@ use crate::parser::token::token_with_location; pub(crate) use error::InternalLexError; pub use error::{LexError, LexErrorKind}; -// TODO: replace manual parsing with strum::EnumString (FromStr)? -// See: https://github.com/rigetti/quil-rs/issues/94 -#[derive(Debug, Copy, Clone, PartialEq, Eq, strum::Display)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, strum::Display, strum::EnumString)] #[strum(serialize_all = "SCREAMING-KEBAB-CASE")] pub enum Command { Add, @@ -88,6 +86,7 @@ pub enum Command { ShiftPhase, Store, Sub, + Wait, Xor, } @@ -216,6 +215,7 @@ fn recognize_command_or_identifier(identifier: String) -> Token { "DEFCIRCUIT" => Token::Command(DefCircuit), "MEASURE" => Token::Command(Measure), "HALT" => Token::Command(Halt), + "WAIT" => Token::Command(Wait), "JUMP-WHEN" => Token::Command(JumpWhen), "JUMP-UNLESS" => Token::Command(JumpUnless), "JUMP" => Token::Command(Jump), @@ -262,7 +262,7 @@ fn lex_identifier_raw(input: LexInput) -> InternalLexResult { take_while1(is_valid_identifier_leading_character), take_while(is_valid_identifier_middle_character), )), - |(left, right)| format!("{}{}", left, right), + |(left, right)| format!("{left}{right}"), ), )(input) .and_then(|(remaining, result)| { diff --git a/src/parser/lexer/quoted_strings.rs b/quil-rs/src/parser/lexer/quoted_strings.rs similarity index 100% rename from src/parser/lexer/quoted_strings.rs rename to quil-rs/src/parser/lexer/quoted_strings.rs diff --git a/src/parser/lexer/wrapped_parsers.rs b/quil-rs/src/parser/lexer/wrapped_parsers.rs similarity index 100% rename from src/parser/lexer/wrapped_parsers.rs rename to quil-rs/src/parser/lexer/wrapped_parsers.rs diff --git a/src/parser/macros.rs b/quil-rs/src/parser/macros.rs similarity index 100% rename from src/parser/macros.rs rename to quil-rs/src/parser/macros.rs diff --git a/src/parser/mod.rs b/quil-rs/src/parser/mod.rs similarity index 100% rename from src/parser/mod.rs rename to quil-rs/src/parser/mod.rs diff --git a/src/parser/token.rs b/quil-rs/src/parser/token.rs similarity index 71% rename from src/parser/token.rs rename to quil-rs/src/parser/token.rs index 3a3aad92..8f8adf2b 100644 --- a/src/parser/token.rs +++ b/quil-rs/src/parser/token.rs @@ -101,28 +101,28 @@ impl fmt::Display for Token { Token::As => write!(f, "AS"), Token::Colon => write!(f, ":"), Token::Comma => write!(f, ","), - Token::Command(cmd) => write!(f, "{}", cmd), - Token::Comment(comment) => write!(f, "# {}", comment), - Token::DataType(typ) => write!(f, "{}", typ), - Token::Float(float) => write!(f, "{}", float), - Token::Identifier(ident) => write!(f, "{}", ident), + Token::Command(cmd) => write!(f, "{cmd}"), + Token::Comment(comment) => write!(f, "# {comment}"), + Token::DataType(typ) => write!(f, "{typ}"), + Token::Float(float) => write!(f, "{float}"), + Token::Identifier(ident) => write!(f, "{ident}"), Token::Indentation => write!(f, " "), - Token::Integer(i) => write!(f, "{}", i), - Token::Label(label) => write!(f, "{}", label), + Token::Integer(i) => write!(f, "{i}"), + Token::Label(label) => write!(f, "{label}"), Token::LBracket => write!(f, "["), Token::LParenthesis => write!(f, "("), Token::NonBlocking => write!(f, "NONBLOCKING"), Token::Matrix => write!(f, "MATRIX"), - Token::Modifier(m) => write!(f, "{}", m), + Token::Modifier(m) => write!(f, "{m}"), Token::NewLine => write!(f, "NEWLINE"), - Token::Operator(op) => write!(f, "{}", op), + Token::Operator(op) => write!(f, "{op}"), Token::Permutation => write!(f, "PERMUTATION"), Token::RBracket => write!(f, "]"), Token::RParenthesis => write!(f, ")"), Token::Semicolon => write!(f, ";"), Token::Sharing => write!(f, "SHARING"), - Token::String(s) => write!(f, "{:?}", s), - Token::Variable(v) => write!(f, "{}", v), + Token::String(s) => write!(f, "{s:?}"), + Token::Variable(v) => write!(f, "{v}"), } } } @@ -130,31 +130,31 @@ impl fmt::Display for Token { impl fmt::Debug for Token { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - Token::As => write!(f, "{}", self), + Token::As => write!(f, "{self}"), Token::Colon => write!(f, "COLON"), Token::Comma => write!(f, "COMMA"), - Token::Command(cmd) => write!(f, "COMMAND({})", cmd), - Token::Comment(comment) => write!(f, "COMMENT({:?})", comment), - Token::DataType(typ) => write!(f, "DATATYPE({})", typ), - Token::Float(float) => write!(f, "FLOAT({})", float), - Token::Identifier(id) => write!(f, "IDENTIFIER({})", id), + Token::Command(cmd) => write!(f, "COMMAND({cmd})"), + Token::Comment(comment) => write!(f, "COMMENT({comment:?})"), + Token::DataType(typ) => write!(f, "DATATYPE({typ})"), + Token::Float(float) => write!(f, "FLOAT({float})"), + Token::Identifier(id) => write!(f, "IDENTIFIER({id})"), Token::Indentation => write!(f, "INDENT"), - Token::Integer(i) => write!(f, "INTEGER({})", i), - Token::Label(label) => write!(f, "@{}", label), + Token::Integer(i) => write!(f, "INTEGER({i})"), + Token::Label(label) => write!(f, "@{label}"), Token::LBracket => write!(f, "LBRACKET"), Token::LParenthesis => write!(f, "LPAREN"), - Token::NonBlocking => write!(f, "{}", self), - Token::Matrix => write!(f, "{}", self), - Token::Modifier(m) => write!(f, "MODIFIER({})", m), + Token::NonBlocking => write!(f, "{self}"), + Token::Matrix => write!(f, "{self}"), + Token::Modifier(m) => write!(f, "MODIFIER({m})"), Token::NewLine => write!(f, "NEWLINE"), - Token::Operator(op) => write!(f, "OPERATOR({})", op), - Token::Permutation => write!(f, "{}", self), + Token::Operator(op) => write!(f, "OPERATOR({op})"), + Token::Permutation => write!(f, "{self}"), Token::RBracket => write!(f, "RBRACKET"), Token::RParenthesis => write!(f, "RPAREN"), Token::Semicolon => write!(f, "SEMICOLON"), - Token::Sharing => write!(f, "{}", self), - Token::String(s) => write!(f, "STRING({:?})", s), - Token::Variable(v) => write!(f, "VARIABLE({})", v), + Token::Sharing => write!(f, "{self}"), + Token::String(s) => write!(f, "STRING({s:?})"), + Token::Variable(v) => write!(f, "VARIABLE({v})"), } } } diff --git a/src/program/calibration.rs b/quil-rs/src/program/calibration.rs similarity index 98% rename from src/program/calibration.rs rename to quil-rs/src/program/calibration.rs index 1b7b3419..478b3ebd 100644 --- a/src/program/calibration.rs +++ b/quil-rs/src/program/calibration.rs @@ -27,8 +27,8 @@ use super::error::ProgramError; /// A collection of Quil calibrations (`DEFCAL` instructions) with utility methods. #[derive(Clone, Debug, Default, PartialEq)] pub struct CalibrationSet { - calibrations: Vec, - measure_calibrations: Vec, + pub calibrations: Vec, + pub measure_calibrations: Vec, } struct MatchedCalibration<'a> { @@ -321,6 +321,12 @@ impl CalibrationSet { self.measure_calibrations.push(calibration) } + /// Append another [`CalibrationSet`] onto this one + pub fn extend(&mut self, other: CalibrationSet) { + self.calibrations.extend(other.calibrations); + self.measure_calibrations.extend(other.measure_calibrations); + } + /// Return the Quil instructions which describe the contained calibrations. pub fn to_instructions(&self) -> Vec { self.calibrations diff --git a/src/program/error/leftover.rs b/quil-rs/src/program/error/leftover.rs similarity index 100% rename from src/program/error/leftover.rs rename to quil-rs/src/program/error/leftover.rs diff --git a/src/program/error/mod.rs b/quil-rs/src/program/error/mod.rs similarity index 72% rename from src/program/error/mod.rs rename to quil-rs/src/program/error/mod.rs index 81a6245e..910bd63d 100644 --- a/src/program/error/mod.rs +++ b/quil-rs/src/program/error/mod.rs @@ -27,6 +27,7 @@ pub use result::{disallow_leftover, map_parsed, recover}; pub use syntax::SyntaxError; /// Errors that may occur while parsing a [`Program`](crate::program::Program). +// TODO: [`Program`] should have its own error type: https://github.com/rigetti/quil-rs/issues/150 #[derive(Debug, PartialEq)] pub enum ProgramError { InvalidCalibration { @@ -35,6 +36,9 @@ pub enum ProgramError { }, RecursiveCalibration(Instruction), Syntax(SyntaxError), + InvalidQuiltInstruction(Instruction), + InvalidProtoQuilInstruction(Instruction), + UnsupportedOperation(Instruction), } impl From for ProgramError @@ -80,6 +84,11 @@ impl ProgramError { }, Self::RecursiveCalibration(inst) => ProgramError::RecursiveCalibration(inst), Self::Syntax(err) => ProgramError::Syntax(err.map_parsed(map)), + Self::InvalidQuiltInstruction(inst) => ProgramError::InvalidQuiltInstruction(inst), + Self::InvalidProtoQuilInstruction(inst) => { + ProgramError::InvalidProtoQuilInstruction(inst) + } + Self::UnsupportedOperation(inst) => ProgramError::UnsupportedOperation(inst), } } } @@ -93,11 +102,18 @@ where Self::InvalidCalibration { instruction, message, - } => write!(f, "invalid calibration `{}`: {}", instruction, message), + } => write!(f, "invalid calibration `{instruction}`: {message}"), Self::RecursiveCalibration(instruction) => { - write!(f, "instruction {} expands into itself", instruction) + write!(f, "instruction {instruction} expands into itself") } Self::Syntax(err) => fmt::Display::fmt(err, f), + Self::InvalidQuiltInstruction(inst) => write!(f, "invalid quilt instruction: {inst}"), + Self::InvalidProtoQuilInstruction(inst) => { + write!(f, "invalid protoquil instruction: {inst}") + } + Self::UnsupportedOperation(inst) => { + write!(f, "operation is not supported on instruction: {inst}") + } } } } @@ -111,6 +127,9 @@ where Self::InvalidCalibration { .. } => None, Self::RecursiveCalibration(_) => None, Self::Syntax(err) => Some(err), + Self::InvalidQuiltInstruction(_) => None, + Self::InvalidProtoQuilInstruction(_) => None, + Self::UnsupportedOperation(_) => None, } } } diff --git a/src/program/error/result.rs b/quil-rs/src/program/error/result.rs similarity index 100% rename from src/program/error/result.rs rename to quil-rs/src/program/error/result.rs diff --git a/src/program/error/syntax.rs b/quil-rs/src/program/error/syntax.rs similarity index 100% rename from src/program/error/syntax.rs rename to quil-rs/src/program/error/syntax.rs diff --git a/src/program/frame.rs b/quil-rs/src/program/frame.rs similarity index 95% rename from src/program/frame.rs rename to quil-rs/src/program/frame.rs index 8f8f9b4b..5374491d 100644 --- a/src/program/frame.rs +++ b/quil-rs/src/program/frame.rs @@ -40,9 +40,9 @@ impl FrameSet { /// Return all frames in the set which match all of these conditions. If a frame _would_ match, but is /// not present in this [FrameSet], then it is not returned (notably, the [FrameMatchCondition::Specific] /// match condition). - pub(crate) fn get_matching_keys<'s, 'a>( + pub(crate) fn get_matching_keys<'s>( &'s self, - condition: FrameMatchCondition<'a>, + condition: FrameMatchCondition, ) -> HashSet<&'s FrameIdentifier> { let keys = self.frames.keys(); @@ -90,6 +90,11 @@ impl FrameSet { self.frames.insert(identifier, attributes); } + /// Merge another [FrameSet] with this one, overwriting any existing keys + pub fn merge(&mut self, other: FrameSet) { + self.frames.extend(other.frames); + } + /// Return a new [FrameSet] which describes only the given [FrameIdentifier]s. pub fn intersection(&self, identifiers: &HashSet<&FrameIdentifier>) -> Self { let mut new_frameset = Self::new(); diff --git a/src/program/graph.rs b/quil-rs/src/program/graph.rs similarity index 99% rename from src/program/graph.rs rename to quil-rs/src/program/graph.rs index 34919681..9192412f 100644 --- a/src/program/graph.rs +++ b/quil-rs/src/program/graph.rs @@ -537,7 +537,8 @@ impl ScheduledProgram { | Instruction::ShiftPhase(_) | Instruction::SwapPhases(_) | Instruction::RawCapture(_) - | Instruction::Reset(_) => { + | Instruction::Reset(_) + | Instruction::Wait => { working_instructions.push(instruction); Ok(()) } @@ -639,10 +640,10 @@ impl ScheduledProgram { fn generate_autoincremented_label(block_labels: &IndexMap) -> String { let mut suffix = 0; - let mut label = format!("block_{}", suffix); + let mut label = format!("block_{suffix}"); while block_labels.get(&label).is_some() { suffix += 1; - label = format!("block_{}", suffix); + label = format!("block_{suffix}"); } label } diff --git a/src/program/graphviz_dot.rs b/quil-rs/src/program/graphviz_dot.rs similarity index 99% rename from src/program/graphviz_dot.rs rename to quil-rs/src/program/graphviz_dot.rs index b57d179b..5841b116 100644 --- a/src/program/graphviz_dot.rs +++ b/quil-rs/src/program/graphviz_dot.rs @@ -190,13 +190,13 @@ fn escape_label(original: &str) -> String { fn get_node_id(node: &ScheduledGraphNode, prefix: &str) -> String { match node { ScheduledGraphNode::BlockEnd => { - format!("\"{}_end\"", prefix) + format!("\"{prefix}_end\"") } ScheduledGraphNode::BlockStart => { - format!("\"{}_start\"", prefix) + format!("\"{prefix}_start\"") } ScheduledGraphNode::InstructionIndex(index) => { - format!("\"{}_{}\"", prefix, index) + format!("\"{prefix}_{index}\"") } } } diff --git a/src/program/memory.rs b/quil-rs/src/program/memory.rs similarity index 96% rename from src/program/memory.rs rename to quil-rs/src/program/memory.rs index 09851b59..2dad7f6b 100644 --- a/src/program/memory.rs +++ b/quil-rs/src/program/memory.rs @@ -14,7 +14,7 @@ use std::collections::HashSet; -use crate::expression::Expression; +use crate::expression::{Expression, FunctionCallExpression, InfixExpression, PrefixExpression}; use crate::instruction::{ Arithmetic, ArithmeticOperand, BinaryLogic, BinaryOperand, Capture, CircuitDefinition, Comparison, ComparisonOperand, Delay, Exchange, Gate, GateDefinition, GateSpecification, @@ -260,6 +260,7 @@ impl Instruction { | Instruction::Fence(_) | Instruction::FrameDefinition(_) | Instruction::Halt + | Instruction::Wait | Instruction::Include(_) | Instruction::Jump(Jump { target: _ }) | Instruction::Label(Label(_)) @@ -289,15 +290,19 @@ impl Expression { pub fn get_memory_references(&self) -> Vec<&MemoryReference> { match self { Expression::Address(reference) => vec![reference], - Expression::FunctionCall { expression, .. } => expression.get_memory_references(), - Expression::Infix { left, right, .. } => { + Expression::FunctionCall(FunctionCallExpression { expression, .. }) => { + expression.get_memory_references() + } + Expression::Infix(InfixExpression { left, right, .. }) => { let mut result = left.get_memory_references(); result.extend(right.get_memory_references()); result } Expression::Number(_) => vec![], Expression::PiConstant => vec![], - Expression::Prefix { expression, .. } => expression.get_memory_references(), + Expression::Prefix(PrefixExpression { expression, .. }) => { + expression.get_memory_references() + } Expression::Variable(_) => vec![], } } diff --git a/src/program/mod.rs b/quil-rs/src/program/mod.rs similarity index 88% rename from src/program/mod.rs rename to quil-rs/src/program/mod.rs index db044dec..5757ac89 100644 --- a/src/program/mod.rs +++ b/quil-rs/src/program/mod.rs @@ -13,6 +13,7 @@ // limitations under the License. use std::collections::{BTreeMap, HashSet}; +use std::ops; use std::str::FromStr; use nom_locate::LocatedSpan; @@ -100,6 +101,26 @@ impl Program { .for_each(|i| self.add_instruction(i)); } + /// Creates a new conjugate transpose of the [`Program`] by reversing the order of gate + /// instructions and applying the DAGGER modifier to each. + /// + /// + /// # Errors + /// + /// Errors if any of the instructions in the program are not [`Instruction::Gate`] + pub fn dagger(&self) -> Result { + self.to_instructions(true).into_iter().try_rfold( + Program::new(), + |mut new_program, instruction| match instruction { + Instruction::Gate(gate) => { + new_program.add_instruction(Instruction::Gate(gate.dagger())); + Ok(new_program) + } + _ => Err(ProgramError::::UnsupportedOperation(instruction)), + }, + ) + } + /// Expand any instructions in the program which have a matching calibration, leaving the others /// unchanged. Recurses though each instruction while ensuring there is no cycle in the expansion /// graph (i.e. no calibration expands directly or indirectly into itself) @@ -221,21 +242,7 @@ impl Program { let mut result = vec![]; if include_headers { - result.extend(self.memory_regions.iter().map(|(name, descriptor)| { - Instruction::Declaration(Declaration { - name: name.clone(), - size: descriptor.size.clone(), - sharing: descriptor.sharing.clone(), - }) - })); - result.extend(self.frames.to_instructions()); - result.extend(self.waveforms.iter().map(|(name, definition)| { - Instruction::WaveformDefinition(WaveformDefinition { - name: name.clone(), - definition: definition.clone(), - }) - })); - result.extend(self.calibrations.to_instructions()); + result.extend(self.to_headers()) } result.extend(self.instructions.clone()); @@ -243,10 +250,30 @@ impl Program { result } + pub fn to_headers(&self) -> Vec { + let mut result = vec![]; + result.extend(self.memory_regions.iter().map(|(name, descriptor)| { + Instruction::Declaration(Declaration { + name: name.clone(), + size: descriptor.size.clone(), + sharing: descriptor.sharing.clone(), + }) + })); + result.extend(self.frames.to_instructions()); + result.extend(self.waveforms.iter().map(|(name, definition)| { + Instruction::WaveformDefinition(WaveformDefinition { + name: name.clone(), + definition: definition.clone(), + }) + })); + result.extend(self.calibrations.to_instructions()); + result + } + pub fn to_string(&self, include_headers: bool) -> String { self.to_instructions(include_headers) .iter() - .map(|inst| format!("{}\n", inst)) + .map(|inst| format!("{inst}\n")) .collect() } } @@ -279,6 +306,20 @@ impl From> for Program { } } +impl<'a, 'b> ops::Add<&'b Program> for &'a Program { + type Output = Program; + + fn add(self, rhs: &'b Program) -> Program { + let mut new = self.clone(); + new.calibrations.extend(rhs.calibrations.clone()); + new.frames.merge(rhs.frames.clone()); + new.memory_regions.extend(rhs.memory_regions.clone()); + new.waveforms.extend(rhs.waveforms.clone()); + new.instructions.extend(rhs.instructions.clone()); + new + } +} + #[cfg(test)] mod tests { use std::{collections::HashSet, str::FromStr}; @@ -505,8 +546,7 @@ DEFFRAME 0 1 \"2q\": .collect(); assert_eq!( used_frames, expected_used_frames, - "Instruction {} *used* frames `{:?}` but we expected `{:?}`", - instruction, used_frames, expected_used_frames + "Instruction {instruction} *used* frames `{used_frames:?}` but we expected `{expected_used_frames:?}`" ); let blocked_frames: HashSet = program @@ -521,8 +561,7 @@ DEFFRAME 0 1 \"2q\": .collect(); assert_eq!( blocked_frames, expected_blocked_frames, - "Instruction {} *blocked* frames `{:?}` but we expected `{:?}`", - instruction, blocked_frames, expected_blocked_frames + "Instruction {instruction} *blocked* frames `{blocked_frames:?}` but we expected `{expected_blocked_frames:?}`" ); } } diff --git a/src/program/snapshots/quil_rs__program__graph__tests__graph__active_reset_single_frame.snap b/quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__active_reset_single_frame.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graph__tests__graph__active_reset_single_frame.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__active_reset_single_frame.snap diff --git a/src/program/snapshots/quil_rs__program__graph__tests__graph__chained_pulses.snap b/quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__chained_pulses.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graph__tests__graph__chained_pulses.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__chained_pulses.snap diff --git a/src/program/snapshots/quil_rs__program__graph__tests__graph__different_frames_blocking.snap b/quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__different_frames_blocking.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graph__tests__graph__different_frames_blocking.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__different_frames_blocking.snap diff --git a/src/program/snapshots/quil_rs__program__graph__tests__graph__different_frames_nonblocking.snap b/quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__different_frames_nonblocking.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graph__tests__graph__different_frames_nonblocking.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__different_frames_nonblocking.snap diff --git a/src/program/snapshots/quil_rs__program__graph__tests__graph__fence_all_with_nonblocking_pulses.snap b/quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__fence_all_with_nonblocking_pulses.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graph__tests__graph__fence_all_with_nonblocking_pulses.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__fence_all_with_nonblocking_pulses.snap diff --git a/src/program/snapshots/quil_rs__program__graph__tests__graph__jump.snap b/quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__jump.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graph__tests__graph__jump.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__jump.snap diff --git a/src/program/snapshots/quil_rs__program__graph__tests__graph__single_dependency.snap b/quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__single_dependency.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graph__tests__graph__single_dependency.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__single_dependency.snap diff --git a/src/program/snapshots/quil_rs__program__graph__tests__graph__single_instruction.snap b/quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__single_instruction.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graph__tests__graph__single_instruction.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graph__tests__graph__single_instruction.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__active_reset_single_frame.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__active_reset_single_frame.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__active_reset_single_frame.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__active_reset_single_frame.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__blocking_2q_pulse.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__blocking_2q_pulse.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__blocking_2q_pulse.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__blocking_2q_pulse.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__blocking_pulses_after_nonblocking.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__blocking_pulses_after_nonblocking.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__blocking_pulses_after_nonblocking.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__blocking_pulses_after_nonblocking.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__blocking_pulses_wrap_nonblocking.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__blocking_pulses_wrap_nonblocking.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__blocking_pulses_wrap_nonblocking.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__blocking_pulses_wrap_nonblocking.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__chained_pulses.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__chained_pulses.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__chained_pulses.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__chained_pulses.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_blocking.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_blocking.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_blocking.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_blocking.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_nonblocking.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_nonblocking.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_nonblocking.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_nonblocking.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_all.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_all.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_all.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_all.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_all_with_nonblocking_pulses.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_all_with_nonblocking_pulses.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_all_with_nonblocking_pulses.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_all_with_nonblocking_pulses.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_wrapper.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_wrapper.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_wrapper.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_wrapper.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__jump.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__jump.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__jump.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__jump.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__labels_only.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__labels_only.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__labels_only.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__labels_only.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__multiple_classical_instructions.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__multiple_classical_instructions.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__multiple_classical_instructions.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__multiple_classical_instructions.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulse.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulse.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulse.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulse.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulse_using_capture_results.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulse_using_capture_results.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulse_using_capture_results.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulse_using_capture_results.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulses_using_capture_results.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulses_using_capture_results.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulses_using_capture_results.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulses_using_capture_results.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__pulse_after_capture.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__pulse_after_capture.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__pulse_after_capture.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__pulse_after_capture.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__simple_capture.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__simple_capture.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__simple_capture.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__simple_capture.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__simple_memory_access.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__simple_memory_access.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__simple_memory_access.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__simple_memory_access.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__single_dependency.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__single_dependency.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__single_dependency.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__single_dependency.snap diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__single_instruction.snap b/quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__single_instruction.snap similarity index 100% rename from src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__single_instruction.snap rename to quil-rs/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__single_instruction.snap diff --git a/src/program/type_check.rs b/quil-rs/src/program/type_check.rs similarity index 98% rename from src/program/type_check.rs rename to quil-rs/src/program/type_check.rs index a6edbb89..e19f3558 100644 --- a/src/program/type_check.rs +++ b/quil-rs/src/program/type_check.rs @@ -2,7 +2,7 @@ //! //! See the [Quil spec](https://quil-lang.github.io/). use crate::{ - expression::Expression, + expression::{Expression, FunctionCallExpression, InfixExpression, PrefixExpression}, instruction::{ Arithmetic, ArithmeticOperand, ArithmeticOperator, BinaryLogic, BinaryOperand, BinaryOperator, Comparison, ComparisonOperand, ComparisonOperator, Exchange, Instruction, @@ -209,11 +209,15 @@ fn should_be_real( undefined_memory_reference(instruction, reference) } } - Expression::FunctionCall { expression, .. } => { + Expression::FunctionCall(FunctionCallExpression { expression, .. }) => { should_be_real(instruction, expression, memory_regions) } - Expression::Infix { left, right, .. } => should_be_real(instruction, left, memory_regions) - .and(should_be_real(instruction, right, memory_regions)), + Expression::Infix(InfixExpression { left, right, .. }) => should_be_real( + instruction, + left, + memory_regions, + ) + .and(should_be_real(instruction, right, memory_regions)), Expression::Number(value) => { if value.im.abs() > f64::EPSILON { real_value_required(instruction, this_expression, "`imaginary`") @@ -222,7 +226,7 @@ fn should_be_real( } } Expression::PiConstant => Ok(()), - Expression::Prefix { expression, .. } => { + Expression::Prefix(PrefixExpression { expression, .. }) => { should_be_real(instruction, expression, memory_regions) } Expression::Variable(_) => real_value_required(instruction, this_expression, "`variable`"), diff --git a/quil-rs/src/reserved.rs b/quil-rs/src/reserved.rs new file mode 100644 index 00000000..d0727efa --- /dev/null +++ b/quil-rs/src/reserved.rs @@ -0,0 +1,133 @@ +//! This module contains enums for reserved tokens in [quil](https://quil-lang.github.io) + +use std::{fmt::Display, str::FromStr}; + +use strum; + +/// An enum that can represent any reserved token in quil. +#[derive(Debug, PartialEq)] +pub enum ReservedToken { + Keyword(ReservedKeyword), + Gate(ReservedGate), + Constant(ReservedConstant), +} + +#[derive(Clone, Debug)] +pub struct NotReservedToken(String); + +impl FromStr for ReservedToken { + type Err = NotReservedToken; + + fn from_str(s: &str) -> Result { + if let Ok(keyword) = ReservedKeyword::from_str(s) { + Ok(Self::Keyword(keyword)) + } else if let Ok(gate) = ReservedGate::from_str(s) { + Ok(Self::Gate(gate)) + } else if let Ok(constant) = ReservedConstant::from_str(s) { + Ok(Self::Constant(constant)) + } else { + Err(NotReservedToken(format!("{s} is not a reserved token"))) + } + } +} + +impl Display for ReservedToken { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Keyword(keyword) => write!(f, "{keyword}"), + Self::Gate(gate) => write!(f, "{gate}"), + Self::Constant(constant) => write!(f, "{constant}"), + } + } +} + +/// Any reserved keyword that isn't specifically a gate identifier or constant +#[derive(Debug, PartialEq, Eq, strum::Display, strum::EnumString)] +#[strum(serialize_all = "UPPERCASE")] +pub enum ReservedKeyword { + Add, + And, + As, + Controlled, + Convert, + Dagger, + Declare, + DefCircuit, + DefGate, + Div, + Eq, + Exchange, + Forked, + Ge, + Gt, + Halt, + Include, + Ior, + Jump, + #[strum(serialize = "JUMP-UNLESS")] + JumpUnless, + #[strum(serialize = "JUMP-WHEN")] + JumpWhen, + Label, + Le, + Load, + Lt, + Matrix, + Measure, + Move, + Mul, + Neg, + Nop, + Not, + Offset, + #[strum(serialize = "PAULI-SUM")] + PauliSum, + Permutation, + Pragma, + Reset, + Sharing, + Store, + Sub, + Wait, + Xor, +} + +/// Every reserved Gate identifier +#[derive(Debug, PartialEq, Eq, strum::Display, strum::EnumString)] +#[strum(serialize_all = "UPPERCASE")] +pub enum ReservedGate { + CAN, + CCNOT, + CNOT, + CPHASE, + CPHASE00, + CPHASE01, + CPHASE10, + CSWAP, + CZ, + H, + I, + ISWAP, + PHASE, + PISWAP, + PSWAP, + RX, + RY, + RZ, + S, + SWAP, + T, + X, + XY, + Y, + Z, +} + +/// Every reserved constant +#[derive(Debug, PartialEq, Eq, strum::Display, strum::EnumString)] +#[strum(serialize_all = "lowercase")] +pub enum ReservedConstant { + #[strum(serialize = "i")] + Imaginary, + Pi, +} diff --git a/quil-rs/src/validation/identifier.rs b/quil-rs/src/validation/identifier.rs new file mode 100644 index 00000000..d7394318 --- /dev/null +++ b/quil-rs/src/validation/identifier.rs @@ -0,0 +1,66 @@ +//! Types and functions related to validating Quil identifiers +use std::str::FromStr; + +use once_cell::sync::Lazy; +use regex::Regex; +use thiserror; + +use crate::reserved::ReservedToken; + +#[derive(Debug, PartialEq, thiserror::Error)] +pub enum IdentifierValidationError { + #[error("{0} is a reserved token")] + Reserved(ReservedToken), + + #[error("{0} is not a valid identifier")] + Invalid(String), +} + +/// A regex that matches only valid Quil identifiers +const IDENTIFIER_REGEX_STRING: &str = r"^([A-Za-z_]|[A-Za-z_][A-Za-z0-9\-_]*[A-Za-z0-9_])$"; + +static IDENTIFIER_REGEX: Lazy = + Lazy::new(|| Regex::new(IDENTIFIER_REGEX_STRING).expect("regex should be valid")); + +/// Returns an error if the given identifier is not a valid Quil Identifier +pub fn validate_identifier(ident: &str) -> Result<(), IdentifierValidationError> { + match IDENTIFIER_REGEX.is_match(ident) { + true => Ok(()), + false => Err(IdentifierValidationError::Invalid(ident.to_string())), + } +} + +/// Returns an error if the given identifier is reserved, or if it is not a valid Quil identifier +pub fn validate_user_identifier(ident: &str) -> Result<(), IdentifierValidationError> { + ReservedToken::from_str(ident).map_or(validate_identifier(ident), |t| { + Err(IdentifierValidationError::Reserved(t)) + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use rstest::rstest; + + #[rstest] + #[case("Good_Ident1f1er-0", true)] + #[case("H", true)] + #[case("-Cant-start-with-dash", false)] + #[case("Cant-end-with-dash-", false)] + #[case("1-Cant-start-with-number", false)] + fn test_validate_identifier(#[case] input: &str, #[case] ok: bool) { + assert_eq!(validate_identifier(input).is_ok(), ok) + } + + #[rstest] + #[case("Good_Ident1f1er-0", true)] + #[case("DEFGATE", false)] + #[case("AS", false)] + #[case("pi", false)] + #[case("PAULI-SUM", false)] + #[case("H", false)] + #[case("G", true)] + fn test_validate_user_identifier(#[case] input: &str, #[case] ok: bool) { + assert_eq!(validate_user_identifier(input).is_ok(), ok) + } +} diff --git a/quil-rs/src/validation/mod.rs b/quil-rs/src/validation/mod.rs new file mode 100644 index 00000000..34cdf0af --- /dev/null +++ b/quil-rs/src/validation/mod.rs @@ -0,0 +1 @@ +pub mod identifier;