From 3d920fd4638cc1d685a9d293cc17ac7534e6b74f Mon Sep 17 00:00:00 2001 From: Nicolas Vogt Date: Tue, 3 Aug 2021 21:50:11 +0200 Subject: [PATCH 1/4] WIP: testing things --- CHANGELOG.md | 10 + qoqo-macros/LICENSE | 201 ++++++++++++++++++ qoqo/LICENSE | 201 ++++++++++++++++++ .../_auto_generated_operation_conversion.rs | 2 +- qoqo/src/operations/pragma_operations.rs | 10 +- .../operations/operation_conversions.rs | 22 +- .../operations/pragma_operations.rs | 24 +-- roqoqo-derive/LICENSE | 201 ++++++++++++++++++ roqoqo/LICENSE | 201 ++++++++++++++++++ roqoqo/src/devices.rs | 112 ++++++++++ roqoqo/src/lib.rs | 1 + roqoqo/src/operations/pragma_operations.rs | 33 +-- .../operations/pragma_operations.rs | 120 +++++------ 13 files changed, 1033 insertions(+), 105 deletions(-) create mode 100644 qoqo-macros/LICENSE create mode 100644 qoqo/LICENSE create mode 100644 roqoqo-derive/LICENSE create mode 100644 roqoqo/LICENSE create mode 100644 roqoqo/src/devices.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index e42d4dce..452780a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ This changelog track changes to the qoqo project starting at version 0.5.0 +## 0.6 + +### Added + +* Device trait: A minimal trait for quantum computing devices used with roqoqo + +### Changed in roqoqo/qoqo + +* PragmaGeneralNoise uses sigma^+ sigma^- and sigma^z as a basis to for Lindblad decoherence rates to avoid using complex rates. + ## 0.5.1 ### Fixed in roqoqo 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/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/_auto_generated_operation_conversion.rs b/qoqo/src/operations/_auto_generated_operation_conversion.rs index 1c766faa..e4c23113 100644 --- a/qoqo/src/operations/_auto_generated_operation_conversion.rs +++ b/qoqo/src/operations/_auto_generated_operation_conversion.rs @@ -1465,7 +1465,7 @@ pub fn convert_pyany_to_operation(op: &PyAny) -> Result { let array = op .call_method0("operators") .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(); diff --git a/qoqo/src/operations/pragma_operations.rs b/qoqo/src/operations/pragma_operations.rs index 59a8f3dc..035d1d5b 100644 --- a/qoqo/src/operations/pragma_operations.rs +++ b/qoqo/src/operations/pragma_operations.rs @@ -874,7 +874,7 @@ insert_pyany_to_operation!( let array = op.call_method0("operators") .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(); @@ -911,7 +911,7 @@ impl PragmaGeneralNoiseWrapper { rate: Py, operators: Py, ) -> PyResult { - let operators_casted: Vec = Python::with_gil(|py| -> Vec { + 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(); @@ -966,13 +966,13 @@ impl PragmaGeneralNoiseWrapper { /// /// Returns: /// np.ndarray: The operators of the PRAGMA operation. - fn operators(&self) -> Py> { - Python::with_gil(|py| -> Py> { + fn operators(&self) -> Py> { + Python::with_gil(|py| -> Py> { self.internal .operators() .iter() .cloned() - .collect::>() + .collect::>() .to_pyarray(py) .to_owned() }) diff --git a/qoqo/tests/integration/operations/operation_conversions.rs b/qoqo/tests/integration/operations/operation_conversions.rs index 5ca990d3..0b67bb62 100644 --- a/qoqo/tests/integration/operations/operation_conversions.rs +++ b/qoqo/tests/integration/operations/operation_conversions.rs @@ -136,22 +136,22 @@ fn densitymatrix() -> Array2 { densitymatrix } -fn operators() -> Array2 { - let operators: Array2 = array![ +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) + 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 ], ]; operators diff --git a/qoqo/tests/integration/operations/pragma_operations.rs b/qoqo/tests/integration/operations/pragma_operations.rs index 0f35a75a..47caf33a 100644 --- a/qoqo/tests/integration/operations/pragma_operations.rs +++ b/qoqo/tests/integration/operations/pragma_operations.rs @@ -85,22 +85,22 @@ fn densitymatrix() -> Array2 { densitymatrix } -fn operators() -> Array2 { - let operators: Array2 = array![ +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) + 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 ], ]; operators @@ -498,7 +498,7 @@ fn test_pyo3_inputs_generalnoise() { &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 = + let to_operators_op: Vec = Vec::extract(operation.call_method0(py, "operators").unwrap().as_ref(py)).unwrap(); let operators_op = Array::from_shape_vec((3, 3), to_operators_op).unwrap(); assert_eq!(operators_op, operators()); 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/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/src/devices.rs b/roqoqo/src/devices.rs new file mode 100644 index 00000000..925fdae6 --- /dev/null +++ b/roqoqo/src/devices.rs @@ -0,0 +1,112 @@ +// 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; + + + /// 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 single 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; + + /// Returns the gate time of a multi qubit operation if the mulit qubit operation is available on device. + /// + /// + /// # Arguments + /// + /// * `hqslang` - The hqslang name of a single 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; + + /// 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>` - The decoherence rates. + /// * `None` - The qubit is not part of the device. + fn qubit_decohernce_rates(&self, qubits: &[usize]) -> Option>; + + + /// Returns the number of qubits the device supports. + fn number_qubits(&self) -> usize; +} \ No newline at end of file diff --git a/roqoqo/src/lib.rs b/roqoqo/src/lib.rs index e88925c8..49a3c210 100644 --- a/roqoqo/src/lib.rs +++ b/roqoqo/src/lib.rs @@ -173,3 +173,4 @@ pub use circuit::*; pub mod backends; pub mod measurements; pub mod registers; +pub mod devices; \ No newline at end of file diff --git a/roqoqo/src/operations/pragma_operations.rs b/roqoqo/src/operations/pragma_operations.rs index e61312dd..7391e692 100644 --- a/roqoqo/src/operations/pragma_operations.rs +++ b/roqoqo/src/operations/pragma_operations.rs @@ -749,33 +749,34 @@ 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} +/// $$ /// /// # Example /// /// ``` /// use ndarray::{array, Array2}; -/// use num_complex::Complex64; /// use roqoqo::operations::PragmaGeneralNoise; /// use qoqo_calculator::CalculatorFloat; /// -/// let operators: Array2 = array![ +/// let operators: 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( @@ -785,7 +786,7 @@ impl OperatePragmaNoise for PragmaRandomNoise { /// operators.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, @@ -806,7 +807,7 @@ pub struct PragmaGeneralNoise { /// The error rate of the noise (in 1/second). rate: CalculatorFloat, /// The operators representing the general noise (a 3x3 matrix). - operators: Array2, + operators: Array2, } #[allow(non_upper_case_globals)] diff --git a/roqoqo/tests/integration/operations/pragma_operations.rs b/roqoqo/tests/integration/operations/pragma_operations.rs index 8139aff0..7a87a256 100644 --- a/roqoqo/tests/integration/operations/pragma_operations.rs +++ b/roqoqo/tests/integration/operations/pragma_operations.rs @@ -2505,21 +2505,21 @@ fn pragma_random_noise_serde_compact() { /// Test PragmaGeneralNoise inputs and involved qubits #[test] fn pragma_general_noise_inputs_qubits() { - let operators: Array2 = array![ + let operators: 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, + 0.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, + 0.0 ], ]; let pragma = PragmaGeneralNoise::new( @@ -2544,21 +2544,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![ + let operators: 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, + 0.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( @@ -2599,21 +2599,21 @@ fn pragma_general_noise_simple_traits() { /// Test PragmaGeneralNoise Operate trait #[test] fn pragma_general_noise_operate_trait() { - let operators: Array2 = array![ + let operators: 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( @@ -2642,21 +2642,21 @@ fn pragma_general_noise_operate_trait() { /// Test PragmaGeneralNoise Substitute trait #[test] fn pragma_general_noise_substitute_trait() { - let operators: Array2 = array![ + let operators: 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( @@ -2701,21 +2701,21 @@ fn pragma_general_noise_substitute_trait() { #[cfg(feature = "serialize")] #[test] fn pragma_general_noise_serde_readable() { - let operators: Array2 = array![ + let operators: 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_serialization = PragmaGeneralNoise::new( @@ -2798,21 +2798,21 @@ fn pragma_general_noise_serde_readable() { #[cfg(feature = "serialize")] #[test] fn pragma_general_noise_serde_compact() { - let operators: Array2 = array![ + let operators: 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_serialization = PragmaGeneralNoise::new( From 27f349dc786c929189c9bf9ca2c6b38f675688e0 Mon Sep 17 00:00:00 2001 From: Nicolas Vogt Date: Wed, 1 Sep 2021 16:14:32 +0200 Subject: [PATCH 2/4] Fixed new convention for PragmaGeneralNoise --- CHANGELOG.md | 2 +- Cargo.lock | 106 ++++++---- qoqo/Cargo.toml | 2 +- qoqo/src/operations/pragma_operations.rs | 89 ++++---- .../operations/operation_conversions.rs | 20 +- .../operations/pragma_operations.rs | 64 ++---- roqoqo-test/Cargo.toml | 2 +- roqoqo/Cargo.toml | 2 +- roqoqo/src/devices.rs | 17 +- roqoqo/src/lib.rs | 4 +- roqoqo/src/operations/pragma_operations.rs | 143 +++++++++++-- .../operations/pragma_operations.rs | 193 ++---------------- 12 files changed, 288 insertions(+), 356 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2da0edf7..dbd738d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This changelog track changes to the qoqo project starting at version 0.5.0 ### Changed * 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. +* 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. ### Added 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/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/src/operations/pragma_operations.rs b/qoqo/src/operations/pragma_operations.rs index 035d1d5b..c301e30d 100644 --- a/qoqo/src/operations/pragma_operations.rs +++ b/qoqo/src/operations/pragma_operations.rs @@ -864,21 +864,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 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,11 +889,31 @@ 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. @@ -908,13 +921,11 @@ impl PragmaGeneralNoiseWrapper { fn new( qubit: usize, gate_time: Py, - rate: Py, - operators: Py, + rates: 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(); + 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 +933,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,24 +957,14 @@ 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> { + /// np.ndarray: The rates of the PRAGMA operation. + fn rates(&self) -> Py> { Python::with_gil(|py| -> Py> { self.internal - .operators() + .rates() .iter() .cloned() .collect::>() @@ -978,6 +973,20 @@ impl PragmaGeneralNoiseWrapper { }) } + /// 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 fdf8bbf6..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(); @@ -137,23 +137,7 @@ fn densitymatrix() -> Array2 { } 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 - ], - ]; + 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 47caf33a..0fb5ba9d 100644 --- a/qoqo/tests/integration/operations/pragma_operations.rs +++ b/qoqo/tests/integration/operations/pragma_operations.rs @@ -86,23 +86,7 @@ fn densitymatrix() -> Array2 { } 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 - ], - ]; + 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(); + 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,7 @@ 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(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_tags_single(input_measurement: Operation, tag_name: &str) { pyo3::prepare_freethreaded_python(); @@ -882,7 +861,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 +900,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 +977,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 +1058,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 +1132,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 +1438,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 +2368,17 @@ 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") + .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 +2403,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 +2415,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 +2473,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-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/src/devices.rs b/roqoqo/src/devices.rs index 925fdae6..fa1bb865 100644 --- a/roqoqo/src/devices.rs +++ b/roqoqo/src/devices.rs @@ -33,10 +33,9 @@ //! //! //! * 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 +//! accessing the quantum computing hardware. The devices also encode a connectivity model //! - use ndarray::Array2; /// Trait for roqoqo devices. @@ -45,7 +44,7 @@ use ndarray::Array2; 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 + /// The base assumption /// /// # Arguments /// @@ -58,7 +57,6 @@ pub trait Device: Sized { /// * `None` - The gate is not available on the device. fn single_qubit_gate_time(&self, hqslang: &str, qubit: usize) -> Option; - /// Returns the gate time of a two qubit operation if the two qubit operation is available on device-. /// /// @@ -91,8 +89,8 @@ pub trait Device: Sized { /// 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_0 = \sigma^{+} \\\\ + /// L_1 = \sigma^{-} \\\\ /// L_3 = \sigma^{z} /// $$ /// @@ -102,11 +100,10 @@ pub trait Device: Sized { /// /// # Returns /// - /// * `Some>` - The decoherence rates. + /// * `Some<&Array2>` - The decoherence rates. /// * `None` - The qubit is not part of the device. - fn qubit_decohernce_rates(&self, qubits: &[usize]) -> Option>; - + fn qubit_decohernce_rates(&self, qubits: &[usize]) -> Option<&Array2>; /// Returns the number of qubits the device supports. fn number_qubits(&self) -> usize; -} \ No newline at end of file +} diff --git a/roqoqo/src/lib.rs b/roqoqo/src/lib.rs index 84f35962..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,6 +177,6 @@ pub mod operations; pub mod prelude; pub use circuit::*; pub mod backends; +pub mod devices; pub mod measurements; pub mod registers; -pub mod devices; \ No newline at end of file diff --git a/roqoqo/src/operations/pragma_operations.rs b/roqoqo/src/operations/pragma_operations.rs index 7391e692..e05c8cff 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, + OperateSingleQubit, RoqoqoError, Substitute, +}; +use crate::Circuit; +use nalgebra::Matrix4; +use ndarray::{array, Array1, Array2, Array}; 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 @@ -740,8 +740,8 @@ impl OperatePragmaNoise for PragmaRandomNoise { /// 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 \\\\ @@ -750,11 +750,13 @@ impl OperatePragmaNoise for PragmaRandomNoise { /// where the coefficients correspond to the following summands /// expanded from the first term of the non-coherent part 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_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 /// /// ``` @@ -762,7 +764,7 @@ impl OperatePragmaNoise for PragmaRandomNoise { /// use roqoqo::operations::PragmaGeneralNoise; /// use qoqo_calculator::CalculatorFloat; /// -/// let operators: Array2 = array![ +/// let rates: Array2 = array![ /// [ /// 1.0, /// 0.0, @@ -782,8 +784,7 @@ impl OperatePragmaNoise for PragmaRandomNoise { /// let pragma = PragmaGeneralNoise::new( /// 0, /// CalculatorFloat::from(0.005), -/// CalculatorFloat::from(0.02), -/// operators.clone(), +/// rates.clone(), /// ); /// ``` /// That will result into $. @@ -804,10 +805,8 @@ 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)] @@ -818,6 +817,114 @@ const TAGS_PragmaGeneralNoise: &[&str; 4] = &[ "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) + } + + fn probability(&self) -> CalculatorFloat { + CalculatorFloat::ZERO + } + + /// 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/tests/integration/operations/pragma_operations.rs b/roqoqo/tests/integration/operations/pragma_operations.rs index 7a87a256..1389c5c8 100644 --- a/roqoqo/tests/integration/operations/pragma_operations.rs +++ b/roqoqo/tests/integration/operations/pragma_operations.rs @@ -2505,35 +2505,17 @@ fn pragma_random_noise_serde_compact() { /// Test PragmaGeneralNoise inputs and involved qubits #[test] fn pragma_general_noise_inputs_qubits() { - let operators: Array2 = array![ - [ - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - ]; + 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), - CalculatorFloat::from(0.02), 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,34 +2526,17 @@ 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![ - [ - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ], - ]; + 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), - CalculatorFloat::from(0.02), 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 @@ -2581,13 +2546,11 @@ fn pragma_general_noise_simple_traits() { 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(), ); assert!(pragma_0 == pragma); @@ -2599,27 +2562,10 @@ fn pragma_general_noise_simple_traits() { /// Test PragmaGeneralNoise Operate trait #[test] fn pragma_general_noise_operate_trait() { - let operators: Array2 = array![ - [ - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ], - ]; + 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), - CalculatorFloat::from(0.02), operators.clone(), ); @@ -2642,27 +2588,10 @@ fn pragma_general_noise_operate_trait() { /// Test PragmaGeneralNoise Substitute trait #[test] fn pragma_general_noise_substitute_trait() { - let operators: Array2 = array![ - [ - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ], - ]; + 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), - CalculatorFloat::from(0.02), operators.clone(), ); @@ -2670,7 +2599,6 @@ fn pragma_general_noise_substitute_trait() { let pragma_test = PragmaGeneralNoise::new( 0, CalculatorFloat::from("test"), - CalculatorFloat::from(0.02), operators.clone(), ); let mut substitution_dict: Calculator = Calculator::new(); @@ -2684,7 +2612,6 @@ fn pragma_general_noise_substitute_trait() { let pragma_test = PragmaGeneralNoise::new( 1, CalculatorFloat::from(0.005), - CalculatorFloat::from(0.02), operators.clone(), ); let mut qubit_mapping_test: HashMap = HashMap::new(); @@ -2701,27 +2628,10 @@ fn pragma_general_noise_substitute_trait() { #[cfg(feature = "serialize")] #[test] fn pragma_general_noise_serde_readable() { - let operators: Array2 = array![ - [ - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ], - ]; + 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), - CalculatorFloat::from(0.02), operators.clone(), ); assert_tokens( @@ -2729,15 +2639,13 @@ fn pragma_general_noise_serde_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 +2659,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,27 +2679,10 @@ fn pragma_general_noise_serde_readable() { #[cfg(feature = "serialize")] #[test] fn pragma_general_noise_serde_compact() { - let operators: Array2 = array![ - [ - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ], - ]; + 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), - CalculatorFloat::from(0.02), operators.clone(), ); assert_tokens( @@ -2826,7 +2690,7 @@ fn pragma_general_noise_serde_compact() { &[ Token::Struct { name: "PragmaGeneralNoise", - len: 4, + len: 3, }, Token::Str("qubit"), Token::U64(0), @@ -2836,13 +2700,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 +2714,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, From a480db1a528b61f75397601596e1e77e6e767256 Mon Sep 17 00:00:00 2001 From: Kirsten Bark Date: Thu, 2 Sep 2021 19:21:03 +0200 Subject: [PATCH 3/4] Added devices unittests and separated OperatePragmaNoise into OperatePragmaNoise and OperatePragmaNoiseProba --- qoqo-macros/src/lib.rs | 22 ++- qoqo/src/operations/pragma_operations.rs | 49 ++++-- .../operations/pragma_operations.rs | 25 ++- roqoqo-derive/src/lib.rs | 9 ++ roqoqo-derive/src/operate_unitary.rs | 36 +++-- roqoqo/build.rs | 30 +++- roqoqo/src/devices.rs | 28 ++-- roqoqo/src/operations/mod.rs | 31 +++- roqoqo/src/operations/pragma_operations.rs | 95 ++++++----- .../single_qubit_gate_operations.rs | 2 +- roqoqo/src/prelude.rs | 4 +- roqoqo/tests/integration/devices.rs | 148 ++++++++++++++++++ roqoqo/tests/integration/main.rs | 3 + .../operations/pragma_operations.rs | 85 +++------- 14 files changed, 408 insertions(+), 159 deletions(-) create mode 100644 roqoqo/tests/integration/devices.rs 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/src/operations/pragma_operations.rs b/qoqo/src/operations/pragma_operations.rs index c301e30d..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. @@ -918,11 +942,7 @@ impl PragmaGeneralNoiseWrapper { /// Returns: /// self: The new PragmaGeneralNoise. #[new] - fn new( - qubit: usize, - gate_time: Py, - rates: Py, - ) -> PyResult { + 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(); @@ -933,7 +953,7 @@ impl PragmaGeneralNoiseWrapper { ) }) })?; - + Ok(Self { internal: PragmaGeneralNoise::new(qubit, gate_time_cf, rates_array), }) @@ -979,11 +999,10 @@ impl PragmaGeneralNoiseWrapper { /// 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))) - } + match self.internal.superoperator() { + Ok(x) => Ok(x.to_pyarray(py).to_owned()), + Err(err) => Err(PyRuntimeError::new_err(format!("{:?}", err))), + } }) } diff --git a/qoqo/tests/integration/operations/pragma_operations.rs b/qoqo/tests/integration/operations/pragma_operations.rs index 0fb5ba9d..72944585 100644 --- a/qoqo/tests/integration/operations/pragma_operations.rs +++ b/qoqo/tests/integration/operations/pragma_operations.rs @@ -805,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), 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(); @@ -823,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")] @@ -840,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); @@ -2373,9 +2392,7 @@ fn test_pyo3_new_general_noise() { let convert_to_get_operators = convert_operation_to_pyobject(to_get_operators) .unwrap() .clone(); - let operators_op = convert_to_get_operators - .call_method0(py, "rates") - .unwrap(); + let operators_op = convert_to_get_operators.call_method0(py, "rates").unwrap(); let new_op = operation .call1((0, 0.005, operators_op.clone())) 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..c7a2b3c5 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.clone().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/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 index fa1bb865..84d09062 100644 --- a/roqoqo/src/devices.rs +++ b/roqoqo/src/devices.rs @@ -23,7 +23,8 @@ //! 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 \} \\\\ +//! $$ +//! \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} @@ -55,14 +56,14 @@ pub trait Device: Sized { /// /// * `Some` - The gate time. /// * `None` - The gate is not available on the device. - fn single_qubit_gate_time(&self, hqslang: &str, qubit: usize) -> Option; + 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 single qubit gate. + /// * `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 /// @@ -70,29 +71,30 @@ pub trait Device: Sized { /// /// * `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; + 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 mulit qubit operation is available on device. + /// 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 single qubit gate. + /// * `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; + 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} - /// $$ + /// $$ + /// \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 /// @@ -102,7 +104,7 @@ pub trait Device: Sized { /// /// * `Some<&Array2>` - The decoherence rates. /// * `None` - The qubit is not part of the device. - fn qubit_decohernce_rates(&self, qubits: &[usize]) -> Option<&Array2>; + fn qubit_decoherence_rates(&self, qubits: &[usize]) -> Option>; // ask /// Returns the number of qubits the device supports. fn number_qubits(&self) -> usize; 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 e05c8cff..f7890b44 100644 --- a/roqoqo/src/operations/pragma_operations.rs +++ b/roqoqo/src/operations/pragma_operations.rs @@ -15,11 +15,11 @@ use crate::operations::{ InvolveQubits, InvolvedQubits, Operate, OperateMultiQubit, OperatePragma, OperatePragmaNoise, - OperateSingleQubit, RoqoqoError, Substitute, + OperatePragmaNoiseProba, OperateSingleQubit, RoqoqoError, Substitute, }; use crate::Circuit; use nalgebra::Matrix4; -use ndarray::{array, Array1, Array2, Array}; +use ndarray::{array, Array, Array1, Array2}; use num_complex::Complex64; use qoqo_calculator::{Calculator, CalculatorFloat}; #[cfg(feature = "serialize")] @@ -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,13 +752,6 @@ 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. @@ -810,10 +826,11 @@ pub struct PragmaGeneralNoise { } #[allow(non_upper_case_globals)] -const TAGS_PragmaGeneralNoise: &[&str; 4] = &[ +const TAGS_PragmaGeneralNoise: &[&str; 5] = &[ "Operation", "SingleQubitOperation", "PragmaOperation", + "PragmaNoiseOperation", "PragmaGeneralNoise", ]; @@ -900,7 +917,7 @@ impl OperatePragmaNoise for PragmaGeneralNoise { 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; + superop += gate_time * self.rates[(i, j)] * tmp_superop; } } // Integrate superoperator for infinitesimal time to get superoperator for given rate and gate-time @@ -908,15 +925,11 @@ impl OperatePragmaNoise for PragmaGeneralNoise { 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()); + let array: Array2 = Array::from_shape_simple_fn((4, 4), || *tmp_iter.next().unwrap()); Ok(array) } - fn probability(&self) -> CalculatorFloat { - CalculatorFloat::ZERO - } - /// Returns the gate to the power of `power`. fn powercf(&self, power: CalculatorFloat) -> Self { let mut new = self.clone(); 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..49b84d87 --- /dev/null +++ b/roqoqo/tests/integration/devices.rs @@ -0,0 +1,148 @@ +// 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>, + dephasing_rates: HashMap, + depolarising_rates: HashMap, + damping_rates: HashMap, +} + +impl TestDevice { + pub fn new( + number_qubits: usize, + single_qubit_gates: HashMap>, + two_qubit_gates: HashMap>, + multi_qubit_gates: HashMap>, + dephasing_rates: HashMap, + depolarising_rates: HashMap, + damping_rates: HashMap, + ) -> Self { + TestDevice { + number_qubits, + single_qubit_gates, + two_qubit_gates, + multi_qubit_gates, + dephasing_rates, + depolarising_rates, + damping_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> { + match self.multi_qubit_gates.get(&hqslang.to_string()) { + Some(x) => x.get(&(qubits[0], qubits[1], qubits[2])), // ask, + None => None, + } + } + + fn qubit_decoherence_rates(&self, qubits: &[usize]) -> Option> { + let default: Array2 = array![[qubits[0] as f64]]; + Some(default) + } +} + +/// 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_ms_map: HashMap<(usize, usize, usize), f64> = HashMap::new(); + multi_ms_map.insert((0, 1, 2), 0.8); + let mut multi_qubit_gates: HashMap> = + HashMap::new(); + multi_qubit_gates.insert("MultiQubitMS".to_string(), multi_ms_map); + + let mut deph_map: HashMap = HashMap::new(); + deph_map.insert(0, 0.003); + deph_map.insert(1, 0.002); + deph_map.insert(2, 0.001); + let mut depol_map: HashMap = HashMap::new(); + depol_map.insert(0, 0.009); + depol_map.insert(1, 0.008); + depol_map.insert(2, 0.007); + let mut damp_map: HashMap = HashMap::new(); + damp_map.insert(0, 0.006); + damp_map.insert(1, 0.005); + damp_map.insert(2, 0.004); + + let device = TestDevice::new( + 3, + single_qubit_gates, + two_qubit_gates, + multi_qubit_gates, + deph_map, + depol_map, + damp_map, + ); + + assert_eq!(device.number_qubits(), 3usize); + assert_eq!(device.qubit_decoherence_rates(&[0]), Some(array![[0.0]])); + + 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("MultiQubitMS", &[0, 1, 3]), + None + ); + 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 1389c5c8..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( @@ -2506,11 +2510,7 @@ fn pragma_random_noise_serde_compact() { #[test] fn pragma_general_noise_inputs_qubits() { 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(), - ); + let pragma = PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators.clone()); // Test inputs are correct assert_eq!(pragma.qubit(), &0_usize); @@ -2527,11 +2527,7 @@ fn pragma_general_noise_inputs_qubits() { #[test] fn pragma_general_noise_simple_traits() { 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(), - ); + let pragma = PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators.clone()); // Test Debug trait assert_eq!( @@ -2543,16 +2539,8 @@ fn pragma_general_noise_simple_traits() { assert_eq!(pragma.clone(), pragma); // Test PartialEq trait - let pragma_0 = PragmaGeneralNoise::new( - 0, - CalculatorFloat::from(0.005), - operators.clone(), - ); - let pragma_1 = PragmaGeneralNoise::new( - 0, - CalculatorFloat::from(0.006), - 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); @@ -2563,17 +2551,14 @@ fn pragma_general_noise_simple_traits() { #[test] fn pragma_general_noise_operate_trait() { 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(), - ); + 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); @@ -2589,18 +2574,10 @@ fn pragma_general_noise_operate_trait() { #[test] fn pragma_general_noise_substitute_trait() { 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(), - ); + let pragma = PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators.clone()); // (1) Substitute parameters function - let pragma_test = PragmaGeneralNoise::new( - 0, - CalculatorFloat::from("test"), - 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 @@ -2609,11 +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), - 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(); @@ -2629,11 +2602,8 @@ fn pragma_general_noise_substitute_trait() { #[test] fn pragma_general_noise_serde_readable() { 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(), - ); + let pragma_serialization = + PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators.clone()); assert_tokens( &pragma_serialization.readable(), &[ @@ -2680,11 +2650,8 @@ fn pragma_general_noise_serde_readable() { #[test] fn pragma_general_noise_serde_compact() { 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(), - ); + let pragma_serialization = + PragmaGeneralNoise::new(0, CalculatorFloat::from(0.005), operators.clone()); assert_tokens( &pragma_serialization.compact(), &[ From f8cbdb05fe62dd2910abee6a99f6061112eae54f Mon Sep 17 00:00:00 2001 From: Kirsten Bark Date: Fri, 3 Sep 2021 12:54:25 +0200 Subject: [PATCH 4/4] Fixed unittest for devices and updated changelog --- CHANGELOG.md | 9 ++-- roqoqo-derive/src/operate_unitary.rs | 2 +- roqoqo/src/devices.rs | 2 +- roqoqo/tests/integration/devices.rs | 73 +++++++++++----------------- 4 files changed, 35 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbd738d1..ae1c287e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +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 -* 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.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/roqoqo-derive/src/operate_unitary.rs b/roqoqo-derive/src/operate_unitary.rs index c7a2b3c5..92797c69 100644 --- a/roqoqo-derive/src/operate_unitary.rs +++ b/roqoqo-derive/src/operate_unitary.rs @@ -476,7 +476,7 @@ pub fn dispatch_struct_enum_operate_noise_proba_pragma(input: DeriveInput) -> To 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.clone().map(|(vident, _, _)| { + let match_proba_quotes = variants_with_type.map(|(vident, _, _)| { quote! { &#ident::#vident(ref inner) => inner.probability(), } diff --git a/roqoqo/src/devices.rs b/roqoqo/src/devices.rs index 84d09062..a768b54c 100644 --- a/roqoqo/src/devices.rs +++ b/roqoqo/src/devices.rs @@ -104,7 +104,7 @@ pub trait Device: Sized { /// /// * `Some<&Array2>` - The decoherence rates. /// * `None` - The qubit is not part of the device. - fn qubit_decoherence_rates(&self, qubits: &[usize]) -> Option>; // ask + 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/tests/integration/devices.rs b/roqoqo/tests/integration/devices.rs index 49b84d87..c57a9d37 100644 --- a/roqoqo/tests/integration/devices.rs +++ b/roqoqo/tests/integration/devices.rs @@ -19,10 +19,8 @@ struct TestDevice { number_qubits: usize, single_qubit_gates: HashMap>, two_qubit_gates: HashMap>, - multi_qubit_gates: HashMap>, - dephasing_rates: HashMap, - depolarising_rates: HashMap, - damping_rates: HashMap, + multi_qubit_gates: HashMap, + rates: HashMap>, } impl TestDevice { @@ -30,19 +28,15 @@ impl TestDevice { number_qubits: usize, single_qubit_gates: HashMap>, two_qubit_gates: HashMap>, - multi_qubit_gates: HashMap>, - dephasing_rates: HashMap, - depolarising_rates: HashMap, - damping_rates: HashMap, + multi_qubit_gates: HashMap, + rates: HashMap>, ) -> Self { TestDevice { number_qubits, single_qubit_gates, two_qubit_gates, multi_qubit_gates, - dephasing_rates, - depolarising_rates, - damping_rates, + rates, } } } @@ -66,16 +60,12 @@ impl Device for TestDevice { } } - fn multi_qubit_gate_time(&self, hqslang: &str, qubits: &[usize]) -> Option<&f64> { - match self.multi_qubit_gates.get(&hqslang.to_string()) { - Some(x) => x.get(&(qubits[0], qubits[1], qubits[2])), // ask, - 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, qubits: &[usize]) -> Option> { - let default: Array2 = array![[qubits[0] as f64]]; - Some(default) + fn qubit_decoherence_rates(&self, qubit: usize) -> Option<&Array2> { + self.rates.get(&qubit) } } @@ -96,37 +86,34 @@ fn it_works() { let mut two_qubit_gates: HashMap> = HashMap::new(); two_qubit_gates.insert("CNOT".to_string(), cnot_map); - let mut multi_ms_map: HashMap<(usize, usize, usize), f64> = HashMap::new(); - multi_ms_map.insert((0, 1, 2), 0.8); - let mut multi_qubit_gates: HashMap> = - HashMap::new(); - multi_qubit_gates.insert("MultiQubitMS".to_string(), multi_ms_map); - - let mut deph_map: HashMap = HashMap::new(); - deph_map.insert(0, 0.003); - deph_map.insert(1, 0.002); - deph_map.insert(2, 0.001); - let mut depol_map: HashMap = HashMap::new(); - depol_map.insert(0, 0.009); - depol_map.insert(1, 0.008); - depol_map.insert(2, 0.007); - let mut damp_map: HashMap = HashMap::new(); - damp_map.insert(0, 0.006); - damp_map.insert(1, 0.005); - damp_map.insert(2, 0.004); + 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, - deph_map, - depol_map, - damp_map, + 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![[0.0]])); + 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); @@ -140,9 +127,5 @@ fn it_works() { device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), Some(&0.8f64) ); - assert_eq!( - device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 3]), - None - ); assert_eq!(device.multi_qubit_gate_time("Other", &[0, 1, 2]), None); }