diff --git a/CHANGELOG.md b/CHANGELOG.md index 31e89232..ae1c287e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,16 @@ This changelog track changes to the qoqo project starting at version 0.5.0 ## Not released -### Changed - -* Rarely used qubit mapping is now the last argument in PragmaRepeatedMeasurement +## 0.6.0 ### Added +* Device trait: A minimal trait for quantum computing devices used with roqoqo * `RoqoqoBackendError` now has a variant `GenericError` for additional backend error types +### Added + +* Rarely used qubit mapping is now the last argument in PragmaRepeatedMeasurement +* PragmaGeneralNoise uses sigma^+ sigma^- and sigma^z as a basis to for Lindblad decoherence rates to avoid using complex rates. Rate and operators parameters of PragmaGeneralNoise have been combined in single parameter rates. ## 0.5.1 diff --git a/Cargo.lock b/Cargo.lock index 0e3e7ec5..b1ca9a1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bytemuck" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" + [[package]] name = "cfg-if" version = "0.1.10" @@ -46,9 +52,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ctor" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" +checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" dependencies = [ "quote", "syn", @@ -147,9 +153,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "lazy_static" @@ -159,9 +165,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.99" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" +checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" [[package]] name = "libm" @@ -171,9 +177,9 @@ checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" [[package]] name = "lock_api" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" dependencies = [ "scopeguard", ] @@ -189,9 +195,9 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39fe9195887b501f296ac918ecbff4650b6935da164d4902ef73e140c46a0dce" +checksum = "d506eb7e08d6329505faa8a3a00a5dcc6de9f76e0c77e4b75763ae3c770831ff" dependencies = [ "approx", "matrixmultiply", @@ -300,9 +306,9 @@ checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" [[package]] name = "parking_lot" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", @@ -311,9 +317,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ "cfg-if 1.0.0", "instant", @@ -362,18 +368,18 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.28" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" dependencies = [ "unicode-xid", ] [[package]] name = "pyo3" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af205762ba65eec9f27a2fa1a57a40644e8e3368784b8c8b2f2de48f6e8ddd96" +checksum = "a192cd06356bb941c663c969a7f3e27c7c8e187efe772c1406a447f122443f71" dependencies = [ "cfg-if 1.0.0", "indoc", @@ -388,18 +394,18 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755944027ce803c7238e59c5a18e59c1d0a4553db50b23e9ba209a568353028d" +checksum = "650911ce22a793e9af67a0a880741ab1519e4f84740642716cbe83e129d17a2e" dependencies = [ "once_cell", ] [[package]] name = "pyo3-macros" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd31b36bccfd902c78804bd96c28ea93eac6fa0ca311f9d21ef2230b6665b29a" +checksum = "92d6659c1e336eec5a6ebc53bd80705e31ea0b95bff03bf384e868984b8ce573" dependencies = [ "pyo3-macros-backend", "quote", @@ -408,9 +414,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c21c59ba36db9c823e931c662766b0dd01a030b1d96585b67d8857a96a56b972" +checksum = "0b425a4975523acb80087d24903cffce30287a1324ab29714ce33006043c7dbe" dependencies = [ "proc-macro2", "pyo3-build-config", @@ -610,6 +616,15 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "safe_arch" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" +dependencies = [ + "bytemuck", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -618,18 +633,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.127" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.127" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" dependencies = [ "proc-macro2", "quote", @@ -638,9 +653,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127" +checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950" dependencies = [ "itoa", "ryu", @@ -649,23 +664,24 @@ dependencies = [ [[package]] name = "serde_test" -version = "1.0.127" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de9e52f2f83e2608a121618b6d3885b514613aac702306232c4f035ff60fdb56" +checksum = "d82178225dbdeae2d5d190e8649287db6a3a32c6d24da22ae3146325aa353e4c" dependencies = [ "serde", ] [[package]] name = "simba" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e82063457853d00243beda9952e910b82593e4b07ae9f721b9278a99a0d3d5c" +checksum = "f0b7840f121a46d63066ee7a99fc81dcabbc6105e437cae43528cea199b5a05f" dependencies = [ "approx", "num-complex 0.4.0", "num-traits", "paste 1.0.5", + "wide", ] [[package]] @@ -676,9 +692,9 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "syn" -version = "1.0.74" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7" dependencies = [ "proc-macro2", "quote", @@ -700,18 +716,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" +checksum = "283d5230e63df9608ac7d9691adc1dfb6e701225436eb64d0b9a7f0a5a04f6ec" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" +checksum = "fa3884228611f5cd3608e2d409bf7dce832e4eb3135e3f11addbd7e41bd68e71" dependencies = [ "proc-macro2", "quote", @@ -772,6 +788,16 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wide" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd89cf484471f953ee84f07c0dff0ea20e9ddf976f03cabdf5dda48b221f22e7" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/qoqo-macros/LICENSE b/qoqo-macros/LICENSE new file mode 100644 index 00000000..7e63c825 --- /dev/null +++ b/qoqo-macros/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 HQS Quantum Simulations GmbH + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/qoqo-macros/src/lib.rs b/qoqo-macros/src/lib.rs index 6efb101c..4489364f 100644 --- a/qoqo-macros/src/lib.rs +++ b/qoqo-macros/src/lib.rs @@ -125,13 +125,6 @@ pub fn wrap( let py = gil.python(); Ok(self.internal.superoperator().unwrap().to_pyarray(py).to_owned()) } - /// Returns the probability associated with the noise operation - /// - /// Returns: - /// CalculatorFloat - pub fn probability(&self) -> CalculatorFloatWrapper{ - CalculatorFloatWrapper{cf_internal: self.internal.probability().clone()} - } /// Return the power of the noise gate /// /// Args: @@ -146,6 +139,20 @@ pub fn wrap( } else { TokenStream::new() }; + let operate_pragma_noise_proba_quote = + if attribute_arguments.contains("OperatePragmaNoiseProba") { + quote! { + /// Returns the probability associated with the noise operation + /// + /// Returns: + /// CalculatorFloat + pub fn probability(&self) -> CalculatorFloatWrapper{ + CalculatorFloatWrapper{cf_internal: self.internal.probability().clone()} + } + } + } else { + TokenStream::new() + }; let operate_single_qubit_quote = if attribute_arguments.contains("OperateSingleQubit") { quote! { /// Return the qubit the operation acts on @@ -358,6 +365,7 @@ pub fn wrap( #rotate_quote #operate_pragma_quote #operate_pragma_noise_quote + #operate_pragma_noise_proba_quote #define_quote #operate_constant_gate_quote fn __format__(&self, _format_spec: &str) -> PyResult { diff --git a/qoqo/Cargo.toml b/qoqo/Cargo.toml index 964cc950..a526cb89 100644 --- a/qoqo/Cargo.toml +++ b/qoqo/Cargo.toml @@ -39,7 +39,7 @@ serde_json = "1.0" [dev-dependencies] test-case = "1.1.0" -nalgebra = "0.28" +nalgebra = "0.29" [build-dependencies] quote = "1.0" diff --git a/qoqo/LICENSE b/qoqo/LICENSE new file mode 100644 index 00000000..7e63c825 --- /dev/null +++ b/qoqo/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 HQS Quantum Simulations GmbH + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/qoqo/src/operations/pragma_operations.rs b/qoqo/src/operations/pragma_operations.rs index 59a8f3dc..e78ca1e9 100644 --- a/qoqo/src/operations/pragma_operations.rs +++ b/qoqo/src/operations/pragma_operations.rs @@ -613,7 +613,13 @@ pub struct PragmaStopDecompositionBlock { qubits: Vec, } -#[wrap(Operate, OperateSingleQubit, OperatePragma, OperatePragmaNoise)] +#[wrap( + Operate, + OperateSingleQubit, + OperatePragma, + OperatePragmaNoise, + OperatePragmaNoiseProba +)] /// The damping PRAGMA noise operation. /// /// This PRAGMA operation applies a pure damping error corresponding to zero temperature environments. @@ -666,7 +672,13 @@ pub struct PragmaDamping { // } // } -#[wrap(Operate, OperateSingleQubit, OperatePragma, OperatePragmaNoise)] +#[wrap( + Operate, + OperateSingleQubit, + OperatePragma, + OperatePragmaNoise, + OperatePragmaNoiseProba +)] /// The depolarising PRAGMA noise operation. /// /// This PRAGMA operation applies a depolarising error corresponding to infinite temperature environments. @@ -719,7 +731,13 @@ pub struct PragmaDepolarising { // } // } -#[wrap(Operate, OperateSingleQubit, OperatePragma, OperatePragmaNoise)] +#[wrap( + Operate, + OperateSingleQubit, + OperatePragma, + OperatePragmaNoise, + OperatePragmaNoiseProba +)] /// The dephasing PRAGMA noise operation. /// /// This PRAGMA operation applies a pure dephasing error. @@ -772,7 +790,13 @@ pub struct PragmaDephasing { // } // } -#[wrap(Operate, OperateSingleQubit, OperatePragma, OperatePragmaNoise)] +#[wrap( + Operate, + OperateSingleQubit, + OperatePragma, + OperatePragmaNoise, + OperatePragmaNoiseProba +)] /// The random noise PRAGMA operation. /// /// This PRAGMA operation applies a pure damping error corresponding to zero temperature environments. @@ -864,21 +888,14 @@ insert_pyany_to_operation!( let gate_time: CalculatorFloat = convert_into_calculator_float(gatetm).map_err(|_| { QoqoError::ConversionError })?; - - let rt = op.call_method0("rate") - .map_err(|_| QoqoError::ConversionError)?; - let rate: CalculatorFloat = convert_into_calculator_float(rt).map_err(|_| { - QoqoError::ConversionError - })?; - - let array = op.call_method0("operators") + let array = op.call_method0("rates") .map_err(|_| QoqoError::ConversionError)?; - let densmat_casted: Vec = Vec::extract(array).unwrap(); + let densmat_casted: Vec = Vec::extract(array).unwrap(); let length: usize = densmat_casted.len(); let dim: usize = (length as f64).sqrt() as usize; - let operators = Array::from_shape_vec((dim, dim), densmat_casted).unwrap(); - Ok(PragmaGeneralNoise::new(qubit, gate_time, rate, operators).into()) + let rates = Array::from_shape_vec((dim, dim), densmat_casted).unwrap(); + Ok(PragmaGeneralNoise::new(qubit, gate_time, rates).into()) } ); insert_operation_to_pyobject!( @@ -896,25 +913,39 @@ insert_operation_to_pyobject!( impl PragmaGeneralNoiseWrapper { /// Create a PragmaGeneralNoise. /// + /// This PRAGMA operation applies a noise term according to the given operators. + /// The operators are represented by a 3x3 matrix: + /// + /// .. math :: + /// M = \begin{pmatrix} + /// a & b & c \\ + /// d & e & f \\ + /// g & h & j \\ + /// \end{pmatrix} + /// + /// where the coefficients correspond to the following summands + /// expanded from the first term of the non-coherent part of the Lindblad equation: + /// + /// .. math:: + /// \frac{d}{dt}\rho = \sum_{i,j=0}^{2} M_{i,j} L_{i} \rho L_{j}^{\dagger} - \frac{1}{2} \{ L_{j}^{\dagger} L_i, \rho \} \\ + /// L_0 = \sigma^{+} \\ + /// L_1 = \sigma^{-} \\ + /// L_3 = \sigma^{z} + /// + /// Applying the Pragma with a given `gate_time` corresponds to applying the full time-evolution under the Lindblad equation for `gate_time` time. + /// /// Args: /// qubit (int): The qubit the PRAGMA operation is applied to. /// gate_time (CalculatorFloat): The time (in seconds) the gate takes to be applied to the qubit on the (simulated) hardware - /// rate (CalculatorFloat): The error rate of the noise (in 1/second). - /// operators (list[complex]): The operators representing the general noise. + /// rates (list[complex]): The rate matrix M. /// /// Returns: /// self: The new PragmaGeneralNoise. #[new] - fn new( - qubit: usize, - gate_time: Py, - rate: Py, - operators: Py, - ) -> PyResult { - let operators_casted: Vec = Python::with_gil(|py| -> Vec { - Vec::extract(operators.as_ref(py)).unwrap() - }); - let operators_array = Array::from_shape_vec((3, 3), operators_casted).unwrap(); + fn new(qubit: usize, gate_time: Py, rates: Py) -> PyResult { + let rates_casted: Vec = + Python::with_gil(|py| -> Vec { Vec::extract(rates.as_ref(py)).unwrap() }); + let rates_array = Array::from_shape_vec((3, 3), rates_casted).unwrap(); let gate_time_cf = Python::with_gil(|py| -> PyResult { convert_into_calculator_float(gate_time.as_ref(py)).map_err(|_| { pyo3::exceptions::PyTypeError::new_err( @@ -922,15 +953,9 @@ impl PragmaGeneralNoiseWrapper { ) }) })?; - let rate_cf = Python::with_gil(|py| -> PyResult { - convert_into_calculator_float(rate.as_ref(py)).map_err(|_| { - pyo3::exceptions::PyTypeError::new_err( - "Argument rate cannot be converted to CalculatorFloat", - ) - }) - })?; + Ok(Self { - internal: PragmaGeneralNoise::new(qubit, gate_time_cf, rate_cf, operators_array), + internal: PragmaGeneralNoise::new(qubit, gate_time_cf, rates_array), }) } @@ -952,32 +977,35 @@ impl PragmaGeneralNoiseWrapper { } } - /// Return the `rate` of the PRAGMA operation. - /// - /// Returns: - /// CalculatorFloat: The rate of the PRAGMA operation. - fn rate(&self) -> CalculatorFloatWrapper { - CalculatorFloatWrapper { - cf_internal: self.internal.rate().clone(), - } - } - - /// Return the operators of the PRAGMA operation. + /// Return the rates of the PRAGMA operation. /// /// Returns: - /// np.ndarray: The operators of the PRAGMA operation. - fn operators(&self) -> Py> { - Python::with_gil(|py| -> Py> { + /// np.ndarray: The rates of the PRAGMA operation. + fn rates(&self) -> Py> { + Python::with_gil(|py| -> Py> { self.internal - .operators() + .rates() .iter() .cloned() - .collect::>() + .collect::>() .to_pyarray(py) .to_owned() }) } + /// Return the rates of the PRAGMA operation. + /// + /// Returns: + /// np.ndarray: The rates of the PRAGMA operation. + fn superoperator(&self) -> PyResult>> { + Python::with_gil(|py| -> PyResult>> { + match self.internal.superoperator() { + Ok(x) => Ok(x.to_pyarray(py).to_owned()), + Err(err) => Err(PyRuntimeError::new_err(format!("{:?}", err))), + } + }) + } + /// List all involved qubits. /// /// Returns: diff --git a/qoqo/tests/integration/operations/operation_conversions.rs b/qoqo/tests/integration/operations/operation_conversions.rs index e0074549..785fdb7e 100644 --- a/qoqo/tests/integration/operations/operation_conversions.rs +++ b/qoqo/tests/integration/operations/operation_conversions.rs @@ -99,7 +99,7 @@ use test_case::test_case; #[test_case(Operation::from(PragmaDepolarising::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))); "PragmaDepolarising")] #[test_case(Operation::from(PragmaDephasing::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))); "PragmaDephasing")] #[test_case(Operation::from(PragmaRandomNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))); "PragmaRandomNoise")] -#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), operators())); "PragmaGeneralNoise")] +#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators())); "PragmaGeneralNoise")] #[test_case(Operation::from(PragmaConditional::new(String::from("ro"), 1, create_circuit())); "PragmaConditional")] fn test_conversion(input: Operation) { pyo3::prepare_freethreaded_python(); @@ -136,24 +136,8 @@ fn densitymatrix() -> Array2 { densitymatrix } -fn operators() -> Array2 { - let operators: Array2 = array![ - [ - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0) - ], - ]; +fn operators() -> Array2 { + let operators: Array2 = array![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],]; operators } diff --git a/qoqo/tests/integration/operations/pragma_operations.rs b/qoqo/tests/integration/operations/pragma_operations.rs index 0f35a75a..72944585 100644 --- a/qoqo/tests/integration/operations/pragma_operations.rs +++ b/qoqo/tests/integration/operations/pragma_operations.rs @@ -85,24 +85,8 @@ fn densitymatrix() -> Array2 { densitymatrix } -fn operators() -> Array2 { - let operators: Array2 = array![ - [ - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0) - ], - ]; +fn operators() -> Array2 { + let operators: Array2 = array![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],]; operators } @@ -474,7 +458,6 @@ fn test_pyo3_inputs_generalnoise() { let input_pragma = Operation::from(PragmaGeneralNoise::new( 0, CalculatorFloat::from(0.005), - CalculatorFloat::from(0.02), operators(), )); pyo3::prepare_freethreaded_python(); @@ -494,12 +477,8 @@ fn test_pyo3_inputs_generalnoise() { CalculatorFloat::from(0.005), ); - let rate_op: &f64 = - &f64::extract(operation.call_method0(py, "rate").unwrap().as_ref(py)).unwrap(); - assert_eq!(CalculatorFloat::from(rate_op), CalculatorFloat::from(0.02)); - - let to_operators_op: Vec = - Vec::extract(operation.call_method0(py, "operators").unwrap().as_ref(py)).unwrap(); + let to_operators_op: Vec = + Vec::extract(operation.call_method0(py, "rates").unwrap().as_ref(py)).unwrap(); let operators_op = Array::from_shape_vec((3, 3), to_operators_op).unwrap(); assert_eq!(operators_op, operators()); } @@ -587,7 +566,7 @@ fn test_pyo3_involved_qubits_all(input_definition: Operation) { #[test_case(Operation::from(PragmaDepolarising::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))); "PragmaDepolarising")] #[test_case(Operation::from(PragmaDephasing::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))); "PragmaDephasing")] #[test_case(Operation::from(PragmaRandomNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))); "PragmaRandomNoise")] -#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), operators())); "PragmaGeneralNoise")] +#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators())); "PragmaGeneralNoise")] #[test_case(Operation::from(PragmaConditional::new(String::from("ro"), 1, create_circuit())); "PragmaConditional")] fn test_pyo3_involved_qubits_qubit(input_definition: Operation) { pyo3::prepare_freethreaded_python(); @@ -645,8 +624,8 @@ fn test_pyo3_involved_qubits_qubit_overrotation(input_definition: Operation) { "PragmaDephasing { qubit: 0, gate_time: Float(0.005), rate: Float(0.02) }"; "PragmaDephasing")] #[test_case(Operation::from(PragmaRandomNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))), "PragmaRandomNoise { qubit: 0, gate_time: Float(0.005), depolarising_rate: Float(0.02), dephasing_rate: Float(0.01) }"; "PragmaRandomNoise")] -#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), operators())), - "PragmaGeneralNoise { qubit: 0, gate_time: Float(0.005), rate: Float(0.02), operators: [[Complex { re: 1.0, im: 0.0 }, Complex { re: 0.0, im: 0.0 }, Complex { re: 0.0, im: 0.0 }],\n [Complex { re: 0.0, im: 0.0 }, Complex { re: 1.0, im: 0.0 }, Complex { re: 0.0, im: 0.0 }],\n [Complex { re: 0.0, im: 0.0 }, Complex { re: 0.0, im: 0.0 }, Complex { re: 1.0, im: 0.0 }]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2 }"; "PragmaGeneralNoise")] +#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators())), + "PragmaGeneralNoise { qubit: 0, gate_time: Float(0.005), rates: [[1.0, 0.0, 0.0],\n [0.0, 1.0, 0.0],\n [0.0, 0.0, 1.0]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2 }"; "PragmaGeneralNoise")] #[test_case(Operation::from(PragmaConditional::new(String::from("ro"), 1, Circuit::default())), "PragmaConditional { condition_register: \"ro\", condition_index: 1, circuit: Circuit { definitions: [], operations: [] } }"; "PragmaConditional")] fn test_pyo3_format_repr(input_measurement: Operation, format_repr: &str) { @@ -693,7 +672,7 @@ fn test_pyo3_format_repr_overrotation(input_measurement: Operation, format_repr: #[test_case(Operation::from(PragmaDepolarising::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))); "PragmaDepolarising")] #[test_case(Operation::from(PragmaDephasing::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))); "PragmaDephasing")] #[test_case(Operation::from(PragmaRandomNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))); "PragmaRandomNoise")] -#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), operators())); "PragmaGeneralNoise")] +#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators())); "PragmaGeneralNoise")] #[test_case(Operation::from(PragmaConditional::new(String::from("ro"), 1, create_circuit())); "PragmaConditional")] fn test_pyo3_copy_deepcopy(input_measurement: Operation) { pyo3::prepare_freethreaded_python(); @@ -826,7 +805,6 @@ fn test_pyo3_tags_multi_overrotation(input_measurement: Operation, tag_name: &st /// Test tags function for Pragmas that are also SingleQubitGates #[test_case(Operation::from(PragmaActiveReset::new(0)), "PragmaActiveReset"; "PragmaActiveReset")] -#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), operators())), "PragmaGeneralNoise"; "PragmaGeneralNoise")] #[test_case(Operation::from(PragmaConditional::new(String::from("ro"), 1, create_circuit())), "PragmaConditional"; "PragmaConditional")] fn test_pyo3_tags_single(input_measurement: Operation, tag_name: &str) { pyo3::prepare_freethreaded_python(); @@ -844,6 +822,25 @@ fn test_pyo3_tags_single(input_measurement: Operation, tag_name: &str) { assert_eq!(tags_op, tags_param); } +/// Test tags function for PragmaGeneralNoise +#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators())), "PragmaGeneralNoise"; "PragmaGeneralNoise")] +fn test_pyo3_tags_general_noise(input_measurement: Operation, tag_name: &str) { + pyo3::prepare_freethreaded_python(); + let gil = pyo3::Python::acquire_gil(); + let py = gil.python(); + let operation = convert_operation_to_pyobject(input_measurement).unwrap(); + let to_tag = operation.call_method0(py, "tags").unwrap(); + let tags_op: &Vec<&str> = &Vec::extract(to_tag.as_ref(py)).unwrap(); + let tags_param: &[&str] = &[ + "Operation", + "SingleQubitOperation", + "PragmaOperation", + "PragmaNoiseOperation", + tag_name, + ]; + assert_eq!(tags_op, tags_param); +} + /// Test tags function for Noise Pragmas #[test_case(Operation::from(PragmaDamping::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))), "PragmaDamping"; "PragmaDamping")] #[test_case(Operation::from(PragmaDepolarising::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))), "PragmaDepolarising"; "PragmaDepolarising")] @@ -861,6 +858,7 @@ fn test_pyo3_tags_noise(input_measurement: Operation, tag_name: &str) { "SingleQubitOperation", "PragmaOperation", "PragmaNoiseOperation", + "PragmaNoiseProbaOperation", tag_name, ]; assert_eq!(tags_op, tags_param); @@ -882,7 +880,7 @@ fn test_pyo3_tags_noise(input_measurement: Operation, tag_name: &str) { #[test_case(Operation::from(PragmaDepolarising::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))), "PragmaDepolarising"; "PragmaDepolarising")] #[test_case(Operation::from(PragmaDephasing::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))), "PragmaDephasing"; "PragmaDephasing")] #[test_case(Operation::from(PragmaRandomNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))), "PragmaRandomNoise"; "PragmaRandomNoise")] -#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), operators())), "PragmaGeneralNoise"; "PragmaGeneralNoise")] +#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators())), "PragmaGeneralNoise"; "PragmaGeneralNoise")] #[test_case(Operation::from(PragmaConditional::new(String::from("ro"), 1, create_circuit())), "PragmaConditional"; "PragmaConditional")] fn test_pyo3_hqslang(input_measurement: Operation, hqslang_param: &str) { pyo3::prepare_freethreaded_python(); @@ -921,7 +919,7 @@ fn test_pyo3_hqslang_overrotation(input_measurement: Operation, hqslang_param: & #[test_case(Operation::from(PragmaDepolarising::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))); "PragmaDepolarising")] #[test_case(Operation::from(PragmaDephasing::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))); "PragmaDephasing")] #[test_case(Operation::from(PragmaRandomNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))); "PragmaRandomNoise")] -#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), operators())); "PragmaGeneralNoise")] +#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators())); "PragmaGeneralNoise")] #[test_case(Operation::from(PragmaConditional::new(String::from("ro"), 1, create_circuit())); "PragmaConditional")] fn test_pyo3_is_parametrized(input_measurement: Operation) { pyo3::prepare_freethreaded_python(); @@ -998,8 +996,8 @@ fn test_pyo3_is_parametrized_overrotation(input_measurement: Operation) { #[test_case(Operation::from(PragmaRandomNoise::new(0, CalculatorFloat::from("test"), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))), Operation::from(PragmaRandomNoise::new(0, CalculatorFloat::from(1.0), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))); "PragmaRandomNoise")] -#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from("test"), CalculatorFloat::from(0.02), operators())), - Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(1.0), CalculatorFloat::from(0.02), operators())); +#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from("test"), operators())), + Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(1.0), operators())); "PragmaGeneralNoise")] #[test_case(Operation::from(PragmaConditional::new(String::from("ro"), 1, create_circuit())), Operation::from(PragmaConditional::new(String::from("ro"), 1, create_circuit())); @@ -1079,7 +1077,7 @@ fn test_pyo3_substitute_parameters_overrotation() { "PragmaDephasing")] #[test_case(Operation::from(PragmaRandomNoise::new(0, CalculatorFloat::from("test"), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))); "PragmaRandomNoise")] -#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from("test"), CalculatorFloat::from(0.02), operators())); +#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from("test"), operators())); "PragmaGeneralNoise")] fn test_pyo3_substitute_params_error(input_operation: Operation) { pyo3::prepare_freethreaded_python(); @@ -1153,8 +1151,8 @@ fn test_pyo3_substituteparameters_error(input_operation: Operation) { #[test_case(Operation::from(PragmaRandomNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))), Operation::from(PragmaRandomNoise::new(2, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))); "PragmaRandomNoise")] -#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), operators())), - Operation::from(PragmaGeneralNoise::new(2, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), operators())); +#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators())), + Operation::from(PragmaGeneralNoise::new(2, CalculatorFloat::from(0.005), operators())); "PragmaGeneralNoise")] #[test_case(Operation::from(PragmaConditional::new(String::from("ro"), 1, create_circuit())), Operation::from(PragmaConditional::new(String::from("ro"), 1, circuit_remapped())); @@ -1459,8 +1457,8 @@ fn test_pyo3_noise_powercf(first_op: Operation, second_op: Operation) { #[test_case(Operation::from(PragmaRandomNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))), Operation::from(PragmaRandomNoise::new(2, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))); "PragmaRandomNoise")] -#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), operators())), - Operation::from(PragmaGeneralNoise::new(2, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), operators())); +#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators())), + Operation::from(PragmaGeneralNoise::new(2, CalculatorFloat::from(0.005), operators())); "PragmaGeneralNoise")] #[test_case(Operation::from(PragmaConditional::new(String::from("ro"), 1, create_circuit())), Operation::from(PragmaConditional::new(String::from("ro"), 1, circuit_remapped())); @@ -2389,18 +2387,15 @@ fn test_pyo3_new_general_noise() { let to_get_operators = Operation::from(PragmaGeneralNoise::new( 0, CalculatorFloat::from(0.005), - CalculatorFloat::from(0.02), operators(), )); let convert_to_get_operators = convert_operation_to_pyobject(to_get_operators) .unwrap() .clone(); - let operators_op = convert_to_get_operators - .call_method0(py, "operators") - .unwrap(); + let operators_op = convert_to_get_operators.call_method0(py, "rates").unwrap(); let new_op = operation - .call1((0, 0.005, 0.02, operators_op.clone())) + .call1((0, 0.005, operators_op.clone())) .unwrap() .cast_as::>() .unwrap(); @@ -2425,7 +2420,7 @@ fn test_pyo3_new_general_noise() { // Testing PartialEq, Clone and Debug let pragma_wrapper = new_op.extract::().unwrap(); let new_op_diff = operation - .call1((1, 0.005, 0.02, operators_op)) + .call1((1, 0.005, operators_op)) .unwrap() .cast_as::>() .unwrap(); @@ -2437,7 +2432,7 @@ fn test_pyo3_new_general_noise() { assert_eq!( format!("{:?}", pragma_wrapper), - "PragmaGeneralNoiseWrapper { internal: PragmaGeneralNoise { qubit: 0, gate_time: Float(0.005), rate: Float(0.02), operators: [[Complex { re: 1.0, im: 0.0 }, Complex { re: 0.0, im: 0.0 }, Complex { re: 0.0, im: 0.0 }],\n [Complex { re: 0.0, im: 0.0 }, Complex { re: 1.0, im: 0.0 }, Complex { re: 0.0, im: 0.0 }],\n [Complex { re: 0.0, im: 0.0 }, Complex { re: 0.0, im: 0.0 }, Complex { re: 1.0, im: 0.0 }]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2 } }" + "PragmaGeneralNoiseWrapper { internal: PragmaGeneralNoise { qubit: 0, gate_time: Float(0.005), rates: [[1.0, 0.0, 0.0],\n [0.0, 1.0, 0.0],\n [0.0, 0.0, 1.0]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2 } }" ); } @@ -2495,7 +2490,7 @@ fn test_pyo3_new_conditional() { #[test_case(Operation::from(PragmaDepolarising::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))); "PragmaDepolarising")] #[test_case(Operation::from(PragmaDephasing::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02))); "PragmaDephasing")] #[test_case(Operation::from(PragmaRandomNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), CalculatorFloat::from(0.01))); "PragmaRandomNoise")] -#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02), operators())); "PragmaGeneralNoise")] +#[test_case(Operation::from(PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators())); "PragmaGeneralNoise")] fn test_pyo3_remapqubits_error(input_operation: Operation) { // preparation pyo3::prepare_freethreaded_python(); diff --git a/roqoqo-derive/LICENSE b/roqoqo-derive/LICENSE new file mode 100644 index 00000000..7e63c825 --- /dev/null +++ b/roqoqo-derive/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 HQS Quantum Simulations GmbH + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/roqoqo-derive/src/lib.rs b/roqoqo-derive/src/lib.rs index 4fe4d0a8..8634b1d6 100644 --- a/roqoqo-derive/src/lib.rs +++ b/roqoqo-derive/src/lib.rs @@ -102,6 +102,15 @@ pub fn derive_operate_noise_pragmas(input: proc_macro::TokenStream) -> proc_macr operate_unitary::dispatch_struct_enum_operate_noise_pragma(parsed_input).into() } +/// Derive macro for the [roqoqo::OperatePragmaNoiseProba] trait +#[proc_macro_derive(OperatePragmaNoiseProba)] +pub fn derive_operate_noise_proba_pragmas( + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let parsed_input = parse_macro_input!(input as DeriveInput); + operate_unitary::dispatch_struct_enum_operate_noise_proba_pragma(parsed_input).into() +} + /// Derive macro for the [roqoqo::OperateGate] trait #[proc_macro_derive(OperateGate)] pub fn derive_operate_gate(input: proc_macro::TokenStream) -> proc_macro::TokenStream { diff --git a/roqoqo-derive/src/operate_unitary.rs b/roqoqo-derive/src/operate_unitary.rs index dc2f66e3..92797c69 100644 --- a/roqoqo-derive/src/operate_unitary.rs +++ b/roqoqo-derive/src/operate_unitary.rs @@ -441,11 +441,6 @@ fn operate_noise_pragma_enum(de: DataEnum, ident: Ident) -> TokenStream { &#ident::#vident(ref inner) => {OperatePragmaNoise::superoperator(&(*inner))}, } }); - let match_proba_quotes = variants_with_type.clone().map(|(vident, _, _)| { - quote! { - &#ident::#vident(ref inner) => inner.probability(), - } - }); let match_pow_quotes = variants_with_type.map(|(vident, _, _)| { quote! { &#ident::#vident(ref inner) => #ident::#vident(OperatePragmaNoise::powercf(&(*inner), power)), @@ -460,15 +455,38 @@ fn operate_noise_pragma_enum(de: DataEnum, ident: Ident) -> TokenStream { _ => panic!("Unexpectedly cannot match variant") } } - fn probability(&self) -> CalculatorFloat{ + fn powercf(&self, power: CalculatorFloat) -> #ident { match self{ - #(#match_proba_quotes)* + #(#match_pow_quotes)* _ => panic!("Unexpectedly cannot match variant") } } - fn powercf(&self, power: CalculatorFloat) -> #ident { + } + }; + q +} + +pub fn dispatch_struct_enum_operate_noise_proba_pragma(input: DeriveInput) -> TokenStream { + let ident = input.ident; + match input.data { + Data::Enum(de) => operate_noise_proba_pragma_enum(de, ident), + _ => panic!("OperatePragmaNoiseProba can only be derived on enums"), + } +} + +fn operate_noise_proba_pragma_enum(de: DataEnum, ident: Ident) -> TokenStream { + let variants_with_type = extract_variants_with_types(de).into_iter(); + let match_proba_quotes = variants_with_type.map(|(vident, _, _)| { + quote! { + &#ident::#vident(ref inner) => inner.probability(), + } + }); + let q = quote! { + #[automatically_derived] + impl OperatePragmaNoiseProba for #ident{ + fn probability(&self) -> CalculatorFloat{ match self{ - #(#match_pow_quotes)* + #(#match_proba_quotes)* _ => panic!("Unexpectedly cannot match variant") } } diff --git a/roqoqo-test/Cargo.toml b/roqoqo-test/Cargo.toml index 52b45436..6cf5777b 100644 --- a/roqoqo-test/Cargo.toml +++ b/roqoqo-test/Cargo.toml @@ -20,7 +20,7 @@ crate-type = ["rlib"] qoqo_calculator = {version="0.2"} roqoqo = {version="0.6", path="../roqoqo", features=["serialize"]} rand = "0.8" -nalgebra = "0.28" +nalgebra = "0.29" ndarray = { version = "0.15" } #roqoqo-derive = {version="0.1", path="../roqoqo-derive"} diff --git a/roqoqo/Cargo.toml b/roqoqo/Cargo.toml index 75f49038..3986aab6 100644 --- a/roqoqo/Cargo.toml +++ b/roqoqo/Cargo.toml @@ -29,6 +29,7 @@ dyn-clone = {version="1.0", optional=true} qoqo_calculator = { version="0.3"} roqoqo-derive = {version="0.6", path="../roqoqo-derive"} typetag = {version="0.1", optional=true} +nalgebra = "0.29" #sprs = {version="0.10"} rand_distr = {version="0.4", optional=true} @@ -37,7 +38,6 @@ rand = {version="0.8", optional=true} [dev-dependencies] serde_test = {version="1.0"} test-case = "1.1.0" -nalgebra = "0.28" [build-dependencies] quote = "1.0" diff --git a/roqoqo/LICENSE b/roqoqo/LICENSE new file mode 100644 index 00000000..7e63c825 --- /dev/null +++ b/roqoqo/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 HQS Quantum Simulations GmbH + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/roqoqo/build.rs b/roqoqo/build.rs index 105e4c5f..0d04c667 100644 --- a/roqoqo/build.rs +++ b/roqoqo/build.rs @@ -34,6 +34,8 @@ struct Visitor { pragma_operations: Vec, // Identifiers of structs belonging to PragmaNoiseOperation enum pragma_noise_operations: Vec, + // Identifiers of structs belonging to PragmaNoiseProbaOperation enum + pragma_noise_proba_operations: Vec, // Identifiers of structs belonging to GateOperation enum gate_operations: Vec, // Identifiers of structs belonging to Rotation enum @@ -59,6 +61,7 @@ impl Visitor { multi_qubit_operations: Vec::new(), pragma_operations: Vec::new(), pragma_noise_operations: Vec::new(), + pragma_noise_proba_operations: Vec::new(), gate_operations: Vec::new(), rotations: Vec::new(), definitions: Vec::new(), @@ -145,6 +148,13 @@ impl<'ast> Visit<'ast> for Visitor { { self.pragma_noise_operations.push(i.ident.clone()); } + if parsed_arguments.contains("Operate") + && parsed_arguments.contains("OperatePragma") + && parsed_arguments.contains("OperatePragmaNoise") + && parsed_arguments.contains("OperatePragmaNoiseProba") + { + self.pragma_noise_proba_operations.push(i.ident.clone()); + } if parsed_arguments.contains("Operate") && parsed_arguments.contains("OperateGate") { self.gate_operations.push(i.ident.clone()); @@ -219,6 +229,9 @@ impl<'ast> Visit<'ast> for Visitor { if trait_name.as_str() == "OperatePragmaNoise" { self.pragma_noise_operations.push(id.clone()); } + if trait_name.as_str() == "OperatePragmaNoiseProba" { + self.pragma_noise_proba_operations.push(id.clone()); + } if trait_name.as_str() == "OperateMultiQubitGate" { self.two_qubit_gate_operations.push(id); } @@ -286,13 +299,21 @@ fn main() { #v(#v)} }); // Construct TokenStreams for variants of pragma enum - let pragma_noise_operations_quotes = vis.pragma_noise_operations.into_iter().map(|v| { + let pragma_noise_operations_quotes = vis.pragma_noise_operations.clone().into_iter().map(|v| { let msg = format!("Variant for {}", v); quote! { #[doc = #msg] #v(#v)} }); // Construct TokenStreams for variants of pragma enum + let pragma_noise_proba_operations_quotes = + vis.pragma_noise_proba_operations.into_iter().map(|v| { + let msg = format!("Variant for {}", v); + quote! { + #[doc = #msg] + #v(#v)} + }); + // Construct TokenStreams for variants of pragma enum let gate_operations_quotes = vis.gate_operations.into_iter().map(|v| { let msg = format!("Variant for {}", v); quote! { @@ -392,6 +413,13 @@ fn main() { #(#pragma_noise_operations_quotes),* } + /// Enum of all Operations implementing [OperatePragmaNoiseProba] + #[derive(Debug, Clone, PartialEq, InvolveQubits, Operate, OperateTryFromEnum, Substitute, OperatePragma, OperatePragmaNoise, OperatePragmaNoiseProba)] + #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] + pub enum PragmaNoiseProbaOperation { + #(#pragma_noise_proba_operations_quotes),* + } + /// Enum of all Operations implementing [OperateGate] #[derive(Debug, Clone, PartialEq, InvolveQubits, Operate, OperateTryFromEnum, Substitute, OperateGate)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] diff --git a/roqoqo/src/devices.rs b/roqoqo/src/devices.rs new file mode 100644 index 00000000..a768b54c --- /dev/null +++ b/roqoqo/src/devices.rs @@ -0,0 +1,111 @@ +// Copyright © 2021 HQS Quantum Simulations GmbH. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the +// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits defining the standard functions for roqoqo devices. +//! +//! Devices in roqoqo have two use cases: +//! +//! * Abstract devices: Contain abstract information for the model of a quantum computer and its parameters. +//! They can be used to determine which Operations are available on a specific device model. +//! A typical example are abstract linear chains of square lattices in which two-qubit operations are only +//! available between neighbouring qubits. +//! +//! The abstract devices can also encode a noise model. Roqoqo noise models are in general based on a (pseudo) time +//! needed to execute a quantum operation and Lindblad rates for the qubits in the device. +//! Specifically in the noise model each qubit undergoes a continuous Lindblad-type decoherence time evolution: +//! +//! $$ +//! \frac{d}{dt}\rho = \sum_{i,j=0}^{2} M_{i,j} L_{i} \rho L_{j}^{\dagger} - \frac{1}{2} \{ L_{j}^{\dagger} L_i, \rho \} \\\\ +//! L_0 = \sigma^{-} \\\\ +//! L_1 = \sigma^{+} \\\\ +//! L_3 = \sigma^{z} +//! $$ +//! Note that as long as gate times and decoherence rates are scaled inversely any kind of units can be used, +//! but we recommend using nanoseconds and inverse nanosecconds as units for gate times and decoherence rates. +//! +//! +//! * Actual hardware devices: These devices are provided by roqoqo backends and contain the necessary information for +//! accessing the quantum computing hardware. The devices also encode a connectivity model +//! + +use ndarray::Array2; + +/// Trait for roqoqo devices. +/// +/// Defines standard functions available for roqoqo devices. +pub trait Device: Sized { + /// Returns the gate time of a single qubit operation if the single qubit operation is available on device. + /// + /// The base assumption + /// + /// # Arguments + /// + /// * `hqslang` - The hqslang name of a single qubit gate. + /// * `qubit` - The qubit the gate acts on + /// + /// # Returns + /// + /// * `Some` - The gate time. + /// * `None` - The gate is not available on the device. + fn single_qubit_gate_time(&self, hqslang: &str, qubit: usize) -> Option<&f64>; + + /// Returns the gate time of a two qubit operation if the two qubit operation is available on device-. + /// + /// + /// # Arguments + /// + /// * `hqslang` - The hqslang name of a two qubit gate. + /// * `control` - The control qubit the gate acts on + /// * `target` - The target qubit the gate acts on + /// + /// # Returns + /// + /// * `Some` - The gate time. + /// * `None` - The gate is not available on the device. + fn two_qubit_gate_time(&self, hqslang: &str, control: usize, target: usize) -> Option<&f64>; + + /// Returns the gate time of a multi qubit operation if the multi qubit operation is available on device. + /// + /// + /// # Arguments + /// + /// * `hqslang` - The hqslang name of a multi qubit gate. + /// * `qubits` - The qubits the gate acts on + /// + /// # Returns + /// + /// * `Some` - The gate time. + /// * `None` - The gate is not available on the device. + fn multi_qubit_gate_time(&self, hqslang: &str, qubits: &[usize]) -> Option<&f64>; + + /// Returns the matrix of the decoherence rates of the Lindblad equation. + /// + /// $$ + /// \frac{d}{dt}\rho = \sum_{i,j=0}^{2} M_{i,j} L_{i} \rho L_{j}^{\dagger} - \frac{1}{2} \{ L_{j}^{\dagger} L_i, \rho \} \\\\ + /// L_0 = \sigma^{+} \\\\ + /// L_1 = \sigma^{-} \\\\ + /// L_3 = \sigma^{z} + /// $$ + /// + /// # Arguments + /// + /// * `qubit` - The qubit for which the rate matrix M is returned + /// + /// # Returns + /// + /// * `Some<&Array2>` - The decoherence rates. + /// * `None` - The qubit is not part of the device. + fn qubit_decoherence_rates(&self, qubit: usize) -> Option<&Array2>; + + /// Returns the number of qubits the device supports. + fn number_qubits(&self) -> usize; +} diff --git a/roqoqo/src/lib.rs b/roqoqo/src/lib.rs index 5c9a9121..fd58784a 100644 --- a/roqoqo/src/lib.rs +++ b/roqoqo/src/lib.rs @@ -52,7 +52,7 @@ pub enum RoqoqoError { /// Qubit that can not be mapped. qubit: usize, }, - /// Custom error for failed conversion between enums with the TryFrom crate. + /// Custom error for failed conversion between enums with the TryFrom trait. #[error("Conversion from {start_type} to {end_type} failed")] ConversionError { /// Type from which should be converted. @@ -177,5 +177,6 @@ pub mod operations; pub mod prelude; pub use circuit::*; pub mod backends; +pub mod devices; pub mod measurements; pub mod registers; diff --git a/roqoqo/src/operations/mod.rs b/roqoqo/src/operations/mod.rs index b8c1f356..5b4818a7 100644 --- a/roqoqo/src/operations/mod.rs +++ b/roqoqo/src/operations/mod.rs @@ -303,11 +303,7 @@ pub trait OperatePragma: Operate + InvolveQubits + Substitute + Clone + PartialE /// [0.0, 0.0, 0.0, 1.0 - superop_prob], /// ]; /// assert_eq!(superop, pragma.superoperator().unwrap()); -/// // 2) The probability of the noise Pragma -/// let proba_pre_exp: f64 = -2.0 * 0.005 * 0.02; -/// let proba = CalculatorFloat::from(0.5 * (1.0 - proba_pre_exp.exp())); -/// assert_eq!(proba, pragma.probability()); -/// // 3) The power function applied to the noise Pragma +/// // 2) The power function applied to the noise Pragma /// let pragma_test = PragmaDamping::new( /// 0, /// CalculatorFloat::from(0.005 * 1.5), @@ -321,12 +317,33 @@ pub trait OperatePragmaNoise: { /// Returns superoperator matrix of the Operation. fn superoperator(&self) -> Result, RoqoqoError>; - /// Returns the probability of the gate, based on its gate_time and rate. - fn probability(&self) -> CalculatorFloat; /// Returns the gate to the power of `power`. fn powercf(&self, power: CalculatorFloat) -> Self; } +/// Trait for PRAGMA Operations that are not necessary available on all universal quantum hardware, that indicate noise. +/// +/// # Example +/// ``` +/// use ndarray::{array, Array2}; +/// use roqoqo::operations::{OperatePragmaNoiseProba, PragmaDamping}; +/// use qoqo_calculator::CalculatorFloat; +/// +/// let pragma = PragmaDamping::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02)); +/// +/// // The probability of the noise Pragma +/// let proba_pre_exp: f64 = -2.0 * 0.005 * 0.02; +/// let proba = CalculatorFloat::from(0.5 * (1.0 - proba_pre_exp.exp())); +/// assert_eq!(proba, pragma.probability()); +/// ``` +/// +pub trait OperatePragmaNoiseProba: + Operate + InvolveQubits + Substitute + Clone + PartialEq + OperatePragma + OperatePragmaNoise +{ + /// Returns the probability of the gate, based on its gate_time and rate. + fn probability(&self) -> CalculatorFloat; +} + /// Trait for Operations acting with a unitary gate on a set of qubits. /// /// # Example diff --git a/roqoqo/src/operations/pragma_operations.rs b/roqoqo/src/operations/pragma_operations.rs index e61312dd..f7890b44 100644 --- a/roqoqo/src/operations/pragma_operations.rs +++ b/roqoqo/src/operations/pragma_operations.rs @@ -13,7 +13,13 @@ //! Collection of roqoqo PRAGMA operations. //! -use ndarray::{array, Array1, Array2}; +use crate::operations::{ + InvolveQubits, InvolvedQubits, Operate, OperateMultiQubit, OperatePragma, OperatePragmaNoise, + OperatePragmaNoiseProba, OperateSingleQubit, RoqoqoError, Substitute, +}; +use crate::Circuit; +use nalgebra::Matrix4; +use ndarray::{array, Array, Array1, Array2}; use num_complex::Complex64; use qoqo_calculator::{Calculator, CalculatorFloat}; #[cfg(feature = "serialize")] @@ -21,12 +27,6 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::convert::TryFrom; -use crate::operations::{ - InvolveQubits, InvolvedQubits, Operate, OperateMultiQubit, OperatePragma, OperatePragmaNoise, - OperateSingleQubit, RoqoqoError, Substitute, -}; -use crate::Circuit; - /// This PRAGMA Operation sets the number of measurements of the circuit. /// /// This is used for backends that allow setting the number of tries. However, setting the number of @@ -489,11 +489,12 @@ pub struct PragmaDamping { } #[allow(non_upper_case_globals)] -const TAGS_PragmaDamping: &[&str; 5] = &[ +const TAGS_PragmaDamping: &[&str; 6] = &[ "Operation", "SingleQubitOperation", "PragmaOperation", "PragmaNoiseOperation", + "PragmaNoiseProbaOperation", "PragmaDamping", ]; @@ -516,13 +517,6 @@ impl OperatePragmaNoise for PragmaDamping { ]) } - /// Returns the probability of the noise gate affecting the qubit, based on its `gate_time` and `rate`. - fn probability(&self) -> CalculatorFloat { - let prob: CalculatorFloat = - ((self.gate_time.clone() * self.rate.clone() * (-2.0)).exp() * (-1.0) + 1.0) * 0.5; - prob - } - /// Returns the gate to the power of `power`. fn powercf(&self, power: CalculatorFloat) -> Self { let mut new = self.clone(); @@ -531,6 +525,16 @@ impl OperatePragmaNoise for PragmaDamping { } } +/// OperatePragmaNoiseProba trait creating necessary functions for a PRAGMA noise Operation. +impl OperatePragmaNoiseProba for PragmaDamping { + /// Returns the probability of the noise gate affecting the qubit, based on its `gate_time` and `rate`. + fn probability(&self) -> CalculatorFloat { + let prob: CalculatorFloat = + ((self.gate_time.clone() * self.rate.clone() * (-2.0)).exp() * (-1.0) + 1.0) * 0.5; + prob + } +} + /// The depolarising PRAGMA noise Operation. /// /// This PRAGMA Operation applies a depolarising error corresponding to infinite temperature environments. @@ -556,11 +560,12 @@ pub struct PragmaDepolarising { } #[allow(non_upper_case_globals)] -const TAGS_PragmaDepolarising: &[&str; 5] = &[ +const TAGS_PragmaDepolarising: &[&str; 6] = &[ "Operation", "SingleQubitOperation", "PragmaOperation", "PragmaNoiseOperation", + "PragmaNoiseProbaOperation", "PragmaDepolarising", ]; @@ -585,13 +590,6 @@ impl OperatePragmaNoise for PragmaDepolarising { ]) } - /// Returns the probability of the noise gate affecting the qubit, based on its `gate_time` and `rate`. - fn probability(&self) -> CalculatorFloat { - let prob: CalculatorFloat = - ((self.gate_time.clone() * self.rate.clone() * (-1.0)).exp() * (-1.0) + 1.0) * 0.75; - prob - } - /// Returns the gate to the power of `power`. fn powercf(&self, power: CalculatorFloat) -> Self { let mut new = self.clone(); @@ -600,6 +598,16 @@ impl OperatePragmaNoise for PragmaDepolarising { } } +/// OperatePragmaNoiseProba trait creating necessary functions for a PRAGMA noise Operation. +impl OperatePragmaNoiseProba for PragmaDepolarising { + /// Returns the probability of the noise gate affecting the qubit, based on its `gate_time` and `rate`. + fn probability(&self) -> CalculatorFloat { + let prob: CalculatorFloat = + ((self.gate_time.clone() * self.rate.clone() * (-1.0)).exp() * (-1.0) + 1.0) * 0.75; + prob + } +} + /// The dephasing PRAGMA noise Operation. /// /// This PRAGMA Operation applies a pure dephasing error. @@ -625,11 +633,12 @@ pub struct PragmaDephasing { } #[allow(non_upper_case_globals)] -const TAGS_PragmaDephasing: &[&str; 5] = &[ +const TAGS_PragmaDephasing: &[&str; 6] = &[ "Operation", "SingleQubitOperation", "PragmaOperation", "PragmaNoiseOperation", + "PragmaNoiseProbaOperation", "PragmaDephasing", ]; @@ -651,13 +660,6 @@ impl OperatePragmaNoise for PragmaDephasing { ]) } - /// Returns the probability of the noise gate affecting the qubit, based on its `gate_time` and `rate`. - fn probability(&self) -> CalculatorFloat { - let prob: CalculatorFloat = - ((self.gate_time.clone() * self.rate.clone() * (-2.0)).exp() * (-1.0) + 1.0) * 0.5; - prob - } - /// Returns the gate to the power of `power`. fn powercf(&self, power: CalculatorFloat) -> Self { let mut new = self.clone(); @@ -666,6 +668,16 @@ impl OperatePragmaNoise for PragmaDephasing { } } +/// OperatePragmaNoiseProba trait creating necessary functions for a PRAGMA noise Operation. +impl OperatePragmaNoiseProba for PragmaDephasing { + /// Returns the probability of the noise gate affecting the qubit, based on its `gate_time` and `rate`. + fn probability(&self) -> CalculatorFloat { + let prob: CalculatorFloat = + ((self.gate_time.clone() * self.rate.clone() * (-2.0)).exp() * (-1.0) + 1.0) * 0.5; + prob + } +} + /// The random noise PRAGMA operation. /// /// This PRAGMA Operation applies a stochastically unravelled combination of dephasing and depolarising. @@ -693,11 +705,12 @@ pub struct PragmaRandomNoise { } #[allow(non_upper_case_globals)] -const TAGS_PragmaRandomNoise: &[&str; 5] = &[ +const TAGS_PragmaRandomNoise: &[&str; 6] = &[ "Operation", "SingleQubitOperation", "PragmaOperation", "PragmaNoiseOperation", + "PragmaNoiseProbaOperation", "PragmaRandomNoise", ]; @@ -720,6 +733,16 @@ impl OperatePragmaNoise for PragmaRandomNoise { ]) } + /// Returns the gate to the power of `power`. + fn powercf(&self, power: CalculatorFloat) -> Self { + let mut new = self.clone(); + new.gate_time = power * self.gate_time.clone(); + new + } +} + +/// OperatePragmaNoiseProba trait creating necessary functions for a PRAGMA noise Operation. +impl OperatePragmaNoiseProba for PragmaRandomNoise { /// Returns the probability of the noise gate affecting the qubit, based on its `gate_time`, `depolarising_rate` and `dephasing_rate`. fn probability(&self) -> CalculatorFloat { let rates = [ @@ -729,19 +752,12 @@ impl OperatePragmaNoise for PragmaRandomNoise { ]; (rates[0].clone() + &rates[1] + &rates[2]) * &self.gate_time } - - /// Returns the gate to the power of `power`. - fn powercf(&self, power: CalculatorFloat) -> Self { - let mut new = self.clone(); - new.gate_time = power * self.gate_time.clone(); - new - } } /// The general noise PRAGMA operation. /// -/// This PRAGMA Operation applies a noise term according to the given operators. -/// The operators are represented by a 3x3 matrix: +/// This PRAGMA operation applies a noise term according to the given rates. +/// The rates are represented by a 3x3 matrix: /// $$ M = \begin{pmatrix} /// a & b & c \\\\ /// d & e & f \\\\ @@ -749,43 +765,45 @@ impl OperatePragmaNoise for PragmaRandomNoise { /// \end{pmatrix} $$ /// where the coefficients correspond to the following summands /// expanded from the first term of the non-coherent part of the Lindblad equation: -/// $$ -/// a \cdot \sigma_x \rho \sigma_x + b \cdot \sigma_x \rho \sigma_y + c \cdot \sigma_x \rho \sigma_z + d \cdot \sigma_x \rho \sigma_y + e \cdot \sigma_y \rho \sigma_y + f \cdot \sigma_y \rho \sigma_z + g \cdot \sigma_x \rho \sigma_z + h \cdot \sigma_y \rho \sigma_z + j \cdot \sigma_z \rho \sigma_z. -/// $$ +/// $$ \frac{d}{dt}\rho = \sum_{i,j=0}^{2} M_{i,j} L_{i} \rho L_{j}^{\dagger} - \frac{1}{2} \{ L_{j}^{\dagger} L_i, \rho \} \\\\ +/// L_0 = \sigma^{+} \\\\ +/// L_1 = \sigma^{-} \\\\ +/// L_3 = \sigma^{z} +/// $$ +/// +/// Applying the Pragma with a given `gate_time` corresponds to applying the full time-evolution under the Lindblad equation for `gate_time` time. /// /// # Example /// /// ``` /// use ndarray::{array, Array2}; -/// use num_complex::Complex64; /// use roqoqo::operations::PragmaGeneralNoise; /// use qoqo_calculator::CalculatorFloat; /// -/// let operators: Array2 = array![ +/// let rates: Array2 = array![ /// [ -/// Complex64::new(1.0, 0.0), -/// Complex64::new(0.0, 0.0), -/// Complex64::new(0.0, 0.0) +/// 1.0, +/// 0.0, +/// 0.0 /// ], /// [ -/// Complex64::new(0.0, 0.0), -/// Complex64::new(1.0, 0.0), -/// Complex64::new(0.0, 0.0) +/// 0.0, +/// 1.0, +/// 0.0 /// ], /// [ -/// Complex64::new(0.0, 0.0), -/// Complex64::new(0.0, 0.0), -/// Complex64::new(1.0, 0.0) +/// 0.0, +/// 0.0, +/// 1.0 /// ], /// ]; /// let pragma = PragmaGeneralNoise::new( /// 0, /// CalculatorFloat::from(0.005), -/// CalculatorFloat::from(0.02), -/// operators.clone(), +/// rates.clone(), /// ); /// ``` -/// That will result into $\sigma_x \rho \sigma_x + \sigma_y \rho \sigma_y + \sigma_z \rho \sigma_z$. +/// That will result into $. /// #[derive( Debug, @@ -803,20 +821,123 @@ pub struct PragmaGeneralNoise { qubit: usize, /// The time (in seconds) the gate takes to be applied to the qubit on the (simulated) hardware gate_time: CalculatorFloat, - /// The error rate of the noise (in 1/second). - rate: CalculatorFloat, - /// The operators representing the general noise (a 3x3 matrix). - operators: Array2, + /// The rates representing the general noise matrix M (a 3x3 matrix). + rates: Array2, } #[allow(non_upper_case_globals)] -const TAGS_PragmaGeneralNoise: &[&str; 4] = &[ +const TAGS_PragmaGeneralNoise: &[&str; 5] = &[ "Operation", "SingleQubitOperation", "PragmaOperation", + "PragmaNoiseOperation", "PragmaGeneralNoise", ]; +// Collection of superoperators that appear in the Lindblad equation for a single qubit/spin with +// a basis of the form 0: sigma+ 1:sigma- 2: sigmaz +const PGN_SUPEROP: [[[[f64; 4]; 4]; 3]; 3] = [ + [ + // sigma+ sigma+ + [ + [-1., 0., 0., 0.], + [0., -0.5, 0., 0.], + [0., 0., -0.5, 0.], + [1., 0., 0., 0.], + ], + // sigma+ sigma- + [ + [0., 0., 0., 0.], + [0., 0., 0., 0.], + [0., 1., 0., 0.], + [0., 0., 0., 0.], + ], + // sigma+ sigmaz + [ + [0., 0.5, 0., 0.], + [0., 0., 0., 0.], + [1.5, 0., 0., 0.5], + [0., -0.5, 0., 0.], + ], + ], + [ + // sigma- sigma+ + [ + [0., 0., 0., 0.], + [0., 0., 1., 0.], + [0., 0., 0., 0.], + [0., 0., 0., 0.], + ], + // sigma- sigma- + [ + [0., 0., 0., 1.], + [0., -0.5, 0., 0.], + [0., 0., -0.5, 0.], + [0., 0., 0., -1.], + ], + // sigma- sigmaz + [ + [0., 0., 0.5, 0.], + [-0.5, 0., 0., -1.5], + [0., 0., 0., 0.], + [0., 0., -0.5, 0.], + ], + ], + [ + // sigmaz sigma+ + [ + [0., 0., 0.5, 0.], + [1.5, 0., 0., 0.5], + [0., 0., 0., 0.], + [0., 0., -0.5, 0.], + ], + // sigmaz sigma- + [ + [0., 0.5, 0., 0.], + [0., 0., 0., 0.], + [-0.5, 0., 0., -1.5], + [0., -0.5, 0., 0.], + ], + // sigmaz sigmaz + [ + [0., 0., 0., 0.], + [0., -2., 0., 0.], + [0., 0., -2., 0.], + [0., 0., 0., 0.], + ], + ], +]; + +/// OperatePragmaNoise trait creating necessary functions for a PRAGMA noise Operation. +impl OperatePragmaNoise for PragmaGeneralNoise { + fn superoperator(&self) -> Result, RoqoqoError> { + let gate_time: f64 = f64::try_from(self.gate_time.clone())?; + // Creating the superoperator that propagates the density matrix in vector form scaled by rate and time + let mut superop = Matrix4::::default(); + for (i, row) in PGN_SUPEROP.iter().enumerate() { + for (j, op) in row.iter().clone().enumerate() { + let tmp_superop: Matrix4 = (*op).into(); + superop += gate_time * self.rates[(i, j)] * tmp_superop; + } + } + // Integrate superoperator for infinitesimal time to get superoperator for given rate and gate-time + // Use exponential + let exp_superop: Matrix4 = superop.exp(); + let mut tmp_iter = exp_superop.iter(); + // convert to ndarray. + let array: Array2 = Array::from_shape_simple_fn((4, 4), || *tmp_iter.next().unwrap()); + + Ok(array) + } + + /// Returns the gate to the power of `power`. + fn powercf(&self, power: CalculatorFloat) -> Self { + let mut new = self.clone(); + new.gate_time = power * self.gate_time.clone(); + new + } +} + /// The conditional PRAGMA operation. /// /// This PRAGMA executes a circuit when the condition bit/bool stored in a [crate::registers::BitRegister] is true. diff --git a/roqoqo/src/operations/single_qubit_gate_operations.rs b/roqoqo/src/operations/single_qubit_gate_operations.rs index e909864d..9629d513 100644 --- a/roqoqo/src/operations/single_qubit_gate_operations.rs +++ b/roqoqo/src/operations/single_qubit_gate_operations.rs @@ -169,7 +169,7 @@ impl OperateSingleQubitGate for SingleQubitGate { /// -i \sin(\frac{\theta}{2}) & 0\\\\ /// 0 & i \sin(\frac{\theta}{2}) /// \end{pmatrix} -/// $$ +/// $$ /// #[derive( Debug, diff --git a/roqoqo/src/prelude.rs b/roqoqo/src/prelude.rs index 981fd7f2..7ce6d438 100644 --- a/roqoqo/src/prelude.rs +++ b/roqoqo/src/prelude.rs @@ -25,7 +25,7 @@ pub use crate::measurements::{Measure, MeasureExpectationValues}; pub use crate::operations::{ Define, InvolveQubits, InvolvedQubits, Operate, OperateConstantGate, OperateGate, OperateMultiQubit, OperateMultiQubitGate, OperatePragma, OperatePragmaNoise, - OperateSingleQubit, OperateSingleQubitGate, OperateTwoQubit, OperateTwoQubitGate, Rotate, - Substitute, + OperatePragmaNoiseProba, OperateSingleQubit, OperateSingleQubitGate, OperateTwoQubit, + OperateTwoQubitGate, Rotate, Substitute, }; pub use crate::{RoqoqoBackendError, RoqoqoError}; diff --git a/roqoqo/tests/integration/devices.rs b/roqoqo/tests/integration/devices.rs new file mode 100644 index 00000000..c57a9d37 --- /dev/null +++ b/roqoqo/tests/integration/devices.rs @@ -0,0 +1,131 @@ +// Copyright © 2021 HQS Quantum Simulations GmbH. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the +// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing permissions and +// limitations under the License. + +use ndarray::{array, Array2}; +use roqoqo::devices::Device; +use std::collections::HashMap; + +#[derive(Debug, Clone, PartialEq)] +struct TestDevice { + number_qubits: usize, + single_qubit_gates: HashMap>, + two_qubit_gates: HashMap>, + multi_qubit_gates: HashMap, + rates: HashMap>, +} + +impl TestDevice { + pub fn new( + number_qubits: usize, + single_qubit_gates: HashMap>, + two_qubit_gates: HashMap>, + multi_qubit_gates: HashMap, + rates: HashMap>, + ) -> Self { + TestDevice { + number_qubits, + single_qubit_gates, + two_qubit_gates, + multi_qubit_gates, + rates, + } + } +} + +impl Device for TestDevice { + fn number_qubits(&self) -> usize { + self.number_qubits + } + + fn single_qubit_gate_time(&self, hqslang: &str, qubit: usize) -> Option<&f64> { + match self.single_qubit_gates.get(&hqslang.to_string()) { + Some(x) => x.get(&qubit), + None => None, + } + } + + fn two_qubit_gate_time(&self, hqslang: &str, control: usize, target: usize) -> Option<&f64> { + match self.two_qubit_gates.get(&hqslang.to_string()) { + Some(x) => x.get(&(control, target)), + None => None, + } + } + + fn multi_qubit_gate_time(&self, hqslang: &str, _qubits: &[usize]) -> Option<&f64> { + self.multi_qubit_gates.get(&hqslang.to_string()) + } + + fn qubit_decoherence_rates(&self, qubit: usize) -> Option<&Array2> { + self.rates.get(&qubit) + } +} + +/// Basic functional test +#[test] +fn it_works() { + let mut rotate_x_map: HashMap = HashMap::new(); + rotate_x_map.insert(0, 0.1); + rotate_x_map.insert(1, 0.05); + rotate_x_map.insert(2, 0.07); + let mut single_qubit_gates: HashMap> = HashMap::new(); + single_qubit_gates.insert("RotateX".to_string(), rotate_x_map); + + let mut cnot_map: HashMap<(usize, usize), f64> = HashMap::new(); + cnot_map.insert((0, 1), 0.5); + cnot_map.insert((0, 2), 0.4); + cnot_map.insert((1, 2), 0.3); + let mut two_qubit_gates: HashMap> = HashMap::new(); + two_qubit_gates.insert("CNOT".to_string(), cnot_map); + + let mut multi_qubit_gates: HashMap = HashMap::new(); + multi_qubit_gates.insert("MultiQubitMS".to_string(), 0.8); + + let mut rates: HashMap> = HashMap::new(); + rates.insert( + 0, + array![[0.003, 0.0, 0.0], [0.0, 0.0, 00.0], [0.0, 0.0, 0.0]], + ); + rates.insert( + 1, + array![[0.0, 0.0, 0.0], [0.0, 0.002, 0.0], [0.0, 0.0, 0.0]], + ); + rates.insert( + 2, + array![[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.001, 0.0]], + ); + + let device = TestDevice::new( + 3, + single_qubit_gates, + two_qubit_gates, + multi_qubit_gates, + rates, + ); + + let array: Array2 = array![[0.003, 0.0, 0.0], [0.0, 0.0, 00.0], [0.0, 0.0, 0.0]]; + assert_eq!(device.number_qubits(), 3usize); + assert_eq!(device.qubit_decoherence_rates(0), Some(&array)); + + assert_eq!(device.single_qubit_gate_time("RotateX", 0), Some(&0.1f64)); + assert_eq!(device.single_qubit_gate_time("RotateX", 3), None); + assert_eq!(device.single_qubit_gate_time("RotateZ", 0), None); + + assert_eq!(device.two_qubit_gate_time("CNOT", 0, 1), Some(&0.5f64)); + assert_eq!(device.two_qubit_gate_time("CNOT", 0, 3), None); + assert_eq!(device.two_qubit_gate_time("CZ", 0, 1), None); + + assert_eq!( + device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), + Some(&0.8f64) + ); + assert_eq!(device.multi_qubit_gate_time("Other", &[0, 1, 2]), None); +} diff --git a/roqoqo/tests/integration/main.rs b/roqoqo/tests/integration/main.rs index e0f3ba8b..ae9caa15 100644 --- a/roqoqo/tests/integration/main.rs +++ b/roqoqo/tests/integration/main.rs @@ -18,3 +18,6 @@ mod measurements; #[cfg(test)] mod circuit; + +#[cfg(test)] +mod devices; diff --git a/roqoqo/tests/integration/operations/pragma_operations.rs b/roqoqo/tests/integration/operations/pragma_operations.rs index 8139aff0..3309eb96 100644 --- a/roqoqo/tests/integration/operations/pragma_operations.rs +++ b/roqoqo/tests/integration/operations/pragma_operations.rs @@ -1762,11 +1762,12 @@ fn pragma_damping_operate_trait() { let pragma = PragmaDamping::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02)); // (1) Test tags function - let tags: &[&str; 5] = &[ + let tags: &[&str; 6] = &[ "Operation", "SingleQubitOperation", "PragmaOperation", "PragmaNoiseOperation", + "PragmaNoiseProbaOperation", "PragmaDamping", ]; assert_eq!(pragma.tags(), tags); @@ -1809,7 +1810,7 @@ fn pragma_damping_substitute_trait() { assert_eq!(result, Err(RoqoqoError::QubitMappingError { qubit: 1 })); } -/// Test PragmaDamping OperatePragmaNoise trait +/// Test PragmaDamping OperatePragmaNoise and OperatePragmaNoiseProba trait #[test] fn pragma_damping_pragmanoise_trait() { let pragma = PragmaDamping::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02)); @@ -1946,11 +1947,12 @@ fn pragma_depolarising_operate_trait() { PragmaDepolarising::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02)); // (1) Test tags function - let tags: &[&str; 5] = &[ + let tags: &[&str; 6] = &[ "Operation", "SingleQubitOperation", "PragmaOperation", "PragmaNoiseOperation", + "PragmaNoiseProbaOperation", "PragmaDepolarising", ]; assert_eq!(pragma.tags(), tags); @@ -1994,7 +1996,7 @@ fn pragma_depolarising_substitute_trait() { assert_eq!(result, Err(RoqoqoError::QubitMappingError { qubit: 1 })); } -/// Test PragmaDepolarising OperatePragmaNoise trait +/// Test PragmaDepolarising OperatePragmaNoise and OperatePragmaNoiseProba trait #[test] fn pragma_depolarising_pragmanoise_trait() { let pragma = @@ -2131,11 +2133,12 @@ fn pragma_dephasing_operate_trait() { let pragma = PragmaDephasing::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02)); // (1) Test tags function - let tags: &[&str; 5] = &[ + let tags: &[&str; 6] = &[ "Operation", "SingleQubitOperation", "PragmaOperation", "PragmaNoiseOperation", + "PragmaNoiseProbaOperation", "PragmaDephasing", ]; assert_eq!(pragma.tags(), tags); @@ -2178,7 +2181,7 @@ fn pragma_dephasing_substitute_trait() { assert_eq!(result, Err(RoqoqoError::QubitMappingError { qubit: 1 })); } -/// Test PragmaDephasing OperatePragmaNoise trait +/// Test PragmaDephasing OperatePragmaNoise and OperatePragmaNoiseProba trait #[test] fn pragma_dephasing_pragmanoise_trait() { let pragma = PragmaDephasing::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02)); @@ -2336,11 +2339,12 @@ fn pragma_random_noise_operate_trait() { ); // (1) Test tags function - let tags: &[&str; 5] = &[ + let tags: &[&str; 6] = &[ "Operation", "SingleQubitOperation", "PragmaOperation", "PragmaNoiseOperation", + "PragmaNoiseProbaOperation", "PragmaRandomNoise", ]; assert_eq!(pragma.tags(), tags); @@ -2393,7 +2397,7 @@ fn pragma_random_noise_substitute_trait() { assert_eq!(result, Err(RoqoqoError::QubitMappingError { qubit: 1 })); } -/// Test PragmaRandomNoise OperatePragmaNoise trait +/// Test PragmaRandomNoise OperatePragmaNoise and OperatePragmaNoiseProba trait #[test] fn pragma_random_noise_pragmanoise_trait() { let pragma = PragmaRandomNoise::new( @@ -2505,35 +2509,13 @@ fn pragma_random_noise_serde_compact() { /// Test PragmaGeneralNoise inputs and involved qubits #[test] fn pragma_general_noise_inputs_qubits() { - let operators: Array2 = array![ - [ - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0) - ], - ]; - let pragma = PragmaGeneralNoise::new( - 0, - CalculatorFloat::from(0.005), - CalculatorFloat::from(0.02), - operators.clone(), - ); + let operators: Array2 = array![[1.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0],]; + let pragma = PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators.clone()); // Test inputs are correct assert_eq!(pragma.qubit(), &0_usize); assert_eq!(pragma.gate_time(), &CalculatorFloat::from(0.005)); - assert_eq!(pragma.rate(), &CalculatorFloat::from(0.02)); - assert_eq!(pragma.operators(), &operators); + assert_eq!(pragma.rates(), &operators); // Test InvolveQubits trait let mut qubits: HashSet = HashSet::new(); @@ -2544,52 +2526,21 @@ fn pragma_general_noise_inputs_qubits() { /// Test PragmaGeneralNoise standard derived traits (Debug, Clone, PartialEq) #[test] fn pragma_general_noise_simple_traits() { - let operators: Array2 = array![ - [ - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0) - ], - ]; - let pragma = PragmaGeneralNoise::new( - 0, - CalculatorFloat::from(0.005), - CalculatorFloat::from(0.02), - operators.clone(), - ); + let operators: Array2 = array![[1.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 1.0],]; + let pragma = PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators.clone()); // Test Debug trait assert_eq!( format!("{:?}", pragma), - "PragmaGeneralNoise { qubit: 0, gate_time: Float(0.005), rate: Float(0.02), operators: [[Complex { re: 1.0, im: 0.0 }, Complex { re: 0.0, im: 0.0 }, Complex { re: 0.0, im: 0.0 }],\n [Complex { re: 0.0, im: 0.0 }, Complex { re: 1.0, im: 0.0 }, Complex { re: 0.0, im: 0.0 }],\n [Complex { re: 0.0, im: 0.0 }, Complex { re: 0.0, im: 0.0 }, Complex { re: 1.0, im: 0.0 }]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2 }" + "PragmaGeneralNoise { qubit: 0, gate_time: Float(0.005), rates: [[1.0, 0.0, 0.0],\n [0.0, 0.0, 0.0],\n [0.0, 0.0, 1.0]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2 }" ); // Test Clone trait assert_eq!(pragma.clone(), pragma); // Test PartialEq trait - let pragma_0 = PragmaGeneralNoise::new( - 0, - CalculatorFloat::from(0.005), - CalculatorFloat::from(0.02), - operators.clone(), - ); - let pragma_1 = PragmaGeneralNoise::new( - 0, - CalculatorFloat::from(0.006), - CalculatorFloat::from(0.02), - operators.clone(), - ); + let pragma_0 = PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators.clone()); + let pragma_1 = PragmaGeneralNoise::new(0, CalculatorFloat::from(0.006), operators.clone()); assert!(pragma_0 == pragma); assert!(pragma == pragma_0); assert!(pragma_1 != pragma); @@ -2599,35 +2550,15 @@ fn pragma_general_noise_simple_traits() { /// Test PragmaGeneralNoise Operate trait #[test] fn pragma_general_noise_operate_trait() { - let operators: Array2 = array![ - [ - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0) - ], - ]; - let pragma = PragmaGeneralNoise::new( - 0, - CalculatorFloat::from(0.005), - CalculatorFloat::from(0.02), - operators.clone(), - ); + let operators: Array2 = array![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],]; + let pragma = PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators.clone()); // (1) Test tags function - let tags: &[&str; 4] = &[ + let tags: &[&str; 5] = &[ "Operation", "SingleQubitOperation", "PragmaOperation", + "PragmaNoiseOperation", "PragmaGeneralNoise", ]; assert_eq!(pragma.tags(), tags); @@ -2642,37 +2573,11 @@ fn pragma_general_noise_operate_trait() { /// Test PragmaGeneralNoise Substitute trait #[test] fn pragma_general_noise_substitute_trait() { - let operators: Array2 = array![ - [ - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0) - ], - ]; - let pragma = PragmaGeneralNoise::new( - 0, - CalculatorFloat::from(0.005), - CalculatorFloat::from(0.02), - operators.clone(), - ); + let operators: Array2 = array![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],]; + let pragma = PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators.clone()); // (1) Substitute parameters function - let pragma_test = PragmaGeneralNoise::new( - 0, - CalculatorFloat::from("test"), - CalculatorFloat::from(0.02), - operators.clone(), - ); + let pragma_test = PragmaGeneralNoise::new(0, CalculatorFloat::from("test"), operators.clone()); let mut substitution_dict: Calculator = Calculator::new(); substitution_dict.set_variable("test", 0.005); let result = pragma_test @@ -2681,12 +2586,7 @@ fn pragma_general_noise_substitute_trait() { assert_eq!(result, pragma); // (2) Remap qubits function - let pragma_test = PragmaGeneralNoise::new( - 1, - CalculatorFloat::from(0.005), - CalculatorFloat::from(0.02), - operators.clone(), - ); + let pragma_test = PragmaGeneralNoise::new(1, CalculatorFloat::from(0.005), operators.clone()); let mut qubit_mapping_test: HashMap = HashMap::new(); qubit_mapping_test.insert(1, 0); let result = pragma_test.remap_qubits(&qubit_mapping_test).unwrap(); @@ -2701,43 +2601,21 @@ fn pragma_general_noise_substitute_trait() { #[cfg(feature = "serialize")] #[test] fn pragma_general_noise_serde_readable() { - let operators: Array2 = array![ - [ - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0) - ], - ]; - let pragma_serialization = PragmaGeneralNoise::new( - 0, - CalculatorFloat::from(0.005), - CalculatorFloat::from(0.02), - operators.clone(), - ); + let operators: Array2 = array![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],]; + let pragma_serialization = + PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators.clone()); assert_tokens( &pragma_serialization.readable(), &[ Token::Struct { name: "PragmaGeneralNoise", - len: 4, + len: 3, }, Token::Str("qubit"), Token::U64(0), Token::Str("gate_time"), Token::F64(0.005), - Token::Str("rate"), - Token::F64(0.02), - Token::Str("operators"), + Token::Str("rates"), Token::Struct { name: "Array", len: 3, @@ -2751,42 +2629,15 @@ fn pragma_general_noise_serde_readable() { Token::TupleEnd, Token::Str("data"), Token::Seq { len: Some(9) }, - Token::Tuple { len: 2 }, Token::F64(1.0), Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, - Token::F64(0.0), - Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, - Token::F64(0.0), - Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, Token::F64(0.0), Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, Token::F64(1.0), Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, - Token::F64(0.0), - Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, Token::F64(0.0), Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, - Token::F64(0.0), - Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, Token::F64(1.0), - Token::F64(0.0), - Token::TupleEnd, Token::SeqEnd, Token::StructEnd, Token::StructEnd, @@ -2798,35 +2649,15 @@ fn pragma_general_noise_serde_readable() { #[cfg(feature = "serialize")] #[test] fn pragma_general_noise_serde_compact() { - let operators: Array2 = array![ - [ - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0), - Complex64::new(0.0, 0.0) - ], - [ - Complex64::new(0.0, 0.0), - Complex64::new(0.0, 0.0), - Complex64::new(1.0, 0.0) - ], - ]; - let pragma_serialization = PragmaGeneralNoise::new( - 0, - CalculatorFloat::from(0.005), - CalculatorFloat::from(0.02), - operators.clone(), - ); + let operators: Array2 = array![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],]; + let pragma_serialization = + PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators.clone()); assert_tokens( &pragma_serialization.compact(), &[ Token::Struct { name: "PragmaGeneralNoise", - len: 4, + len: 3, }, Token::Str("qubit"), Token::U64(0), @@ -2836,13 +2667,7 @@ fn pragma_general_noise_serde_compact() { variant: "Float", }, Token::F64(0.005), - Token::Str("rate"), - Token::NewtypeVariant { - name: "CalculatorFloat", - variant: "Float", - }, - Token::F64(0.02), - Token::Str("operators"), + Token::Str("rates"), Token::Struct { name: "Array", len: 3, @@ -2856,42 +2681,15 @@ fn pragma_general_noise_serde_compact() { Token::TupleEnd, Token::Str("data"), Token::Seq { len: Some(9) }, - Token::Tuple { len: 2 }, Token::F64(1.0), Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, - Token::F64(0.0), Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, - Token::F64(0.0), - Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, Token::F64(0.0), - Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, Token::F64(1.0), Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, - Token::F64(0.0), - Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, Token::F64(0.0), Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, - Token::F64(0.0), - Token::F64(0.0), - Token::TupleEnd, - Token::Tuple { len: 2 }, Token::F64(1.0), - Token::F64(0.0), - Token::TupleEnd, Token::SeqEnd, Token::StructEnd, Token::StructEnd,