diff --git a/CHANGELOG.md b/CHANGELOG.md index df4897ef..94635aa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,29 +1,36 @@ # Changelog -This changelog track changes to the qoqo project starting at version 0.5.0 +This changelog track changes to the qoqo project starting at version v0.5.0 + +## v1.0.0-alpha.1 + +* Device trait added in roqoqo. +* Version updated to 1.0.0-alpha.1 ## Release 1.0.0-alpha +prerelease package: documentation not yet complete and new functionalities might be added. + * Removed user access to devices to avoid breaking changes in version 1. * Updated dependencies and README. * Added unit tests for serialization of PragmaRepeatedMeasurement operations in a Circuit. * Removed DoUnitary class from qoqo since functionality replaced by QuantumProgram. -## 0.11.3 +## v0.11.3 * Fixing errors in git pushes -## 0.11.1 +## v0.11.1 -### Fixed 0.11.1 +### Fixed v0.11.1 * Failed dependency resolution in roqoqo/Cargo.toml -## 0.11 +## v0.11 * qoqo can now be built using a source distribution -### Added 0.11 +### Added v0.11 * Semver-style version checking for Circuit serialization. In beta mode (0.y.z) minor version must match (y_library == y_data) in release mode (x.y.z) major version must match (x_library == x_data) and minor version of library must exceed minor version of data (y_library >= y_data). * `json_schema` implementing `JsonSchema` from schemars for roqoqo data structures. @@ -34,57 +41,57 @@ This changelog track changes to the qoqo project starting at version 0.5.0 * New devices implemented in roqoqo and in qoqo: AllToAllDevice, GenericDevice, GenericChain (only next-neighbour qubits are connected) and GenericGrid (a 2D grid device). * New rotation gate `RotateXY(theta, phi)` added to the set of single qubit gates. -### Changed 0.11 +### Changed v0.11 * The multiplication function `mul` for single qubit gates has been updated so that the result is always normalized. * `qoqo/examples` has been moved to the new github repository `qoqo_examples` which also includes qoqo examples in Rust now. -* Dependencies have been updated to `qoqo_calculator = 0.7` and `pyo3 = 0.16`. Qoqo python interface has been migrated from #[pyproto] to #[pymethods]. Mutable qoqo_calculator:Calculator has been changed to unmutable where possible after the upgrade to qoqo_calculator version 0.7. +* Dependencies have been updated to `qoqo_calculator = v0.7` and `pyo3 = v0.16`. Qoqo python interface has been migrated from #[pyproto] to #[pymethods]. Mutable qoqo_calculator:Calculator has been changed to unmutable where possible after the upgrade to qoqo_calculator version v0.7. * BasisRotation and CheatedBasisRotation measurements renamed to PauliZProduct and CheatedPauliZProduct measurement to reflect that this is the measurement of the PauliProduct in the z-basis. * BasisRotation and CheatedBasisRotation measurements renamed to PauliZProduct and CheatedPauliZProduct measurement to reflect that this is the measurement of the PauliProduct in the z-basis. -## 0.10.0 +## v0.10.0 -### Fixed 0.10.0 +### Fixed v0.10.0 * Bug in running register measurements from a qoqo QuantumProgram (`.run_registers()`) -### Changed 0.10.0 +### Changed v0.10.0 * Increased tolerance for unitary violation when construction unitary matrix for SingleQubitGate from `f64::EPSILON` to `1e-6`. * Semver-style version checking for Circuit serialization. In beta mode (0.y.z) minor version must match (y_library == y_data) in release mode (x.y.z) major version must match (x_library == x_data) and minor version of library must exceed minor version of data (y_library >= y_data). * Removed support for deprecated Python 3.6 -### Added 0.10.0 +### Added v0.10.0 * Methon `.input` to return measurement input from measurments in qoqo * Method `.measurement_type` to return the type of measurement in qoqo -## 0.9.0 +## v0.9.0 -### Fixed 0.9.0 +### Fixed v0.9.0 * Bug in the probability function of the PragmaDamping gate -### Added 0.9.0 +### Added v0.9.0 * MultiQubitZZ gate. Rotation under a multi-qubit product of Pauli Z operators. * `two_qubit_edges` function in Device trait. Used to create a simple graph-library-agnostic representation of the connectivity graph of a device. -## 0.8.1 +## v0.8.1 -### Changed 0.8.1 +### Changed v0.8.1 -* Updated to pyo3 0.15.0 +* Updated to pyo3 v0.15.0 -## 0.8.0 +## v0.8.0 -### Added 0.8.0 +### Added v0.8.0 * QuantumProgram: A representation of a quantum program that accepts a list of free classical float parameters, runs measurements on a backend and returns expectation values or the classical register output of the quantum circuits. QuantumProgram is intended as the main interface between classical software and roqoqo quantum programs. -### Changed 0.8.0 +### Changed v0.8.0 * In the Device Trait the `change_device` function changed the signature from @@ -100,54 +107,54 @@ QuantumProgram is intended as the main interface between classical software and including the `hqslang` name of the operation that changes the device. -### Fixed 0.8.0 +### Fixed v0.8.0 * Bug in `wrapped_hqslang` and missing `wrapped_operation` functions in qoqo PragmaChangeDeviceWrapper -## 0.7.0 +## v0.7.0 -### Added 0.7.0 +### Added v0.7.0 * PramgaChangeDevice: A pragma operation acting as a wrapper around device specific Pragmas that can change the device topology. * change_device interface to Device trait allowing for the modification of Devices by Pragmas -## 0.6.3 +## v0.6.3 -### Changed 0.6.3 +### Changed v0.6.3 * Update to rust 2021 edition -### Fixed 0.6.3 +### Fixed v0.6.3 * Fix constructing enum MultiQubitGateOperation for all operations implementing OperateMultiQubitGate * Fixed calculation of superoperator for damping -## 0.6.2 +## v0.6.2 ### Changed * Fixed function signatures in Device trait to uniformly return values instead of references and take references for qubits -## 0.6.1 +## v0.6.1 -### Added 0.6.1 +### Added v0.6.1 * Unittest for the superoperator method of the PragmaGeneralNoise * NegativeEigenvalue RoqoqoError for matrices that are not positive semi-definite -## 0.6.0 +## v0.6.0 -### Added 0.6.0 +### Added v0.6.0 * Device trait: A minimal trait for quantum computing devices used with roqoqo * `RoqoqoBackendError` now has a variant `GenericError` for additional backend error types -### Changed 0.6.0 +### Changed v0.6.0 * 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 +## v0.5.1 ### Fixed in roqoqo @@ -157,19 +164,19 @@ QuantumProgram is intended as the main interface between classical software and * Bugfix measurement selection in stochastic_gate_test -## 0.5.0 +## v0.5.0 -### Changed 0.5.0 +### Changed v0.5.0 * Fixed versioning scheme to use the same version number across the project. -* Updated pyo3 dependency to 0.14.1, numpy to 0.14, num-complex to 0.4 and ndarray to 0.15 +* Updated pyo3 dependency to v0.14.1, numpy to v0.14, num-complex to v0.4 and ndarray to v0.15 * Removed sprs dependency to allow update of other dependencies ### Fixed in qoqo * Wrong Python Class name of ClassicalRegister measurement (was "Cheated") -### Added 0.5.0 +### Added v0.5.0 * PhaseShiftedControlledZ gate in roqoqo * QoqoBackendError to use in the python interface of rust based backends diff --git a/Cargo.lock b/Cargo.lock index 000a3f8c..9e697cd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -391,7 +391,7 @@ dependencies = [ [[package]] name = "qoqo" -version = "1.0.0-alpha" +version = "1.0.0-alpha.1" dependencies = [ "bincode", "nalgebra 0.30.1", @@ -414,7 +414,7 @@ dependencies = [ [[package]] name = "qoqo-macros" -version = "1.0.0-alpha" +version = "1.0.0-alpha.1" dependencies = [ "proc-macro2", "quote", @@ -511,7 +511,7 @@ dependencies = [ [[package]] name = "roqoqo" -version = "1.0.0-alpha" +version = "1.0.0-alpha.1" dependencies = [ "bincode", "dyn-clone", @@ -535,7 +535,7 @@ dependencies = [ [[package]] name = "roqoqo-derive" -version = "1.0.0-alpha" +version = "1.0.0-alpha.1" dependencies = [ "proc-macro2", "quote", @@ -544,7 +544,7 @@ dependencies = [ [[package]] name = "roqoqo-test" -version = "1.0.0-alpha" +version = "1.0.0-alpha.1" dependencies = [ "nalgebra 0.30.1", "ndarray", diff --git a/qoqo-macros/Cargo.toml b/qoqo-macros/Cargo.toml index ce7eab7a..0473a759 100644 --- a/qoqo-macros/Cargo.toml +++ b/qoqo-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qoqo-macros" -version = "1.0.0-alpha" +version = "1.0.0-alpha.1" authors = ["HQS Quantum Simulations "] license = "Apache-2.0" readme = "../README.md" diff --git a/qoqo/Cargo.toml b/qoqo/Cargo.toml index 3a5d5428..3982aaef 100644 --- a/qoqo/Cargo.toml +++ b/qoqo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qoqo" -version = "1.0.0-alpha" +version = "1.0.0-alpha.1" authors = ["HQS Quantum Simulations "] license = "Apache-2.0" homepage = "https://github.com/HQSquantumsimulations/qoqo" @@ -32,8 +32,8 @@ num-complex = "0.4" thiserror = "1.0" qoqo_calculator = { version="0.7" } qoqo_calculator_pyo3 = {version="0.7", default-features=false} -qoqo-macros = {version="1.0.0-alpha", path="../qoqo-macros"} -roqoqo = {version="1.0.0-alpha", path="../roqoqo", features=["serialize", "overrotate"]} +qoqo-macros = {version="1.0.0-alpha.1", path="../qoqo-macros"} +roqoqo = {version="1.0.0-alpha.1", path="../roqoqo", features=["serialize", "overrotate"]} numpy = "0.16" bincode = "1.3" serde_json = "1.0" diff --git a/qoqo/qoqo/DEPENDENCIES b/qoqo/qoqo/DEPENDENCIES index 740fd013..745c8ec7 100644 --- a/qoqo/qoqo/DEPENDENCIES +++ b/qoqo/qoqo/DEPENDENCIES @@ -7757,7 +7757,7 @@ LICENSE: ==================================================== -qoqo 1.0.0-alpha +qoqo 1.0.0-alpha.1 https://github.com/HQSquantumsimulations/qoqo by HQS Quantum Simulations Quantum computing circuit toolkit. Python interface of roqoqo @@ -7969,7 +7969,7 @@ LICENSE: ==================================================== -qoqo-macros 1.0.0-alpha +qoqo-macros 1.0.0-alpha.1 by HQS Quantum Simulations Macros for the qoqo crate License: Apache-2.0 @@ -10055,7 +10055,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==================================================== -roqoqo 1.0.0-alpha +roqoqo 1.0.0-alpha.1 https://github.com/HQSquantumsimulations/qoqo by HQS Quantum Simulations Rust Quantum Computing Toolkit by HQS @@ -10267,7 +10267,7 @@ LICENSE: ==================================================== -roqoqo-derive 1.0.0-alpha +roqoqo-derive 1.0.0-alpha.1 by HQS Quantum Simulations Macros for the roqoqo crate License: Apache-2.0 @@ -10478,7 +10478,7 @@ LICENSE: ==================================================== -roqoqo-test 1.0.0-alpha +roqoqo-test 1.0.0-alpha.1 https://github.com/HQSquantumsimulations/qoqo by HQS Quantum Simulations Testing helper functions for roqoqo toolkit diff --git a/qoqo/qoqo/do_unitary/__init__.py b/qoqo/qoqo/do_unitary/__init__.py deleted file mode 100644 index 1867d1e8..00000000 --- a/qoqo/qoqo/do_unitary/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright © 2019-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. -"""Provides unitary evolution runner - -.. autosummary:: - :toctree: generated/ - - DoUnitary - -""" - -from .do_unitary import DoUnitary \ No newline at end of file diff --git a/qoqo/qoqo/do_unitary/do_unitary.py b/qoqo/qoqo/do_unitary/do_unitary.py deleted file mode 100644 index 76e1e154..00000000 --- a/qoqo/qoqo/do_unitary/do_unitary.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright © 2019-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. -"""Base class for DoUnitary""" - -from typing import ( - Union, - List, - Dict, - Optional, - cast, - Any -) - - -class DoUnitary(object): - r"""DoUnitary class. - - DoUnitary takes a prepared list of circuits (initialization, unitary evolution and measurement) - and executes it. When DoUnitary is called, the parameters replace the free variables in the - prepared circuit. - - 1) The initialization phase can be anything from just starting in a certain - state to starting in the basic |0> state and evolving to the steady-state. - 2) The unitary evolution phase is a circuit with gates usually representing a certain - Hamiltonian, repeated a certain number of times or Trotter steps. - 3) The measurement phase is the measurement of certain expectation values. These are - defined in terms of pauli product expectations, such as the PauliZProduct - (in qoqo -> measurements). - """ - - def __init__(self, - backend: Any, - measurement: Any, - free_parameters: List[str], - device: Optional[Any] = None, - resume_call_parameters: Optional[Dict[str, float]] = None, - resume_file_name: Optional[str] = None) -> None: - """Initialize DoUnitary. - - Args: - backend: A qoqo backend either simulating a circuit or running it on a real QPU - measurement: A qoqo measurement class transforming the result of the simulation - or QPU run into an operator measurement - device: A qoqo device specification giving the available gates, - topology and error properties - free_parameters: List of free parameters that need to be set when calling DoUnitary - to return expectation values - resume_call_parameters: Set of parameters for __call__ when DoUnitary is resuming - an old run. DO NOT SET BY HAND - resume_file_name: Optional file name for resuming DoUnitary run. Fails when - old file would be overwritten. - """ - self._measurement = measurement - self._backend = backend - self._free_parameters = free_parameters - self._device = device - self._backend.device = device - self._resume_file_name: Optional[str] = resume_file_name - self._resume_call_parameters: Optional[Dict[str, float]] = resume_call_parameters - - def __call__(self, - parameters: Optional[Union[List[float], Dict['str', float]]] = None, - ) -> Optional[Dict[str, float]]: - """Run DoUnitary. - - This executes the unitary evolution and the measurement, with the parameters replacing the - free variables in the circuit. - - Args: - parameters: The parameters t_i of the unitary evolution. - These must be given either as a list in the order of the H_i or as - a dictionary of the form {i: t}, where the t_i matches the H_i by name i - - Returns: - Optional[Dict[str, float]]: The measured expectation values plus the set parameters - - Raises: - ValueError: All parameters of the unitary time evolution must be set - """ - self.call_parameters = parameters - - if parameters is None: - parameters = cast(Dict[str, float], dict()) - parameter_substitution_dict: Dict[str, float] = {} - - if isinstance(parameters, dict): - parameter_substitution_dict = parameters - if any([name not in parameter_substitution_dict.keys() - for name in self._free_parameters]): - raise ValueError("All parameters of the unitary time evolution must be set") - else: - for name, p in zip(self._free_parameters, parameters): - parameter_substitution_dict[name] = p - - if self._resume_file_name is not None: - self._resume_call_parameters = parameter_substitution_dict - - run_measurement = self._measurement.substitute_parameters(parameter_substitution_dict) - - expectation_values = self._backend.run_measurement(run_measurement) - if expectation_values is None: - # if self._resume_file_name is not None: - # resume_config = self.to_qonfig() - # resume_config['measurement']['resume_list'] = getattr( - # self._measurement, '_resume_info', None) - # resume_config.save_to_yaml(self._resume_file_name, - # overwrite=False) - return None - self._backend_cached = self._backend - expectation_values.update(parameter_substitution_dict) - return expectation_values diff --git a/roqoqo-derive/Cargo.toml b/roqoqo-derive/Cargo.toml index b96c529f..ef2fa01e 100644 --- a/roqoqo-derive/Cargo.toml +++ b/roqoqo-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "roqoqo-derive" -version = "1.0.0-alpha" +version = "1.0.0-alpha.1" authors = ["HQS Quantum Simulations "] license = "Apache-2.0" edition = "2021" diff --git a/roqoqo-test/Cargo.toml b/roqoqo-test/Cargo.toml index 9680fa93..51afaf65 100644 --- a/roqoqo-test/Cargo.toml +++ b/roqoqo-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "roqoqo-test" -version = "1.0.0-alpha" +version = "1.0.0-alpha.1" authors = ["HQS Quantum Simulations "] license = "Apache-2.0" edition = "2021" @@ -19,7 +19,7 @@ crate-type = ["rlib"] [dependencies] qoqo_calculator = { version="0.7" } -roqoqo = {version="1.0.0-alpha", path="../roqoqo", features=["serialize"]} +roqoqo = {version="1.0.0-alpha.1", path="../roqoqo", features=["serialize"]} rand = "0.8" nalgebra = "0.30" ndarray = { version = "0.15" } diff --git a/roqoqo/Cargo.toml b/roqoqo/Cargo.toml index 92aba718..3add286b 100644 --- a/roqoqo/Cargo.toml +++ b/roqoqo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "roqoqo" -version = "1.0.0-alpha" +version = "1.0.0-alpha.1" authors = ["HQS Quantum Simulations "] license = "Apache-2.0" edition = "2021" @@ -28,7 +28,7 @@ num-complex = { version = "0.4"} thiserror = "1.0" dyn-clone = {version="1.0", optional=true} qoqo_calculator = { version="0.7"} -roqoqo-derive = {version="1.0.0-alpha", path="../roqoqo-derive"} +roqoqo-derive = {version="1.0.0-alpha.1", path="../roqoqo-derive"} typetag = {version="0.1", optional=true} nalgebra = "0.31" bincode = {version="1.3", optional=true} diff --git a/roqoqo/src/devices.rs b/roqoqo/src/devices.rs index c9524aba..9233f7aa 100644 --- a/roqoqo/src/devices.rs +++ b/roqoqo/src/devices.rs @@ -37,9 +37,10 @@ //! accessing the quantum computing hardware. The devices also encode a connectivity model //! -use crate::{RoqoqoBackendError, RoqoqoError}; +use crate::RoqoqoBackendError; use ndarray::Array2; -use std::collections::HashMap; +// use crate::RoqoqoError; +// use std::collections::HashMap; /// Trait for roqoqo devices. /// @@ -174,1513 +175,1513 @@ pub trait Device { } } -/// A device assuming all-to-all connectivity between all involved qubits. -/// -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -pub struct AllToAllDevice { - number_qubits: usize, - single_qubit_gates: HashMap>, - two_qubit_gates: HashMap>, - multi_qubit_gates: HashMap, - decoherence_rates: HashMap>, -} - -impl AllToAllDevice { - /// Create new AllToAllDevice. - /// - /// # Arguments - /// - /// * `number_qubits` - The number of qubits in the device. - /// * `single_qubit_gates` - A list of 'hqslang' names of single-qubit-gates supported by the device. - /// * `two_qubit_gates` - A list of 'hqslang' names of basic two-qubit-gates supported by the device. - // * `multi_qubit_gates` - A list of 'hqslang' names of basic multi-qubit-gates supported by the device. - /// - /// # Returns - /// - /// An initiated AllToAllDevice with empty gate times and decoherence rates set to zero. - /// - pub fn new( - number_qubits: usize, - single_qubit_gates: &[String], - two_qubit_gates: &[String], - multi_qubit_gates: &[String], - ) -> Self { - // Initialization of single qubit gates with empty times - let mut single_qubit_gate_map: HashMap> = HashMap::new(); - for gate in single_qubit_gates.iter() { - let mut empty_times: Vec = Vec::new(); - for qubit in 0..number_qubits { - let qubittime = SingleQubitMap { qubit, time: 0.0 }; - empty_times.push(qubittime); - } - single_qubit_gate_map.insert(gate.clone(), empty_times); - } - - // Initialization of two qubit gates with empty times - let mut two_qubit_gate_map: HashMap> = HashMap::new(); - for gate in two_qubit_gates.iter() { - let mut empty_times: Vec = Vec::new(); - for qubit0 in 0..number_qubits { - for qubit1 in 0..number_qubits { - if qubit0 != qubit1 { - let qubittime1 = TwoQubitMap { - control: qubit0, - target: qubit1, - time: 0.0, - }; - let qubittime2 = TwoQubitMap { - control: qubit1, - target: qubit0, - time: 0.0, - }; - empty_times.push(qubittime1); - empty_times.push(qubittime2); - } - } - } - two_qubit_gate_map.insert(gate.clone(), empty_times); - } - - // Initilization of multi qubit gates with empty times when applied to any qubits - let mut multi_qubit_gate_map: HashMap = HashMap::new(); - for gate in multi_qubit_gates.iter() { - multi_qubit_gate_map.insert(gate.clone(), 0.0); - } - - let mut decoherence_rates: HashMap> = HashMap::new(); - for qubit0 in 0..number_qubits { - decoherence_rates.insert(qubit0, Array2::::zeros((3, 3))); - } - - AllToAllDevice { - number_qubits, - single_qubit_gates: single_qubit_gate_map, - two_qubit_gates: two_qubit_gate_map, - multi_qubit_gates: multi_qubit_gate_map, - decoherence_rates, - } - } - - /// Function that allows to set one gate time per gate type for the single-qubit-gates. - /// - /// # Arguments - /// - /// * `gate` - hqslang name of the single-qubit-gate. - /// * `gate_time` - gate time for the given gate type, valid for all qubits in the device. - /// - /// # Returns - /// - /// An AllToAllDevice with updated gate times. - /// - pub fn set_all_single_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { - if self.single_qubit_gates.get(&gate.to_string()).is_some() { - let mut gatetimes: Vec = Vec::new(); - for qubit in 0..self.number_qubits() { - let qubittime = SingleQubitMap { - qubit, - time: gate_time, - }; - gatetimes.push(qubittime); - } - self.single_qubit_gates.insert(gate.to_string(), gatetimes); - } - self - } - - /// Function that allows to set the gate time for the two-qubit-gates in AllToAllDevice. - /// - /// # Arguments - /// - /// * `gate` - hqslang name of the two-qubit-gate. - /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. - /// - /// # Returns - /// - /// An AllToAllDevice with updated gate times. - /// - pub fn set_all_two_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { - if self.two_qubit_gates.get(&gate.to_string()).is_some() { - let mut times: Vec = Vec::new(); - for qubit0 in 0..self.number_qubits { - for qubit1 in 0..self.number_qubits { - if qubit0 != qubit1 { - let map1 = TwoQubitMap { - control: qubit0, - target: qubit1, - time: gate_time, - }; - let map2 = TwoQubitMap { - control: qubit1, - target: qubit0, - time: gate_time, - }; - times.push(map1); - times.push(map2); - } - } - } - self.two_qubit_gates.insert(gate.to_string(), times); - } - self - } - - /// Function that allows to set the gate time for the multi-qubit-gates in AllToAllDevice, - /// when applied to any qubits in the device. - /// - /// # Arguments - /// - /// * `gate` - hqslang name of the multi-qubit-gate. - /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. - /// - /// # Returns - /// - /// An AllToAllDevice with updated gate times. - /// - pub fn set_all_multi_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { - if self.multi_qubit_gates.get(&gate.to_string()).is_some() { - self.multi_qubit_gates.insert(gate.to_string(), gate_time); - } - self - } - - /// Function to set the decoherence rates for all qubits in the device. - /// - /// # Arguments - /// - /// * `rates` - decoherence rates for the qubits in the device, provided as a (3x3)-matrix. - /// - /// # Returns - /// - /// * `Ok(Self)` - The device with updated decoherence rates. - /// * `Err(RoqoqoError)` - The input parameter `rates` needs to be a (3x3)-matrix. - /// - pub fn set_all_qubit_decoherence_rates( - mut self, - rates: Array2, - ) -> Result { - // Check if input matrix has the dimension (3x3) - let shape = &(*rates.shape()); - if shape == [3, 3] { - for qubit in 0..self.number_qubits() { - self.decoherence_rates.insert(qubit, rates.clone()); - } - Ok(self) - } else { - Err(RoqoqoError::GenericError { - msg: "The input parameter `rates` needs to be a (3x3)-matrix.".to_string(), - }) - } - } -} - -/// Implements Device trait for AllToAllDevice. -/// -/// The Device trait defines standard functions available for roqoqo devices. -/// -impl Device for AllToAllDevice { - /// Returns the number of qubits the device supports. - /// - /// # Returns - /// - /// The number of qubits in the device. - /// - fn number_qubits(&self) -> usize { - self.number_qubits - } - - /// 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 { - match self.single_qubit_gates.get(&hqslang.to_string()) { - Some(x) => { - let mut item = x.iter().filter(|item| item.qubit == *qubit); - item.next().map(|y| y.time) - } - None => None, - } - } - - /// Returns the gate time of a two qubit operation if the two qubit operation is available on device. - /// - /// - /// # Arguments - /// - /// * `hqslang` - The hqslang name of a two qubit gate. - /// * `control` - The control qubit the gate acts on - /// * `target` - The target qubit the gate acts on - /// - /// # Returns - /// - /// * `Some` - The gate time. - /// * `None` - The gate is not available on the device. - /// - fn two_qubit_gate_time(&self, hqslang: &str, control: &usize, target: &usize) -> Option { - match self.two_qubit_gates.get(&hqslang.to_string()) { - Some(x) => { - let mut item = x - .iter() - .filter(|item| item.control == *control && item.target == *target); - item.next().map(|y| y.time) - } - None => None, - } - } - - /// Returns the gate time of a multi qubit operation if the multi qubit operation is available on device. - /// Note: in AllToAllDevice the gate time of multi qubit gates is treated uniformly for all qubits. - /// - /// - /// # Arguments - /// - /// * `hqslang` - The hqslang name of a multi qubit gate. - /// * `qubits` - The qubits the gate acts on. - /// - /// - /// # Returns - /// - /// * `Some` - The gate time. - /// * `None` - The gate is not available on the device. - /// - fn multi_qubit_gate_time(&self, hqslang: &str, qubits: &[usize]) -> Option { - // variable unused in AllToAllDevice, is kept here for consistency purposes. - let _qubits = qubits; - self.multi_qubit_gates.get(&hqslang.to_string()).copied() - } - - /// 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_2 = \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_decoherence_rates(&self, qubit: &usize) -> Option> { - self.decoherence_rates - .get(qubit) - .map(|rates| rates.to_owned()) - } - - /// Returns the list of pairs of qubits linked with a native two-qubit-gate in the device. - /// - /// A pair of qubits is considered linked by a native two-qubit-gate if the device - /// can implement a two-qubit-gate between the two qubits without decomposing it - /// into a sequence of gates that involves a third qubit of the device. - /// The two-qubit-gate also has to form a universal set together with the available - /// single qubit gates. - /// - /// The returned vectors is a simple, graph-library independent, representation of - /// the undirected connectivity graph of the device. - /// It can be used to construct the connectivity graph in a graph library of the users - /// choice from a list of edges and can be used for applications like routing in quantum algorithms. - /// - /// # Returns - /// - /// A list (Vec) of pairs of qubits linked with a native two-qubit-gate in the device. - /// - fn two_qubit_edges(&self) -> Vec<(usize, usize)> { - let mut vector: Vec<(usize, usize)> = Vec::new(); - for row in 0..self.number_qubits() { - for column in row + 1..self.number_qubits() { - vector.push((row, column)); - } - } - vector - } -} - -/// A generic 2D Grid Device with only next-neighbours-connectivity. -/// -/// To construct the geometry of the GenericGrid device the qubits are numbered -/// in a row-major order. -/// -/// # Example: -/// Let `m=3` be the number of rows and `n=4` the number of columns. -/// The number of qubits are numbered as follows: -/// 0 1 2 3 -/// 4 5 6 7 -/// 8 9 10 11 -/// Resulting in qubit #6 being in the 2nd row in the 3rd column. -/// -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -pub struct GenericGrid { - number_rows: usize, - number_columns: usize, - single_qubit_gates: HashMap>, - two_qubit_gates: HashMap>, - multi_qubit_gates: HashMap>, - decoherence_rates: HashMap>, -} - -impl GenericGrid { - /// Create new GenericGrid. - /// - /// # Arguments - /// - /// * `number_rows` - The number of rows in the device. - /// * `number_columns` - The number of columns in the device. - /// * `single_qubit_gates` - A list of 'hqslang' names of single-qubit-gates supported by the device. - /// * `two_qubit_gates` - A list of 'hqslang' namse of the basic two-qubit-gates supported by the device. - /// * `multi_qubit_gates` - Optional. A list of 'hqslang' namse of the basic multi-qubit-gates supported by the device. - /// - /// # Returns - /// - /// An initialized GenericGrid with empty gate times and decoherence rates set to zero. - /// - pub fn new( - number_rows: usize, - number_columns: usize, - single_qubit_gates: &[String], - two_qubit_gates: &[String], - multi_qubit_gates: &[String], - ) -> Self { - let number_qubits = number_rows * number_columns; - - // initialization of single qubit gates with empty times - let mut single_qubit_gate_map: HashMap> = HashMap::new(); - for gate in single_qubit_gates.iter() { - let mut empty_times: Vec = Vec::new(); - for qubit in 0..number_qubits { - let qubittime = SingleQubitMap { qubit, time: 0.0 }; - empty_times.push(qubittime); - } - single_qubit_gate_map.insert(gate.clone(), empty_times); - } - - // initialization of two qubit gates with empty times - let mut two_qubit_gate_map: HashMap> = HashMap::new(); - for gate in two_qubit_gates.iter() { - let mut empty_times: Vec = Vec::new(); - for row in 0..(number_rows) { - for column in 0..(number_columns) { - let qubit = row * number_columns + column; - if column < number_columns - 1 { - let map1 = TwoQubitMap { - control: qubit, - target: qubit + 1, - time: 0.0, - }; - let map2 = TwoQubitMap { - control: qubit + 1, - target: qubit, - time: 0.0, - }; - empty_times.push(map1); - empty_times.push(map2); - } - if row < number_rows - 1 { - let map1 = TwoQubitMap { - control: qubit, - target: qubit + number_columns, - time: 0.0, - }; - let map2 = TwoQubitMap { - control: qubit + number_columns, - target: qubit, - time: 0.0, - }; - empty_times.push(map1); - empty_times.push(map2); - } - } - } - two_qubit_gate_map.insert(gate.clone(), empty_times); - } - - // initialization of multi qubit gates with empty times applied to all qubits in the device - let mut multi_qubit_gate_map: HashMap> = HashMap::new(); - for gate in multi_qubit_gates.iter() { - let mut empty_times: Vec = Vec::new(); - let mut qubits: Vec> = Vec::new(); - // collect qubits per row - for m in (0..number_qubits).step_by(number_columns) { - let vec: Vec = (m..m + number_columns).collect(); - qubits.push(vec); - } - // collect qubits per column - for n in 0..number_columns { - let mut column: Vec = Vec::new(); - for row in 0..number_rows { - column.push(n + row * number_columns); - } - qubits.push(column); - } - // fill empty times for all collected qubit constellations - for item in qubits { - let map = MultiQubitMap { - qubits: item, - time: 0.0, - }; - empty_times.push(map); - } - multi_qubit_gate_map.insert(gate.clone(), empty_times); - } - - let mut decoherence_rates: HashMap> = HashMap::new(); - for qubit0 in 0..number_qubits { - decoherence_rates.insert(qubit0, Array2::::zeros((3, 3))); - } - - GenericGrid { - number_rows, - number_columns, - single_qubit_gates: single_qubit_gate_map, - two_qubit_gates: two_qubit_gate_map, - multi_qubit_gates: multi_qubit_gate_map, - decoherence_rates, - } - } - - /// Function that allows to set one gate time per gate type for the single-qubit-gates. - /// - /// # Arguments - /// * `gate` - hqslang name of the single-qubit-gate. - /// * `gate_time` - gate time for the given gate type, valid for all qubits in the device. - /// - /// # Returns - /// - /// A GenericGrid with updated gate times. - /// - pub fn set_all_single_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { - if self.single_qubit_gates.get(&gate.to_string()).is_some() { - let mut gatetimes: Vec = Vec::new(); - for qubit in 0..self.number_qubits() { - let qubittime = SingleQubitMap { - qubit, - time: gate_time, - }; - gatetimes.push(qubittime); - } - self.single_qubit_gates.insert(gate.to_string(), gatetimes); - } - self - } - - /// Function that allows to set the gate time for the two-qubit-gates - /// considered as connected in the GenericGrid. - /// - /// # Arguments - /// - /// * `gate` - hqslang name of the two-qubit-gate. - /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. - /// - /// # Returns - /// - /// A GenericGrid with updated gate times. - /// - pub fn set_all_two_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { - if self.two_qubit_gates.get(&gate.to_string()).is_some() { - let mut times: Vec = Vec::new(); - for row in 0..(self.number_rows) { - for column in 0..(self.number_columns) { - let qubit = row * self.number_columns + column; - if column < self.number_columns - 1 { - let map1 = TwoQubitMap { - control: qubit, - target: qubit + 1, - time: gate_time, - }; - let map2 = TwoQubitMap { - control: qubit + 1, - target: qubit, - time: gate_time, - }; - times.push(map1); - times.push(map2); - } - if row < self.number_rows - 1 { - let map1 = TwoQubitMap { - control: qubit, - target: qubit + self.number_columns, - time: gate_time, - }; - let map2 = TwoQubitMap { - control: qubit + self.number_columns, - target: qubit, - time: gate_time, - }; - times.push(map1); - times.push(map2); - } - } - } - self.two_qubit_gates.insert(gate.to_string(), times); - } - self - } - - /// Function that allows to set the gate time for the multi-qubit-gates in the GenericGrid. - /// In the GenericGrid device the provided `gate_time` is set for the given `gate` - /// for all qubits considered to be in one row and all qubits in one column. - /// - /// # Arguments - /// - /// * `gate` - hqslang name of the multi-qubit-gate. - /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. - /// - /// # Returns - /// - /// A GenericGrid with updated gate times. - /// - pub fn set_all_multi_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { - let number_qubits = self.number_rows * self.number_columns; - if self.multi_qubit_gates.get(&gate.to_string()).is_some() { - let mut times: Vec = Vec::new(); - let mut qubits: Vec> = Vec::new(); - // collect qubits per row - for m in (0..number_qubits).step_by(self.number_columns) { - let vec: Vec = (m..m + self.number_columns).collect(); - qubits.push(vec); - } - // collect qubits per column - for n in 0..self.number_columns { - let mut column: Vec = Vec::new(); - for row in 0..self.number_rows { - column.push(n + row * self.number_columns); - } - qubits.push(column); - } - // fill empty times for all collected qubit constellations - for item in qubits { - let map = MultiQubitMap { - qubits: item, - time: gate_time, - }; - times.push(map); - } - self.multi_qubit_gates.insert(gate.to_string(), times); - } - self - } - - /// Function to set the decoherence rates for all qubits in the device. - /// - /// # Arguments - /// - /// * `rates` - decoherence rates for the qubits in the device, provided as a (3x3)-matrix. - /// - /// # Returns - /// - /// * `Ok(Self)` - The device with updated decoherence rates. - /// * `Err(RoqoqoError)` - The input parameter `rates` needs to be a (3x3)-matrix. - /// - pub fn set_all_qubit_decoherence_rates( - mut self, - rates: Array2, - ) -> Result { - // Check if input matrix has the dimension (3x3) - let shape = &(*rates.shape()); - if shape == [3, 3] { - for qubit in 0..self.number_qubits() { - self.decoherence_rates.insert(qubit, rates.clone()); - } - Ok(self) - } else { - Err(RoqoqoError::GenericError { - msg: "The input parameter `rates` needs to be a (3x3)-matrix.".to_string(), - }) - } - } - - /// Returns the number of rows in the device. - /// - /// # Returns - /// - /// The number of rows in the device. - /// - pub fn number_rows(&self) -> usize { - self.number_rows - } - - /// Returns the number of columns in the device. - /// - /// # Returns - /// - /// The number of columns in the device. - /// - pub fn number_columns(&self) -> usize { - self.number_columns - } -} - -/// Implements Device trait for GenericGrid. -/// -/// The Device trait defines standard functions available for roqoqo devices. -/// -impl Device for GenericGrid { - /// Returns the number of qubits in the device. - /// - /// # Returns - /// - /// The number of qubits in the device. - /// - fn number_qubits(&self) -> usize { - self.number_rows * self.number_columns - } - - /// Returns the gate time of a single qubit operation if the single qubit operation is available on device. - /// - /// # 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 { - match self.single_qubit_gates.get(&hqslang.to_string()) { - Some(x) => { - let mut item = x.iter().filter(|item| item.qubit == *qubit); - item.next().map(|y| y.time) - } - None => None, - } - } - - /// Returns the gate time of a two qubit operation if the two qubit operation is available on device-. - /// - /// - /// # Arguments - /// - /// * `hqslang` - The hqslang name of a two qubit gate. - /// * `control` - The control qubit the gate acts on - /// * `target` - The target qubit the gate acts on - /// - /// # Returns - /// - /// * `Some` - The gate time. - /// * `None` - The gate is not available on the device. - /// - fn two_qubit_gate_time(&self, hqslang: &str, control: &usize, target: &usize) -> Option { - match self.two_qubit_gates.get(&hqslang.to_string()) { - Some(x) => { - let mut item = x - .iter() - .filter(|item| item.control == *control && item.target == *target); - item.next().map(|y| y.time) - } - None => None, - } - } - - /// Returns the gate time of a multi qubit operation if the multi qubit operation is available on device. - /// - /// - /// # Arguments - /// - /// * `hqslang` - The hqslang name of a multi qubit gate. - /// * `qubits` - The qubits the gate acts on - /// - /// # Returns - /// - /// * `Some` - The gate time. - /// * `None` - The gate is not available on the device. - /// - fn multi_qubit_gate_time(&self, hqslang: &str, qubits: &[usize]) -> Option { - match self.multi_qubit_gates.get(&hqslang.to_string()) { - Some(x) => { - let mut item = x.iter().filter(|item| item.qubits == *qubits); - item.next().map(|y| y.time) - } - None => None, - } - } - - /// 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_decoherence_rates(&self, qubit: &usize) -> Option> { - self.decoherence_rates - .get(qubit) - .map(|rates| rates.to_owned()) - } - - /// Returns the list of pairs of qubits linked with a native two-qubit-gate in the device. - /// - /// A pair of qubits is considered linked by a native two-qubit-gate if the device - /// can implement a two-qubit-gate between the two qubits without decomposing it - /// into a sequence of gates that involves a third qubit of the device. - /// The two-qubit-gate also has to form a universal set together with the available - /// single qubit gates. - /// - /// The returned vectors is a simple, graph-library independent, representation of - /// the undirected connectivity graph of the device. - /// It can be used to construct the connectivity graph in a graph library of the users - /// choice from a list of edges and can be used for applications like routing in quantum algorithms. - /// - /// # Returns - /// - /// A list (Vec) of pairs of qubits linked with a native two-qubit-gate in the device. - /// - fn two_qubit_edges(&self) -> Vec<(usize, usize)> { - let mut vector: Vec<(usize, usize)> = Vec::new(); - for row in 0..(self.number_rows) { - for column in 0..(self.number_columns) { - let qubit = row * self.number_columns + column; - if column < self.number_columns - 1 { - vector.push((qubit, qubit + 1)); - } - if row < self.number_rows - 1 { - vector.push((qubit, qubit + self.number_columns)); - } - } - } - vector - } -} - -/// A generic device containing a linear chain of qubits with next neighbour connectivity. -/// -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -pub struct GenericChain { - number_qubits: usize, - single_qubit_gates: HashMap>, - two_qubit_gates: HashMap>, - multi_qubit_gates: HashMap, - decoherence_rates: HashMap>, -} - -impl GenericChain { - /// Create new GenericChain. - /// - /// # Arguments - /// - /// * `number_qubits` - The number of qubits in the device. - /// * `single_qubit_gates` - A list of 'hqslang' names of single-qubit-gates supported by the device. - /// * `two_qubit_gates` - A list of 'hqslang' names of basic two-qubit-gates supported by the device. - // * `multi_qubit_gates` - A list of 'hqslang' names of basic multi-qubit-gates supported by the device. - /// - /// # Returns - /// - /// An initiated GenericChain with empty gate times and decoherence rates set to zero. - /// - pub fn new( - number_qubits: usize, - single_qubit_gates: &[String], - two_qubit_gates: &[String], - multi_qubit_gates: &[String], - ) -> Self { - // Initialization of single qubit gates with empty times - let mut single_qubit_gate_map: HashMap> = HashMap::new(); - for gate in single_qubit_gates.iter() { - let mut empty_times: Vec = Vec::new(); - for qubit in 0..number_qubits { - let qubittime = SingleQubitMap { qubit, time: 0.0 }; - empty_times.push(qubittime); - } - single_qubit_gate_map.insert(gate.clone(), empty_times); - } - - // Initialization of two qubit gates with empty times - let mut two_qubit_gate_map: HashMap> = HashMap::new(); - for gate in two_qubit_gates.iter() { - let mut empty_times: Vec = Vec::new(); - for qubit in 0..number_qubits - 1 { - let qubittime1 = TwoQubitMap { - control: qubit, - target: qubit + 1, - time: 0.0, - }; - let qubittime2 = TwoQubitMap { - control: qubit + 1, - target: qubit, - time: 0.0, - }; - empty_times.push(qubittime1); - empty_times.push(qubittime2); - } - two_qubit_gate_map.insert(gate.clone(), empty_times); - } - - // Initilization of multi qubit gates with empty times when applied to any qubits - let mut multi_qubit_gate_map: HashMap = HashMap::new(); - for gate in multi_qubit_gates.iter() { - multi_qubit_gate_map.insert(gate.clone(), 0.0); - } - - let mut decoherence_rates: HashMap> = HashMap::new(); - for qubit0 in 0..number_qubits { - decoherence_rates.insert(qubit0, Array2::::zeros((3, 3))); - } - - GenericChain { - number_qubits, - single_qubit_gates: single_qubit_gate_map, - two_qubit_gates: two_qubit_gate_map, - multi_qubit_gates: multi_qubit_gate_map, - decoherence_rates, - } - } - - /// Function that allows to set one gate time per gate type for the single-qubit-gates. - /// - /// # Arguments - /// - /// * `gate` - hqslang name of the single-qubit-gate. - /// * `gate_time` - gate time for the given gate type, valid for all qubits in the device. - /// - /// # Returns - /// - /// An GenericChain with updated gate times. - /// - pub fn set_all_single_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { - if self.single_qubit_gates.get(&gate.to_string()).is_some() { - let mut gatetimes: Vec = Vec::new(); - for qubit in 0..self.number_qubits() { - let qubittime = SingleQubitMap { - qubit, - time: gate_time, - }; - gatetimes.push(qubittime); - } - self.single_qubit_gates.insert(gate.to_string(), gatetimes); - } - self - } - - /// Function that allows to set the gate time for the two-qubit-gates in GenericChain. - /// - /// # Arguments - /// - /// * `gate` - hqslang name of the two-qubit-gate. - /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. - /// - /// # Returns - /// - /// An GenericChain with updated gate times. - /// - pub fn set_all_two_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { - if self.two_qubit_gates.get(&gate.to_string()).is_some() { - let mut times: Vec = Vec::new(); - for qubit in 0..self.number_qubits - 1 { - let map1 = TwoQubitMap { - control: qubit, - target: qubit + 1, - time: gate_time, - }; - let map2 = TwoQubitMap { - control: qubit + 1, - target: qubit, - time: gate_time, - }; - times.push(map1); - times.push(map2); - } - self.two_qubit_gates.insert(gate.to_string(), times); - } - self - } - - /// Function that allows to set the gate time for the multi-qubit-gates in GenericChain, - /// when applied to any qubits in the device. - /// - /// # Arguments - /// - /// * `gate` - hqslang name of the multi-qubit-gate. - /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. - /// - /// # Returns - /// - /// An GenericChain with updated gate times. - /// - pub fn set_all_multi_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { - if self.multi_qubit_gates.get(&gate.to_string()).is_some() { - self.multi_qubit_gates.insert(gate.to_string(), gate_time); - } - self - } - - /// Function to set the decoherence rates for all qubits in the device. - /// - /// # Arguments - /// - /// * `rates` - decoherence rates for the qubits in the device, provided as a (3x3)-matrix. - /// - /// # Returns - /// - /// * `Ok(Self)` - The device with updated decoherence rates. - /// * `Err(RoqoqoError)` - The input parameter `rates` needs to be a (3x3)-matrix. - /// - pub fn set_all_qubit_decoherence_rates( - mut self, - rates: Array2, - ) -> Result { - // Check if input matrix has the dimension (3x3) - let shape = &(*rates.shape()); - if shape == [3, 3] { - for qubit in 0..self.number_qubits() { - self.decoherence_rates.insert(qubit, rates.clone()); - } - Ok(self) - } else { - Err(RoqoqoError::GenericError { - msg: "The input parameter `rates` needs to be a (3x3)-matrix.".to_string(), - }) - } - } -} - -/// Implements Device trait for GenericChain. -/// -/// The Device trait defines standard functions available for roqoqo devices. -/// -impl Device for GenericChain { - /// Returns the number of qubits the device supports. - /// - /// # Returns - /// - /// The number of qubits in the device. - /// - fn number_qubits(&self) -> usize { - self.number_qubits - } - - /// 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 { - match self.single_qubit_gates.get(&hqslang.to_string()) { - Some(x) => { - let mut item = x.iter().filter(|item| item.qubit == *qubit); - item.next().map(|y| y.time) - } - None => None, - } - } - - /// Returns the gate time of a two qubit operation if the two qubit operation is available on device. - /// - /// - /// # Arguments - /// - /// * `hqslang` - The hqslang name of a two qubit gate. - /// * `control` - The control qubit the gate acts on - /// * `target` - The target qubit the gate acts on - /// - /// # Returns - /// - /// * `Some` - The gate time. - /// * `None` - The gate is not available on the device. - /// - fn two_qubit_gate_time(&self, hqslang: &str, control: &usize, target: &usize) -> Option { - match self.two_qubit_gates.get(&hqslang.to_string()) { - Some(x) => { - let mut item = x - .iter() - .filter(|item| item.control == *control && item.target == *target); - item.next().map(|y| y.time) - } - None => None, - } - } - - /// Returns the gate time of a multi qubit operation if the multi qubit operation is available on device. - /// Note: in GenericChain the gate time of multi qubit gates is treated uniformly for all qubits. - /// - /// - /// # Arguments - /// - /// * `hqslang` - The hqslang name of a multi qubit gate. - /// * `qubits` - The qubits the gate acts on. - /// - /// - /// # Returns - /// - /// * `Some` - The gate time. - /// * `None` - The gate is not available on the device. - /// - fn multi_qubit_gate_time(&self, hqslang: &str, qubits: &[usize]) -> Option { - // variable unused in GenericChain, is kept here for consistency purposes. - let _qubits = qubits; - self.multi_qubit_gates.get(&hqslang.to_string()).copied() - } - - /// 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_2 = \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_decoherence_rates(&self, qubit: &usize) -> Option> { - self.decoherence_rates - .get(qubit) - .map(|rates| rates.to_owned()) - } - - /// Returns the list of pairs of qubits linked with a native two-qubit-gate in the device. - /// - /// A pair of qubits is considered linked by a native two-qubit-gate if the device - /// can implement a two-qubit-gate between the two qubits without decomposing it - /// into a sequence of gates that involves a third qubit of the device. - /// The two-qubit-gate also has to form a universal set together with the available - /// single qubit gates. - /// - /// The returned vectors is a simple, graph-library independent, representation of - /// the undirected connectivity graph of the device. - /// It can be used to construct the connectivity graph in a graph library of the users - /// choice from a list of edges and can be used for applications like routing in quantum algorithms. - /// - /// # Returns - /// - /// A list (Vec) of pairs of qubits linked with a native two-qubit-gate in the device. - /// - fn two_qubit_edges(&self) -> Vec<(usize, usize)> { - let mut vector: Vec<(usize, usize)> = Vec::new(); - for qubit in 0..self.number_qubits() - 1 { - vector.push((qubit, qubit + 1)); - } - vector - } -} - -/// A device struct with public fields for a roqoqo device -/// with all-to-all connectivity between all involved qubits. -/// -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -pub struct GenericDevice { - /// The number of qubits in the device. - pub number_qubits: usize, - /// A map including 'hqslang' names of single-qubit-gates supported by the device, - /// with the according gate times specific for the particular qubit ([SingleQubitMap]). - pub single_qubit_gates: HashMap>, - /// A map including 'hqslang' names of two-qubit-gates supported by the device, - /// with the according gate times specific for the particular qubit ([TwoQubitMap]). - pub two_qubit_gates: HashMap>, - /// A map including 'hqslang' names of multi-qubit-gates supported by the device, - /// with the according gate times uniform for all qubits in the device. - pub multi_qubit_gates: HashMap, - /// A (3x3)-matrix of the decoherence rates. - pub decoherence_rates: HashMap>, -} - -impl GenericDevice { - /// Create new GenericDevice. - /// - /// # Arguments - /// - /// * `number_qubits` - The number of qubits in the device. - /// * `single_qubit_gates` - A list of 'hqslang' names of single-qubit-gates supported by the device. - /// * `two_qubit_gates` - A list of 'hqslang' names of basic two-qubit-gates supported by the device. - /// * `multi_qubit_gates` - A list of 'hqslang' names of basic multi-qubit-gates supported by the device. - /// - /// # Returns - /// - /// An initiated GenericDevice with empty gate times and decoherence rates set to zero. - /// - pub fn new( - number_qubits: usize, - single_qubit_gates: &[String], - two_qubit_gates: &[String], - multi_qubit_gates: &[String], - ) -> Self { - // Initialization of single qubit gates with empty times - let mut single_qubit_gate_map: HashMap> = HashMap::new(); - for gate in single_qubit_gates.iter() { - let mut empty_times: Vec = Vec::new(); - for qubit in 0..number_qubits { - let qubittime = SingleQubitMap { qubit, time: 0.0 }; - empty_times.push(qubittime); - } - single_qubit_gate_map.insert(gate.clone(), empty_times); - } - - // Initialization of two qubit gates with empty times - let mut two_qubit_gate_map: HashMap> = HashMap::new(); - for gate in two_qubit_gates.iter() { - let mut empty_times: Vec = Vec::new(); - for qubit0 in 0..number_qubits { - for qubit1 in 0..number_qubits { - if qubit0 != qubit1 { - let qubittime1 = TwoQubitMap { - control: qubit0, - target: qubit1, - time: 0.0, - }; - let qubittime2 = TwoQubitMap { - control: qubit1, - target: qubit0, - time: 0.0, - }; - empty_times.push(qubittime1); - empty_times.push(qubittime2); - } - } - } - two_qubit_gate_map.insert(gate.clone(), empty_times); - } - - // Initilization of multi qubit gates with empty times when applied to any qubits - let mut multi_qubit_gate_map: HashMap = HashMap::new(); - for gate in multi_qubit_gates.iter() { - multi_qubit_gate_map.insert(gate.clone(), 0.0); - } - - let mut decoherence_rates: HashMap> = HashMap::new(); - for qubit0 in 0..number_qubits { - decoherence_rates.insert(qubit0, Array2::::zeros((3, 3))); - } - - GenericDevice { - number_qubits, - single_qubit_gates: single_qubit_gate_map, - two_qubit_gates: two_qubit_gate_map, - multi_qubit_gates: multi_qubit_gate_map, - decoherence_rates, - } - } - - /// Function that allows to set one gate time per gate type for the single-qubit-gates. - /// - /// # Arguments - /// - /// * `gate` - hqslang name of the single-qubit-gate. - /// * `gate_time` - gate time for the given gate type, valid for all qubits in the device. - /// - /// # Returns - /// - /// An GenericDevice with updated gate times. - /// - pub fn set_all_single_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { - if self.single_qubit_gates.get(&gate.to_string()).is_some() { - let mut gatetimes: Vec = Vec::new(); - for qubit in 0..self.number_qubits() { - let qubittime = SingleQubitMap { - qubit, - time: gate_time, - }; - gatetimes.push(qubittime); - } - self.single_qubit_gates.insert(gate.to_string(), gatetimes); - } - self - } - - /// Function that allows to set the gate time for the two-qubit-gates in GenericDevice. - /// - /// # Arguments - /// - /// * `gate` - hqslang name of the two-qubit-gate. - /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. - /// - /// # Returns - /// - /// An GenericDevice with updated gate times. - /// - pub fn set_all_two_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { - if self.two_qubit_gates.get(&gate.to_string()).is_some() { - let mut times: Vec = Vec::new(); - for qubit0 in 0..self.number_qubits { - for qubit1 in 0..self.number_qubits { - if qubit0 != qubit1 { - let map1 = TwoQubitMap { - control: qubit0, - target: qubit1, - time: gate_time, - }; - let map2 = TwoQubitMap { - control: qubit1, - target: qubit0, - time: gate_time, - }; - times.push(map1); - times.push(map2); - } - } - } - self.two_qubit_gates.insert(gate.to_string(), times); - } - self - } - - /// Function that allows to set the gate time for the multi-qubit-gates in GenericDevice, - /// when applied to any qubits in the device. - /// - /// # Arguments - /// - /// * `gate` - hqslang name of the multi-qubit-gate. - /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. - /// - /// # Returns - /// - /// An GenericDevice with updated gate times. - /// - pub fn set_all_multi_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { - if self.multi_qubit_gates.get(&gate.to_string()).is_some() { - self.multi_qubit_gates.insert(gate.to_string(), gate_time); - } - self - } - - /// Function to set the decoherence rates for all qubits in the device. - /// - /// # Arguments - /// - /// * `rates` - decoherence rates for the qubits in the device, provided as a (3x3)-matrix. - /// - /// # Returns - /// - /// * `Ok(Self)` - The device with updated decoherence rates. - /// * `Err(RoqoqoError)` - The input parameter `rates` needs to be a (3x3)-matrix. - /// - pub fn set_all_qubit_decoherence_rates( - mut self, - rates: Array2, - ) -> Result { - // Check if input matrix has the dimension (3x3) - let shape = &(*rates.shape()); - if shape == [3, 3] { - for qubit in 0..self.number_qubits() { - self.decoherence_rates.insert(qubit, rates.clone()); - } - Ok(self) - } else { - Err(RoqoqoError::GenericError { - msg: "The input parameter `rates` needs to be a (3x3)-matrix.".to_string(), - }) - } - } -} - -/// Implements Device trait for GenericDevice. -/// -/// The Device trait defines standard functions available for roqoqo devices. -/// -impl Device for GenericDevice { - /// Returns the number of qubits the device supports. - /// - /// # Returns - /// - /// The number of qubits in the device. - /// - fn number_qubits(&self) -> usize { - self.number_qubits - } - - /// 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 { - match self.single_qubit_gates.get(&hqslang.to_string()) { - Some(x) => { - let mut item = x.iter().filter(|item| item.qubit == *qubit); - item.next().map(|y| y.time) - } - None => None, - } - } - - /// Returns the gate time of a two qubit operation if the two qubit operation is available on device. - /// - /// - /// # Arguments - /// - /// * `hqslang` - The hqslang name of a two qubit gate. - /// * `control` - The control qubit the gate acts on - /// * `target` - The target qubit the gate acts on - /// - /// # Returns - /// - /// * `Some` - The gate time. - /// * `None` - The gate is not available on the device. - /// - fn two_qubit_gate_time(&self, hqslang: &str, control: &usize, target: &usize) -> Option { - match self.two_qubit_gates.get(&hqslang.to_string()) { - Some(x) => { - let mut item = x - .iter() - .filter(|item| item.control == *control && item.target == *target); - item.next().map(|y| y.time) - } - None => None, - } - } - - /// Returns the gate time of a multi qubit operation if the multi qubit operation is available on device. - /// Note: in GenericDevice the gate time of multi qubit gates is treated uniformly for all qubits. - /// - /// - /// # Arguments - /// - /// * `hqslang` - The hqslang name of a multi qubit gate. - /// * `qubits` - The qubits the gate acts on. - /// - /// - /// # Returns - /// - /// * `Some` - The gate time. - /// * `None` - The gate is not available on the device. - /// - fn multi_qubit_gate_time(&self, hqslang: &str, qubits: &[usize]) -> Option { - // variable unused in GenericDevice, is kept here for consistency purposes. - let _qubits = qubits; - self.multi_qubit_gates.get(&hqslang.to_string()).copied() - } - - /// 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_2 = \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_decoherence_rates(&self, qubit: &usize) -> Option> { - self.decoherence_rates - .get(qubit) - .map(|rates| rates.to_owned()) - } - - /// Returns the list of pairs of qubits linked with a native two-qubit-gate in the device. - /// - /// A pair of qubits is considered linked by a native two-qubit-gate if the device - /// can implement a two-qubit-gate between the two qubits without decomposing it - /// into a sequence of gates that involves a third qubit of the device. - /// The two-qubit-gate also has to form a universal set together with the available - /// single qubit gates. - /// - /// The returned vectors is a simple, graph-library independent, representation of - /// the undirected connectivity graph of the device. - /// It can be used to construct the connectivity graph in a graph library of the users - /// choice from a list of edges and can be used for applications like routing in quantum algorithms. - /// - /// # Returns - /// - /// A list (Vec) of pairs of qubits linked with a native two-qubit-gate in the device. - /// - fn two_qubit_edges(&self) -> Vec<(usize, usize)> { - let mut vector: Vec<(usize, usize)> = Vec::new(); - for row in 0..self.number_qubits() { - for column in row + 1..self.number_qubits() { - vector.push((row, column)); - } - } - vector - } -} - -/// A customized struct to use as a value in the HashMap for single_qubit_gates -/// to access the gate times. -/// -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -pub struct SingleQubitMap { - ///The index of the qubit for which the gate time is set. - pub qubit: usize, - /// Gate time for the given qubit for the single qubit gate. - pub time: f64, -} - -/// A customized struct to use as a value in the HashMap for two_qubit_gates -/// to access the gate times. -/// -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -pub struct TwoQubitMap { - /// The index of the control qubit for which the gate time is set. - pub control: usize, - /// The index of the target qubit for which the gate time is set. - pub target: usize, - /// Gate time for the given qubits for the two qubit gate. - pub time: f64, -} - -#[doc(hidden)] -/// A customized struct to use as a value in the HashMap for multi_qubit_gates -/// to access the gate times. -/// -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -pub struct MultiQubitMap { - /// A list of qubit indices for which the gate time is set. - pub qubits: Vec, - /// Gate time for the given qubits for the multi qubit gate. - pub time: f64, -} +// /// A device assuming all-to-all connectivity between all involved qubits. +// /// +// #[derive(Clone, Debug, PartialEq)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub struct AllToAllDevice { +// number_qubits: usize, +// single_qubit_gates: HashMap>, +// two_qubit_gates: HashMap>, +// multi_qubit_gates: HashMap, +// decoherence_rates: HashMap>, +// } + +// impl AllToAllDevice { +// /// Create new AllToAllDevice. +// /// +// /// # Arguments +// /// +// /// * `number_qubits` - The number of qubits in the device. +// /// * `single_qubit_gates` - A list of 'hqslang' names of single-qubit-gates supported by the device. +// /// * `two_qubit_gates` - A list of 'hqslang' names of basic two-qubit-gates supported by the device. +// // * `multi_qubit_gates` - A list of 'hqslang' names of basic multi-qubit-gates supported by the device. +// /// +// /// # Returns +// /// +// /// An initiated AllToAllDevice with empty gate times and decoherence rates set to zero. +// /// +// pub fn new( +// number_qubits: usize, +// single_qubit_gates: &[String], +// two_qubit_gates: &[String], +// multi_qubit_gates: &[String], +// ) -> Self { +// // Initialization of single qubit gates with empty times +// let mut single_qubit_gate_map: HashMap> = HashMap::new(); +// for gate in single_qubit_gates.iter() { +// let mut empty_times: Vec = Vec::new(); +// for qubit in 0..number_qubits { +// let qubittime = SingleQubitMap { qubit, time: 0.0 }; +// empty_times.push(qubittime); +// } +// single_qubit_gate_map.insert(gate.clone(), empty_times); +// } + +// // Initialization of two qubit gates with empty times +// let mut two_qubit_gate_map: HashMap> = HashMap::new(); +// for gate in two_qubit_gates.iter() { +// let mut empty_times: Vec = Vec::new(); +// for qubit0 in 0..number_qubits { +// for qubit1 in 0..number_qubits { +// if qubit0 != qubit1 { +// let qubittime1 = TwoQubitMap { +// control: qubit0, +// target: qubit1, +// time: 0.0, +// }; +// let qubittime2 = TwoQubitMap { +// control: qubit1, +// target: qubit0, +// time: 0.0, +// }; +// empty_times.push(qubittime1); +// empty_times.push(qubittime2); +// } +// } +// } +// two_qubit_gate_map.insert(gate.clone(), empty_times); +// } + +// // Initilization of multi qubit gates with empty times when applied to any qubits +// let mut multi_qubit_gate_map: HashMap = HashMap::new(); +// for gate in multi_qubit_gates.iter() { +// multi_qubit_gate_map.insert(gate.clone(), 0.0); +// } + +// let mut decoherence_rates: HashMap> = HashMap::new(); +// for qubit0 in 0..number_qubits { +// decoherence_rates.insert(qubit0, Array2::::zeros((3, 3))); +// } + +// AllToAllDevice { +// number_qubits, +// single_qubit_gates: single_qubit_gate_map, +// two_qubit_gates: two_qubit_gate_map, +// multi_qubit_gates: multi_qubit_gate_map, +// decoherence_rates, +// } +// } + +// /// Function that allows to set one gate time per gate type for the single-qubit-gates. +// /// +// /// # Arguments +// /// +// /// * `gate` - hqslang name of the single-qubit-gate. +// /// * `gate_time` - gate time for the given gate type, valid for all qubits in the device. +// /// +// /// # Returns +// /// +// /// An AllToAllDevice with updated gate times. +// /// +// pub fn set_all_single_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { +// if self.single_qubit_gates.get(&gate.to_string()).is_some() { +// let mut gatetimes: Vec = Vec::new(); +// for qubit in 0..self.number_qubits() { +// let qubittime = SingleQubitMap { +// qubit, +// time: gate_time, +// }; +// gatetimes.push(qubittime); +// } +// self.single_qubit_gates.insert(gate.to_string(), gatetimes); +// } +// self +// } + +// /// Function that allows to set the gate time for the two-qubit-gates in AllToAllDevice. +// /// +// /// # Arguments +// /// +// /// * `gate` - hqslang name of the two-qubit-gate. +// /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. +// /// +// /// # Returns +// /// +// /// An AllToAllDevice with updated gate times. +// /// +// pub fn set_all_two_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { +// if self.two_qubit_gates.get(&gate.to_string()).is_some() { +// let mut times: Vec = Vec::new(); +// for qubit0 in 0..self.number_qubits { +// for qubit1 in 0..self.number_qubits { +// if qubit0 != qubit1 { +// let map1 = TwoQubitMap { +// control: qubit0, +// target: qubit1, +// time: gate_time, +// }; +// let map2 = TwoQubitMap { +// control: qubit1, +// target: qubit0, +// time: gate_time, +// }; +// times.push(map1); +// times.push(map2); +// } +// } +// } +// self.two_qubit_gates.insert(gate.to_string(), times); +// } +// self +// } + +// /// Function that allows to set the gate time for the multi-qubit-gates in AllToAllDevice, +// /// when applied to any qubits in the device. +// /// +// /// # Arguments +// /// +// /// * `gate` - hqslang name of the multi-qubit-gate. +// /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. +// /// +// /// # Returns +// /// +// /// An AllToAllDevice with updated gate times. +// /// +// pub fn set_all_multi_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { +// if self.multi_qubit_gates.get(&gate.to_string()).is_some() { +// self.multi_qubit_gates.insert(gate.to_string(), gate_time); +// } +// self +// } + +// /// Function to set the decoherence rates for all qubits in the device. +// /// +// /// # Arguments +// /// +// /// * `rates` - decoherence rates for the qubits in the device, provided as a (3x3)-matrix. +// /// +// /// # Returns +// /// +// /// * `Ok(Self)` - The device with updated decoherence rates. +// /// * `Err(RoqoqoError)` - The input parameter `rates` needs to be a (3x3)-matrix. +// /// +// pub fn set_all_qubit_decoherence_rates( +// mut self, +// rates: Array2, +// ) -> Result { +// // Check if input matrix has the dimension (3x3) +// let shape = &(*rates.shape()); +// if shape == [3, 3] { +// for qubit in 0..self.number_qubits() { +// self.decoherence_rates.insert(qubit, rates.clone()); +// } +// Ok(self) +// } else { +// Err(RoqoqoError::GenericError { +// msg: "The input parameter `rates` needs to be a (3x3)-matrix.".to_string(), +// }) +// } +// } +// } + +// /// Implements Device trait for AllToAllDevice. +// /// +// /// The Device trait defines standard functions available for roqoqo devices. +// /// +// impl Device for AllToAllDevice { +// /// Returns the number of qubits the device supports. +// /// +// /// # Returns +// /// +// /// The number of qubits in the device. +// /// +// fn number_qubits(&self) -> usize { +// self.number_qubits +// } + +// /// 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 { +// match self.single_qubit_gates.get(&hqslang.to_string()) { +// Some(x) => { +// let mut item = x.iter().filter(|item| item.qubit == *qubit); +// item.next().map(|y| y.time) +// } +// None => None, +// } +// } + +// /// Returns the gate time of a two qubit operation if the two qubit operation is available on device. +// /// +// /// +// /// # Arguments +// /// +// /// * `hqslang` - The hqslang name of a two qubit gate. +// /// * `control` - The control qubit the gate acts on +// /// * `target` - The target qubit the gate acts on +// /// +// /// # Returns +// /// +// /// * `Some` - The gate time. +// /// * `None` - The gate is not available on the device. +// /// +// fn two_qubit_gate_time(&self, hqslang: &str, control: &usize, target: &usize) -> Option { +// match self.two_qubit_gates.get(&hqslang.to_string()) { +// Some(x) => { +// let mut item = x +// .iter() +// .filter(|item| item.control == *control && item.target == *target); +// item.next().map(|y| y.time) +// } +// None => None, +// } +// } + +// /// Returns the gate time of a multi qubit operation if the multi qubit operation is available on device. +// /// Note: in AllToAllDevice the gate time of multi qubit gates is treated uniformly for all qubits. +// /// +// /// +// /// # Arguments +// /// +// /// * `hqslang` - The hqslang name of a multi qubit gate. +// /// * `qubits` - The qubits the gate acts on. +// /// +// /// +// /// # Returns +// /// +// /// * `Some` - The gate time. +// /// * `None` - The gate is not available on the device. +// /// +// fn multi_qubit_gate_time(&self, hqslang: &str, qubits: &[usize]) -> Option { +// // variable unused in AllToAllDevice, is kept here for consistency purposes. +// let _qubits = qubits; +// self.multi_qubit_gates.get(&hqslang.to_string()).copied() +// } + +// /// 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_2 = \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_decoherence_rates(&self, qubit: &usize) -> Option> { +// self.decoherence_rates +// .get(qubit) +// .map(|rates| rates.to_owned()) +// } + +// /// Returns the list of pairs of qubits linked with a native two-qubit-gate in the device. +// /// +// /// A pair of qubits is considered linked by a native two-qubit-gate if the device +// /// can implement a two-qubit-gate between the two qubits without decomposing it +// /// into a sequence of gates that involves a third qubit of the device. +// /// The two-qubit-gate also has to form a universal set together with the available +// /// single qubit gates. +// /// +// /// The returned vectors is a simple, graph-library independent, representation of +// /// the undirected connectivity graph of the device. +// /// It can be used to construct the connectivity graph in a graph library of the users +// /// choice from a list of edges and can be used for applications like routing in quantum algorithms. +// /// +// /// # Returns +// /// +// /// A list (Vec) of pairs of qubits linked with a native two-qubit-gate in the device. +// /// +// fn two_qubit_edges(&self) -> Vec<(usize, usize)> { +// let mut vector: Vec<(usize, usize)> = Vec::new(); +// for row in 0..self.number_qubits() { +// for column in row + 1..self.number_qubits() { +// vector.push((row, column)); +// } +// } +// vector +// } +// } + +// /// A generic 2D Grid Device with only next-neighbours-connectivity. +// /// +// /// To construct the geometry of the GenericGrid device the qubits are numbered +// /// in a row-major order. +// /// +// /// # Example: +// /// Let `m=3` be the number of rows and `n=4` the number of columns. +// /// The number of qubits are numbered as follows: +// /// 0 1 2 3 +// /// 4 5 6 7 +// /// 8 9 10 11 +// /// Resulting in qubit #6 being in the 2nd row in the 3rd column. +// /// +// #[derive(Clone, Debug, PartialEq)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub struct GenericGrid { +// number_rows: usize, +// number_columns: usize, +// single_qubit_gates: HashMap>, +// two_qubit_gates: HashMap>, +// multi_qubit_gates: HashMap>, +// decoherence_rates: HashMap>, +// } + +// impl GenericGrid { +// /// Create new GenericGrid. +// /// +// /// # Arguments +// /// +// /// * `number_rows` - The number of rows in the device. +// /// * `number_columns` - The number of columns in the device. +// /// * `single_qubit_gates` - A list of 'hqslang' names of single-qubit-gates supported by the device. +// /// * `two_qubit_gates` - A list of 'hqslang' namse of the basic two-qubit-gates supported by the device. +// /// * `multi_qubit_gates` - Optional. A list of 'hqslang' namse of the basic multi-qubit-gates supported by the device. +// /// +// /// # Returns +// /// +// /// An initialized GenericGrid with empty gate times and decoherence rates set to zero. +// /// +// pub fn new( +// number_rows: usize, +// number_columns: usize, +// single_qubit_gates: &[String], +// two_qubit_gates: &[String], +// multi_qubit_gates: &[String], +// ) -> Self { +// let number_qubits = number_rows * number_columns; + +// // initialization of single qubit gates with empty times +// let mut single_qubit_gate_map: HashMap> = HashMap::new(); +// for gate in single_qubit_gates.iter() { +// let mut empty_times: Vec = Vec::new(); +// for qubit in 0..number_qubits { +// let qubittime = SingleQubitMap { qubit, time: 0.0 }; +// empty_times.push(qubittime); +// } +// single_qubit_gate_map.insert(gate.clone(), empty_times); +// } + +// // initialization of two qubit gates with empty times +// let mut two_qubit_gate_map: HashMap> = HashMap::new(); +// for gate in two_qubit_gates.iter() { +// let mut empty_times: Vec = Vec::new(); +// for row in 0..(number_rows) { +// for column in 0..(number_columns) { +// let qubit = row * number_columns + column; +// if column < number_columns - 1 { +// let map1 = TwoQubitMap { +// control: qubit, +// target: qubit + 1, +// time: 0.0, +// }; +// let map2 = TwoQubitMap { +// control: qubit + 1, +// target: qubit, +// time: 0.0, +// }; +// empty_times.push(map1); +// empty_times.push(map2); +// } +// if row < number_rows - 1 { +// let map1 = TwoQubitMap { +// control: qubit, +// target: qubit + number_columns, +// time: 0.0, +// }; +// let map2 = TwoQubitMap { +// control: qubit + number_columns, +// target: qubit, +// time: 0.0, +// }; +// empty_times.push(map1); +// empty_times.push(map2); +// } +// } +// } +// two_qubit_gate_map.insert(gate.clone(), empty_times); +// } + +// // initialization of multi qubit gates with empty times applied to all qubits in the device +// let mut multi_qubit_gate_map: HashMap> = HashMap::new(); +// for gate in multi_qubit_gates.iter() { +// let mut empty_times: Vec = Vec::new(); +// let mut qubits: Vec> = Vec::new(); +// // collect qubits per row +// for m in (0..number_qubits).step_by(number_columns) { +// let vec: Vec = (m..m + number_columns).collect(); +// qubits.push(vec); +// } +// // collect qubits per column +// for n in 0..number_columns { +// let mut column: Vec = Vec::new(); +// for row in 0..number_rows { +// column.push(n + row * number_columns); +// } +// qubits.push(column); +// } +// // fill empty times for all collected qubit constellations +// for item in qubits { +// let map = MultiQubitMap { +// qubits: item, +// time: 0.0, +// }; +// empty_times.push(map); +// } +// multi_qubit_gate_map.insert(gate.clone(), empty_times); +// } + +// let mut decoherence_rates: HashMap> = HashMap::new(); +// for qubit0 in 0..number_qubits { +// decoherence_rates.insert(qubit0, Array2::::zeros((3, 3))); +// } + +// GenericGrid { +// number_rows, +// number_columns, +// single_qubit_gates: single_qubit_gate_map, +// two_qubit_gates: two_qubit_gate_map, +// multi_qubit_gates: multi_qubit_gate_map, +// decoherence_rates, +// } +// } + +// /// Function that allows to set one gate time per gate type for the single-qubit-gates. +// /// +// /// # Arguments +// /// * `gate` - hqslang name of the single-qubit-gate. +// /// * `gate_time` - gate time for the given gate type, valid for all qubits in the device. +// /// +// /// # Returns +// /// +// /// A GenericGrid with updated gate times. +// /// +// pub fn set_all_single_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { +// if self.single_qubit_gates.get(&gate.to_string()).is_some() { +// let mut gatetimes: Vec = Vec::new(); +// for qubit in 0..self.number_qubits() { +// let qubittime = SingleQubitMap { +// qubit, +// time: gate_time, +// }; +// gatetimes.push(qubittime); +// } +// self.single_qubit_gates.insert(gate.to_string(), gatetimes); +// } +// self +// } + +// /// Function that allows to set the gate time for the two-qubit-gates +// /// considered as connected in the GenericGrid. +// /// +// /// # Arguments +// /// +// /// * `gate` - hqslang name of the two-qubit-gate. +// /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. +// /// +// /// # Returns +// /// +// /// A GenericGrid with updated gate times. +// /// +// pub fn set_all_two_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { +// if self.two_qubit_gates.get(&gate.to_string()).is_some() { +// let mut times: Vec = Vec::new(); +// for row in 0..(self.number_rows) { +// for column in 0..(self.number_columns) { +// let qubit = row * self.number_columns + column; +// if column < self.number_columns - 1 { +// let map1 = TwoQubitMap { +// control: qubit, +// target: qubit + 1, +// time: gate_time, +// }; +// let map2 = TwoQubitMap { +// control: qubit + 1, +// target: qubit, +// time: gate_time, +// }; +// times.push(map1); +// times.push(map2); +// } +// if row < self.number_rows - 1 { +// let map1 = TwoQubitMap { +// control: qubit, +// target: qubit + self.number_columns, +// time: gate_time, +// }; +// let map2 = TwoQubitMap { +// control: qubit + self.number_columns, +// target: qubit, +// time: gate_time, +// }; +// times.push(map1); +// times.push(map2); +// } +// } +// } +// self.two_qubit_gates.insert(gate.to_string(), times); +// } +// self +// } + +// /// Function that allows to set the gate time for the multi-qubit-gates in the GenericGrid. +// /// In the GenericGrid device the provided `gate_time` is set for the given `gate` +// /// for all qubits considered to be in one row and all qubits in one column. +// /// +// /// # Arguments +// /// +// /// * `gate` - hqslang name of the multi-qubit-gate. +// /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. +// /// +// /// # Returns +// /// +// /// A GenericGrid with updated gate times. +// /// +// pub fn set_all_multi_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { +// let number_qubits = self.number_rows * self.number_columns; +// if self.multi_qubit_gates.get(&gate.to_string()).is_some() { +// let mut times: Vec = Vec::new(); +// let mut qubits: Vec> = Vec::new(); +// // collect qubits per row +// for m in (0..number_qubits).step_by(self.number_columns) { +// let vec: Vec = (m..m + self.number_columns).collect(); +// qubits.push(vec); +// } +// // collect qubits per column +// for n in 0..self.number_columns { +// let mut column: Vec = Vec::new(); +// for row in 0..self.number_rows { +// column.push(n + row * self.number_columns); +// } +// qubits.push(column); +// } +// // fill empty times for all collected qubit constellations +// for item in qubits { +// let map = MultiQubitMap { +// qubits: item, +// time: gate_time, +// }; +// times.push(map); +// } +// self.multi_qubit_gates.insert(gate.to_string(), times); +// } +// self +// } + +// /// Function to set the decoherence rates for all qubits in the device. +// /// +// /// # Arguments +// /// +// /// * `rates` - decoherence rates for the qubits in the device, provided as a (3x3)-matrix. +// /// +// /// # Returns +// /// +// /// * `Ok(Self)` - The device with updated decoherence rates. +// /// * `Err(RoqoqoError)` - The input parameter `rates` needs to be a (3x3)-matrix. +// /// +// pub fn set_all_qubit_decoherence_rates( +// mut self, +// rates: Array2, +// ) -> Result { +// // Check if input matrix has the dimension (3x3) +// let shape = &(*rates.shape()); +// if shape == [3, 3] { +// for qubit in 0..self.number_qubits() { +// self.decoherence_rates.insert(qubit, rates.clone()); +// } +// Ok(self) +// } else { +// Err(RoqoqoError::GenericError { +// msg: "The input parameter `rates` needs to be a (3x3)-matrix.".to_string(), +// }) +// } +// } + +// /// Returns the number of rows in the device. +// /// +// /// # Returns +// /// +// /// The number of rows in the device. +// /// +// pub fn number_rows(&self) -> usize { +// self.number_rows +// } + +// /// Returns the number of columns in the device. +// /// +// /// # Returns +// /// +// /// The number of columns in the device. +// /// +// pub fn number_columns(&self) -> usize { +// self.number_columns +// } +// } + +// /// Implements Device trait for GenericGrid. +// /// +// /// The Device trait defines standard functions available for roqoqo devices. +// /// +// impl Device for GenericGrid { +// /// Returns the number of qubits in the device. +// /// +// /// # Returns +// /// +// /// The number of qubits in the device. +// /// +// fn number_qubits(&self) -> usize { +// self.number_rows * self.number_columns +// } + +// /// Returns the gate time of a single qubit operation if the single qubit operation is available on device. +// /// +// /// # 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 { +// match self.single_qubit_gates.get(&hqslang.to_string()) { +// Some(x) => { +// let mut item = x.iter().filter(|item| item.qubit == *qubit); +// item.next().map(|y| y.time) +// } +// None => None, +// } +// } + +// /// Returns the gate time of a two qubit operation if the two qubit operation is available on device-. +// /// +// /// +// /// # Arguments +// /// +// /// * `hqslang` - The hqslang name of a two qubit gate. +// /// * `control` - The control qubit the gate acts on +// /// * `target` - The target qubit the gate acts on +// /// +// /// # Returns +// /// +// /// * `Some` - The gate time. +// /// * `None` - The gate is not available on the device. +// /// +// fn two_qubit_gate_time(&self, hqslang: &str, control: &usize, target: &usize) -> Option { +// match self.two_qubit_gates.get(&hqslang.to_string()) { +// Some(x) => { +// let mut item = x +// .iter() +// .filter(|item| item.control == *control && item.target == *target); +// item.next().map(|y| y.time) +// } +// None => None, +// } +// } + +// /// Returns the gate time of a multi qubit operation if the multi qubit operation is available on device. +// /// +// /// +// /// # Arguments +// /// +// /// * `hqslang` - The hqslang name of a multi qubit gate. +// /// * `qubits` - The qubits the gate acts on +// /// +// /// # Returns +// /// +// /// * `Some` - The gate time. +// /// * `None` - The gate is not available on the device. +// /// +// fn multi_qubit_gate_time(&self, hqslang: &str, qubits: &[usize]) -> Option { +// match self.multi_qubit_gates.get(&hqslang.to_string()) { +// Some(x) => { +// let mut item = x.iter().filter(|item| item.qubits == *qubits); +// item.next().map(|y| y.time) +// } +// None => None, +// } +// } + +// /// 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_decoherence_rates(&self, qubit: &usize) -> Option> { +// self.decoherence_rates +// .get(qubit) +// .map(|rates| rates.to_owned()) +// } + +// /// Returns the list of pairs of qubits linked with a native two-qubit-gate in the device. +// /// +// /// A pair of qubits is considered linked by a native two-qubit-gate if the device +// /// can implement a two-qubit-gate between the two qubits without decomposing it +// /// into a sequence of gates that involves a third qubit of the device. +// /// The two-qubit-gate also has to form a universal set together with the available +// /// single qubit gates. +// /// +// /// The returned vectors is a simple, graph-library independent, representation of +// /// the undirected connectivity graph of the device. +// /// It can be used to construct the connectivity graph in a graph library of the users +// /// choice from a list of edges and can be used for applications like routing in quantum algorithms. +// /// +// /// # Returns +// /// +// /// A list (Vec) of pairs of qubits linked with a native two-qubit-gate in the device. +// /// +// fn two_qubit_edges(&self) -> Vec<(usize, usize)> { +// let mut vector: Vec<(usize, usize)> = Vec::new(); +// for row in 0..(self.number_rows) { +// for column in 0..(self.number_columns) { +// let qubit = row * self.number_columns + column; +// if column < self.number_columns - 1 { +// vector.push((qubit, qubit + 1)); +// } +// if row < self.number_rows - 1 { +// vector.push((qubit, qubit + self.number_columns)); +// } +// } +// } +// vector +// } +// } + +// /// A generic device containing a linear chain of qubits with next neighbour connectivity. +// /// +// #[derive(Clone, Debug, PartialEq)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub struct GenericChain { +// number_qubits: usize, +// single_qubit_gates: HashMap>, +// two_qubit_gates: HashMap>, +// multi_qubit_gates: HashMap, +// decoherence_rates: HashMap>, +// } + +// impl GenericChain { +// /// Create new GenericChain. +// /// +// /// # Arguments +// /// +// /// * `number_qubits` - The number of qubits in the device. +// /// * `single_qubit_gates` - A list of 'hqslang' names of single-qubit-gates supported by the device. +// /// * `two_qubit_gates` - A list of 'hqslang' names of basic two-qubit-gates supported by the device. +// // * `multi_qubit_gates` - A list of 'hqslang' names of basic multi-qubit-gates supported by the device. +// /// +// /// # Returns +// /// +// /// An initiated GenericChain with empty gate times and decoherence rates set to zero. +// /// +// pub fn new( +// number_qubits: usize, +// single_qubit_gates: &[String], +// two_qubit_gates: &[String], +// multi_qubit_gates: &[String], +// ) -> Self { +// // Initialization of single qubit gates with empty times +// let mut single_qubit_gate_map: HashMap> = HashMap::new(); +// for gate in single_qubit_gates.iter() { +// let mut empty_times: Vec = Vec::new(); +// for qubit in 0..number_qubits { +// let qubittime = SingleQubitMap { qubit, time: 0.0 }; +// empty_times.push(qubittime); +// } +// single_qubit_gate_map.insert(gate.clone(), empty_times); +// } + +// // Initialization of two qubit gates with empty times +// let mut two_qubit_gate_map: HashMap> = HashMap::new(); +// for gate in two_qubit_gates.iter() { +// let mut empty_times: Vec = Vec::new(); +// for qubit in 0..number_qubits - 1 { +// let qubittime1 = TwoQubitMap { +// control: qubit, +// target: qubit + 1, +// time: 0.0, +// }; +// let qubittime2 = TwoQubitMap { +// control: qubit + 1, +// target: qubit, +// time: 0.0, +// }; +// empty_times.push(qubittime1); +// empty_times.push(qubittime2); +// } +// two_qubit_gate_map.insert(gate.clone(), empty_times); +// } + +// // Initilization of multi qubit gates with empty times when applied to any qubits +// let mut multi_qubit_gate_map: HashMap = HashMap::new(); +// for gate in multi_qubit_gates.iter() { +// multi_qubit_gate_map.insert(gate.clone(), 0.0); +// } + +// let mut decoherence_rates: HashMap> = HashMap::new(); +// for qubit0 in 0..number_qubits { +// decoherence_rates.insert(qubit0, Array2::::zeros((3, 3))); +// } + +// GenericChain { +// number_qubits, +// single_qubit_gates: single_qubit_gate_map, +// two_qubit_gates: two_qubit_gate_map, +// multi_qubit_gates: multi_qubit_gate_map, +// decoherence_rates, +// } +// } + +// /// Function that allows to set one gate time per gate type for the single-qubit-gates. +// /// +// /// # Arguments +// /// +// /// * `gate` - hqslang name of the single-qubit-gate. +// /// * `gate_time` - gate time for the given gate type, valid for all qubits in the device. +// /// +// /// # Returns +// /// +// /// An GenericChain with updated gate times. +// /// +// pub fn set_all_single_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { +// if self.single_qubit_gates.get(&gate.to_string()).is_some() { +// let mut gatetimes: Vec = Vec::new(); +// for qubit in 0..self.number_qubits() { +// let qubittime = SingleQubitMap { +// qubit, +// time: gate_time, +// }; +// gatetimes.push(qubittime); +// } +// self.single_qubit_gates.insert(gate.to_string(), gatetimes); +// } +// self +// } + +// /// Function that allows to set the gate time for the two-qubit-gates in GenericChain. +// /// +// /// # Arguments +// /// +// /// * `gate` - hqslang name of the two-qubit-gate. +// /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. +// /// +// /// # Returns +// /// +// /// An GenericChain with updated gate times. +// /// +// pub fn set_all_two_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { +// if self.two_qubit_gates.get(&gate.to_string()).is_some() { +// let mut times: Vec = Vec::new(); +// for qubit in 0..self.number_qubits - 1 { +// let map1 = TwoQubitMap { +// control: qubit, +// target: qubit + 1, +// time: gate_time, +// }; +// let map2 = TwoQubitMap { +// control: qubit + 1, +// target: qubit, +// time: gate_time, +// }; +// times.push(map1); +// times.push(map2); +// } +// self.two_qubit_gates.insert(gate.to_string(), times); +// } +// self +// } + +// /// Function that allows to set the gate time for the multi-qubit-gates in GenericChain, +// /// when applied to any qubits in the device. +// /// +// /// # Arguments +// /// +// /// * `gate` - hqslang name of the multi-qubit-gate. +// /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. +// /// +// /// # Returns +// /// +// /// An GenericChain with updated gate times. +// /// +// pub fn set_all_multi_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { +// if self.multi_qubit_gates.get(&gate.to_string()).is_some() { +// self.multi_qubit_gates.insert(gate.to_string(), gate_time); +// } +// self +// } + +// /// Function to set the decoherence rates for all qubits in the device. +// /// +// /// # Arguments +// /// +// /// * `rates` - decoherence rates for the qubits in the device, provided as a (3x3)-matrix. +// /// +// /// # Returns +// /// +// /// * `Ok(Self)` - The device with updated decoherence rates. +// /// * `Err(RoqoqoError)` - The input parameter `rates` needs to be a (3x3)-matrix. +// /// +// pub fn set_all_qubit_decoherence_rates( +// mut self, +// rates: Array2, +// ) -> Result { +// // Check if input matrix has the dimension (3x3) +// let shape = &(*rates.shape()); +// if shape == [3, 3] { +// for qubit in 0..self.number_qubits() { +// self.decoherence_rates.insert(qubit, rates.clone()); +// } +// Ok(self) +// } else { +// Err(RoqoqoError::GenericError { +// msg: "The input parameter `rates` needs to be a (3x3)-matrix.".to_string(), +// }) +// } +// } +// } + +// /// Implements Device trait for GenericChain. +// /// +// /// The Device trait defines standard functions available for roqoqo devices. +// /// +// impl Device for GenericChain { +// /// Returns the number of qubits the device supports. +// /// +// /// # Returns +// /// +// /// The number of qubits in the device. +// /// +// fn number_qubits(&self) -> usize { +// self.number_qubits +// } + +// /// 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 { +// match self.single_qubit_gates.get(&hqslang.to_string()) { +// Some(x) => { +// let mut item = x.iter().filter(|item| item.qubit == *qubit); +// item.next().map(|y| y.time) +// } +// None => None, +// } +// } + +// /// Returns the gate time of a two qubit operation if the two qubit operation is available on device. +// /// +// /// +// /// # Arguments +// /// +// /// * `hqslang` - The hqslang name of a two qubit gate. +// /// * `control` - The control qubit the gate acts on +// /// * `target` - The target qubit the gate acts on +// /// +// /// # Returns +// /// +// /// * `Some` - The gate time. +// /// * `None` - The gate is not available on the device. +// /// +// fn two_qubit_gate_time(&self, hqslang: &str, control: &usize, target: &usize) -> Option { +// match self.two_qubit_gates.get(&hqslang.to_string()) { +// Some(x) => { +// let mut item = x +// .iter() +// .filter(|item| item.control == *control && item.target == *target); +// item.next().map(|y| y.time) +// } +// None => None, +// } +// } + +// /// Returns the gate time of a multi qubit operation if the multi qubit operation is available on device. +// /// Note: in GenericChain the gate time of multi qubit gates is treated uniformly for all qubits. +// /// +// /// +// /// # Arguments +// /// +// /// * `hqslang` - The hqslang name of a multi qubit gate. +// /// * `qubits` - The qubits the gate acts on. +// /// +// /// +// /// # Returns +// /// +// /// * `Some` - The gate time. +// /// * `None` - The gate is not available on the device. +// /// +// fn multi_qubit_gate_time(&self, hqslang: &str, qubits: &[usize]) -> Option { +// // variable unused in GenericChain, is kept here for consistency purposes. +// let _qubits = qubits; +// self.multi_qubit_gates.get(&hqslang.to_string()).copied() +// } + +// /// 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_2 = \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_decoherence_rates(&self, qubit: &usize) -> Option> { +// self.decoherence_rates +// .get(qubit) +// .map(|rates| rates.to_owned()) +// } + +// /// Returns the list of pairs of qubits linked with a native two-qubit-gate in the device. +// /// +// /// A pair of qubits is considered linked by a native two-qubit-gate if the device +// /// can implement a two-qubit-gate between the two qubits without decomposing it +// /// into a sequence of gates that involves a third qubit of the device. +// /// The two-qubit-gate also has to form a universal set together with the available +// /// single qubit gates. +// /// +// /// The returned vectors is a simple, graph-library independent, representation of +// /// the undirected connectivity graph of the device. +// /// It can be used to construct the connectivity graph in a graph library of the users +// /// choice from a list of edges and can be used for applications like routing in quantum algorithms. +// /// +// /// # Returns +// /// +// /// A list (Vec) of pairs of qubits linked with a native two-qubit-gate in the device. +// /// +// fn two_qubit_edges(&self) -> Vec<(usize, usize)> { +// let mut vector: Vec<(usize, usize)> = Vec::new(); +// for qubit in 0..self.number_qubits() - 1 { +// vector.push((qubit, qubit + 1)); +// } +// vector +// } +// } + +// /// A device struct with public fields for a roqoqo device +// /// with all-to-all connectivity between all involved qubits. +// /// +// #[derive(Clone, Debug, PartialEq)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub struct GenericDevice { +// /// The number of qubits in the device. +// pub number_qubits: usize, +// /// A map including 'hqslang' names of single-qubit-gates supported by the device, +// /// with the according gate times specific for the particular qubit ([SingleQubitMap]). +// pub single_qubit_gates: HashMap>, +// /// A map including 'hqslang' names of two-qubit-gates supported by the device, +// /// with the according gate times specific for the particular qubit ([TwoQubitMap]). +// pub two_qubit_gates: HashMap>, +// /// A map including 'hqslang' names of multi-qubit-gates supported by the device, +// /// with the according gate times uniform for all qubits in the device. +// pub multi_qubit_gates: HashMap, +// /// A (3x3)-matrix of the decoherence rates. +// pub decoherence_rates: HashMap>, +// } + +// impl GenericDevice { +// /// Create new GenericDevice. +// /// +// /// # Arguments +// /// +// /// * `number_qubits` - The number of qubits in the device. +// /// * `single_qubit_gates` - A list of 'hqslang' names of single-qubit-gates supported by the device. +// /// * `two_qubit_gates` - A list of 'hqslang' names of basic two-qubit-gates supported by the device. +// /// * `multi_qubit_gates` - A list of 'hqslang' names of basic multi-qubit-gates supported by the device. +// /// +// /// # Returns +// /// +// /// An initiated GenericDevice with empty gate times and decoherence rates set to zero. +// /// +// pub fn new( +// number_qubits: usize, +// single_qubit_gates: &[String], +// two_qubit_gates: &[String], +// multi_qubit_gates: &[String], +// ) -> Self { +// // Initialization of single qubit gates with empty times +// let mut single_qubit_gate_map: HashMap> = HashMap::new(); +// for gate in single_qubit_gates.iter() { +// let mut empty_times: Vec = Vec::new(); +// for qubit in 0..number_qubits { +// let qubittime = SingleQubitMap { qubit, time: 0.0 }; +// empty_times.push(qubittime); +// } +// single_qubit_gate_map.insert(gate.clone(), empty_times); +// } + +// // Initialization of two qubit gates with empty times +// let mut two_qubit_gate_map: HashMap> = HashMap::new(); +// for gate in two_qubit_gates.iter() { +// let mut empty_times: Vec = Vec::new(); +// for qubit0 in 0..number_qubits { +// for qubit1 in 0..number_qubits { +// if qubit0 != qubit1 { +// let qubittime1 = TwoQubitMap { +// control: qubit0, +// target: qubit1, +// time: 0.0, +// }; +// let qubittime2 = TwoQubitMap { +// control: qubit1, +// target: qubit0, +// time: 0.0, +// }; +// empty_times.push(qubittime1); +// empty_times.push(qubittime2); +// } +// } +// } +// two_qubit_gate_map.insert(gate.clone(), empty_times); +// } + +// // Initilization of multi qubit gates with empty times when applied to any qubits +// let mut multi_qubit_gate_map: HashMap = HashMap::new(); +// for gate in multi_qubit_gates.iter() { +// multi_qubit_gate_map.insert(gate.clone(), 0.0); +// } + +// let mut decoherence_rates: HashMap> = HashMap::new(); +// for qubit0 in 0..number_qubits { +// decoherence_rates.insert(qubit0, Array2::::zeros((3, 3))); +// } + +// GenericDevice { +// number_qubits, +// single_qubit_gates: single_qubit_gate_map, +// two_qubit_gates: two_qubit_gate_map, +// multi_qubit_gates: multi_qubit_gate_map, +// decoherence_rates, +// } +// } + +// /// Function that allows to set one gate time per gate type for the single-qubit-gates. +// /// +// /// # Arguments +// /// +// /// * `gate` - hqslang name of the single-qubit-gate. +// /// * `gate_time` - gate time for the given gate type, valid for all qubits in the device. +// /// +// /// # Returns +// /// +// /// An GenericDevice with updated gate times. +// /// +// pub fn set_all_single_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { +// if self.single_qubit_gates.get(&gate.to_string()).is_some() { +// let mut gatetimes: Vec = Vec::new(); +// for qubit in 0..self.number_qubits() { +// let qubittime = SingleQubitMap { +// qubit, +// time: gate_time, +// }; +// gatetimes.push(qubittime); +// } +// self.single_qubit_gates.insert(gate.to_string(), gatetimes); +// } +// self +// } + +// /// Function that allows to set the gate time for the two-qubit-gates in GenericDevice. +// /// +// /// # Arguments +// /// +// /// * `gate` - hqslang name of the two-qubit-gate. +// /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. +// /// +// /// # Returns +// /// +// /// An GenericDevice with updated gate times. +// /// +// pub fn set_all_two_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { +// if self.two_qubit_gates.get(&gate.to_string()).is_some() { +// let mut times: Vec = Vec::new(); +// for qubit0 in 0..self.number_qubits { +// for qubit1 in 0..self.number_qubits { +// if qubit0 != qubit1 { +// let map1 = TwoQubitMap { +// control: qubit0, +// target: qubit1, +// time: gate_time, +// }; +// let map2 = TwoQubitMap { +// control: qubit1, +// target: qubit0, +// time: gate_time, +// }; +// times.push(map1); +// times.push(map2); +// } +// } +// } +// self.two_qubit_gates.insert(gate.to_string(), times); +// } +// self +// } + +// /// Function that allows to set the gate time for the multi-qubit-gates in GenericDevice, +// /// when applied to any qubits in the device. +// /// +// /// # Arguments +// /// +// /// * `gate` - hqslang name of the multi-qubit-gate. +// /// * `gate_time` - gate time for the given gate, valid for all qubits in the device. +// /// +// /// # Returns +// /// +// /// An GenericDevice with updated gate times. +// /// +// pub fn set_all_multi_qubit_gate_times(mut self, gate: &str, gate_time: f64) -> Self { +// if self.multi_qubit_gates.get(&gate.to_string()).is_some() { +// self.multi_qubit_gates.insert(gate.to_string(), gate_time); +// } +// self +// } + +// /// Function to set the decoherence rates for all qubits in the device. +// /// +// /// # Arguments +// /// +// /// * `rates` - decoherence rates for the qubits in the device, provided as a (3x3)-matrix. +// /// +// /// # Returns +// /// +// /// * `Ok(Self)` - The device with updated decoherence rates. +// /// * `Err(RoqoqoError)` - The input parameter `rates` needs to be a (3x3)-matrix. +// /// +// pub fn set_all_qubit_decoherence_rates( +// mut self, +// rates: Array2, +// ) -> Result { +// // Check if input matrix has the dimension (3x3) +// let shape = &(*rates.shape()); +// if shape == [3, 3] { +// for qubit in 0..self.number_qubits() { +// self.decoherence_rates.insert(qubit, rates.clone()); +// } +// Ok(self) +// } else { +// Err(RoqoqoError::GenericError { +// msg: "The input parameter `rates` needs to be a (3x3)-matrix.".to_string(), +// }) +// } +// } +// } + +// /// Implements Device trait for GenericDevice. +// /// +// /// The Device trait defines standard functions available for roqoqo devices. +// /// +// impl Device for GenericDevice { +// /// Returns the number of qubits the device supports. +// /// +// /// # Returns +// /// +// /// The number of qubits in the device. +// /// +// fn number_qubits(&self) -> usize { +// self.number_qubits +// } + +// /// 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 { +// match self.single_qubit_gates.get(&hqslang.to_string()) { +// Some(x) => { +// let mut item = x.iter().filter(|item| item.qubit == *qubit); +// item.next().map(|y| y.time) +// } +// None => None, +// } +// } + +// /// Returns the gate time of a two qubit operation if the two qubit operation is available on device. +// /// +// /// +// /// # Arguments +// /// +// /// * `hqslang` - The hqslang name of a two qubit gate. +// /// * `control` - The control qubit the gate acts on +// /// * `target` - The target qubit the gate acts on +// /// +// /// # Returns +// /// +// /// * `Some` - The gate time. +// /// * `None` - The gate is not available on the device. +// /// +// fn two_qubit_gate_time(&self, hqslang: &str, control: &usize, target: &usize) -> Option { +// match self.two_qubit_gates.get(&hqslang.to_string()) { +// Some(x) => { +// let mut item = x +// .iter() +// .filter(|item| item.control == *control && item.target == *target); +// item.next().map(|y| y.time) +// } +// None => None, +// } +// } + +// /// Returns the gate time of a multi qubit operation if the multi qubit operation is available on device. +// /// Note: in GenericDevice the gate time of multi qubit gates is treated uniformly for all qubits. +// /// +// /// +// /// # Arguments +// /// +// /// * `hqslang` - The hqslang name of a multi qubit gate. +// /// * `qubits` - The qubits the gate acts on. +// /// +// /// +// /// # Returns +// /// +// /// * `Some` - The gate time. +// /// * `None` - The gate is not available on the device. +// /// +// fn multi_qubit_gate_time(&self, hqslang: &str, qubits: &[usize]) -> Option { +// // variable unused in GenericDevice, is kept here for consistency purposes. +// let _qubits = qubits; +// self.multi_qubit_gates.get(&hqslang.to_string()).copied() +// } + +// /// 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_2 = \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_decoherence_rates(&self, qubit: &usize) -> Option> { +// self.decoherence_rates +// .get(qubit) +// .map(|rates| rates.to_owned()) +// } + +// /// Returns the list of pairs of qubits linked with a native two-qubit-gate in the device. +// /// +// /// A pair of qubits is considered linked by a native two-qubit-gate if the device +// /// can implement a two-qubit-gate between the two qubits without decomposing it +// /// into a sequence of gates that involves a third qubit of the device. +// /// The two-qubit-gate also has to form a universal set together with the available +// /// single qubit gates. +// /// +// /// The returned vectors is a simple, graph-library independent, representation of +// /// the undirected connectivity graph of the device. +// /// It can be used to construct the connectivity graph in a graph library of the users +// /// choice from a list of edges and can be used for applications like routing in quantum algorithms. +// /// +// /// # Returns +// /// +// /// A list (Vec) of pairs of qubits linked with a native two-qubit-gate in the device. +// /// +// fn two_qubit_edges(&self) -> Vec<(usize, usize)> { +// let mut vector: Vec<(usize, usize)> = Vec::new(); +// for row in 0..self.number_qubits() { +// for column in row + 1..self.number_qubits() { +// vector.push((row, column)); +// } +// } +// vector +// } +// } + +// /// A customized struct to use as a value in the HashMap for single_qubit_gates +// /// to access the gate times. +// /// +// #[derive(Clone, Debug, PartialEq)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub struct SingleQubitMap { +// ///The index of the qubit for which the gate time is set. +// pub qubit: usize, +// /// Gate time for the given qubit for the single qubit gate. +// pub time: f64, +// } + +// /// A customized struct to use as a value in the HashMap for two_qubit_gates +// /// to access the gate times. +// /// +// #[derive(Clone, Debug, PartialEq)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub struct TwoQubitMap { +// /// The index of the control qubit for which the gate time is set. +// pub control: usize, +// /// The index of the target qubit for which the gate time is set. +// pub target: usize, +// /// Gate time for the given qubits for the two qubit gate. +// pub time: f64, +// } + +// #[doc(hidden)] +// /// A customized struct to use as a value in the HashMap for multi_qubit_gates +// /// to access the gate times. +// /// +// #[derive(Clone, Debug, PartialEq)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub struct MultiQubitMap { +// /// A list of qubit indices for which the gate time is set. +// pub qubits: Vec, +// /// Gate time for the given qubits for the multi qubit gate. +// pub time: f64, +// } diff --git a/roqoqo/src/lib.rs b/roqoqo/src/lib.rs index 9d07d134..7e9a77f5 100644 --- a/roqoqo/src/lib.rs +++ b/roqoqo/src/lib.rs @@ -304,10 +304,10 @@ pub use circuit::Circuit; #[doc(hidden)] pub use circuit::*; pub mod backends; +pub mod devices; +pub mod measurements; pub mod operations; pub mod prelude; -// pub mod devices; -pub mod measurements; #[doc(hidden)] mod quantum_program; pub mod registers; diff --git a/roqoqo/tests/integration/devices.rs b/roqoqo/tests/integration/devices.rs index 62ffba5c..636e0791 100644 --- a/roqoqo/tests/integration/devices.rs +++ b/roqoqo/tests/integration/devices.rs @@ -13,7 +13,7 @@ use ndarray::{array, Array2}; use roqoqo::devices::*; use std::collections::HashMap; -use test_case::test_case; +// use test_case::test_case; #[derive(Debug, Clone, PartialEq)] struct TestDevice { @@ -195,640 +195,640 @@ fn change_device_test() { assert!(result.is_err()); } -/// Test new() function for AllToAllDevice -#[test] -fn alltoalldevice_new() { - let number_qubits = 3usize; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &[]; - let device = AllToAllDevice::new( - number_qubits, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - // Test number of qubits - assert_eq!(device.number_qubits(), number_qubits); - // Test that available single-qubit-gate is initialized with gate time set to zero - assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.0)); - // Test that for non-available gates the returned gate time is Non - assert_eq!(device.single_qubit_gate_time("RotateY", &0), None); - - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.0)); - assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.0)); - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); - assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); - - assert_eq!( - device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), - None, - ); - - let empty_rates = array![[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]; - assert_eq!(device.qubit_decoherence_rates(&0), Some(empty_rates)); -} - -/// Test set gate time functions for AllToAllDevice -#[test] -fn test_alltoalldevice_settimes() { - let number_qubits = 3usize; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &[]; - let mut device = AllToAllDevice::new( - number_qubits, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - device = device.set_all_single_qubit_gate_times(&"RotateX", 0.07); - device = device.set_all_single_qubit_gate_times(&"RotateZ", 0.1); - - device = device.set_all_two_qubit_gate_times(&"CNOT", 0.05); - - device = device.set_all_multi_qubit_gate_times(&"test", 0.0); - - assert_eq!(device.single_qubit_gate_time("RotateX", &0), Some(0.07f64)); - assert_eq!( - device.single_qubit_gate_time("RotateX", &number_qubits), - None - ); - assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.1f64)); - - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.05f64)); - assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.05f64)); - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); - assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); - - assert_eq!( - device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), - None, - ); -} - -// Test set decoherence and two_qubit_edges for AllToAllDevice -#[test] -fn test_alltoalldevice_setattributes() { - let number_qubits = 3usize; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &[]; - let mut device = AllToAllDevice::new( - number_qubits, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - let rates_invalid = array![[0.1], [0.2], [0.3]]; - let error = device - .clone() - .set_all_qubit_decoherence_rates(rates_invalid.clone()); - assert!(error.is_err()); - - let rates = array![[0.1, 0.1, 0.1], [0.2, 0.2, 0.2], [0.3, 0.3, 0.3]]; - device = device - .set_all_qubit_decoherence_rates(rates.clone()) - .unwrap(); - assert_eq!(device.qubit_decoherence_rates(&1), Some(rates)); - assert_eq!(device.qubit_decoherence_rates(&number_qubits), None); - - let test_edges = vec![(0, 1), (0, 2), (1, 2)]; - let edges = device.two_qubit_edges(); - assert_eq!(test_edges, edges); -} - -/// Test new() function for GenericChain device -#[test] -fn genericchain_new() { - let number_qubits = 3usize; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &[]; - let device = GenericChain::new( - number_qubits, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - // Test number of qubits - assert_eq!(device.number_qubits(), number_qubits); - // Test that available single-qubit-gate is initialized with gate time set to zero - assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.0)); - // Test that for non-available gates the returned gate time is Non - assert_eq!(device.single_qubit_gate_time("RotateY", &0), None); - - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.0)); - assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.0)); - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); - assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); - - assert_eq!( - device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), - None, - ); - - let empty_rates = array![[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]; - assert_eq!(device.qubit_decoherence_rates(&0), Some(empty_rates)); -} - -/// Test set gate time functions for GenericChain device -#[test] -fn test_genericchain_settimes() { - let number_qubits = 3usize; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &[]; - let mut device = GenericChain::new( - number_qubits, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - device = device.set_all_single_qubit_gate_times(&"RotateX", 0.07); - device = device.set_all_single_qubit_gate_times(&"RotateZ", 0.1); - - device = device.set_all_two_qubit_gate_times(&"CNOT", 0.05); - - assert_eq!(device.single_qubit_gate_time("RotateX", &0), Some(0.07f64)); - assert_eq!( - device.single_qubit_gate_time("RotateX", &number_qubits), - None - ); - assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.1f64)); - - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.05f64)); - assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.05f64)); - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &2), None); - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); - assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); - - assert_eq!( - device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), - None, - ); -} - -// Test set decoherence and two_qubit_edges for GenericChain device -#[test] -fn test_genericchain_setattributes() { - let number_qubits = 3usize; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &[]; - let mut device = GenericChain::new( - number_qubits, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - let rates_invalid = array![[0.1], [0.2], [0.3]]; - let error = device - .clone() - .set_all_qubit_decoherence_rates(rates_invalid.clone()); - assert!(error.is_err()); - - let rates = array![[0.1, 0.1, 0.1], [0.2, 0.2, 0.2], [0.3, 0.3, 0.3]]; - device = device - .set_all_qubit_decoherence_rates(rates.clone()) - .unwrap(); - assert_eq!(device.qubit_decoherence_rates(&1), Some(rates)); - assert_eq!(device.qubit_decoherence_rates(&number_qubits), None); - - let test_edges = vec![(0, 1), (1, 2)]; - let edges = device.two_qubit_edges(); - assert_eq!(test_edges, edges); -} - -/// Test new() function for GenericGrid -#[test] -fn genericgrid_new() { - let number_rows = 3usize; - let number_columns = 4usize; - let number_qubits = number_rows * number_columns; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &[]; - let device = GenericGrid::new( - number_rows, - number_columns, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - // Test number of qubits - assert_eq!(device.number_qubits(), number_qubits); - // Test that available single-qubit-gate is initialized with gate time set to zero - assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.0)); - // Test that for non-available gates the returned gate time is Non - assert_eq!(device.single_qubit_gate_time("RotateY", &0), None); - - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.0)); - assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.0)); - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &5), None); - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); - assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); - - assert_eq!( - device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), - None, - ); - - let empty_rates = array![[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]; - assert_eq!(device.qubit_decoherence_rates(&0), Some(empty_rates)); -} - -/// Test set gate time functions for GenericGrid -#[test] -fn test_genericgrid_settimes() { - let number_rows = 3usize; - let number_columns = 4usize; - let number_qubits = number_rows * number_columns; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &["MultiQubitMS".to_string()]; - let mut device = GenericGrid::new( - number_rows, - number_columns, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - device = device.set_all_single_qubit_gate_times(&"RotateX", 0.07); - device = device.set_all_single_qubit_gate_times(&"RotateZ", 0.1); - - device = device.set_all_two_qubit_gate_times(&"CNOT", 0.05); - device = device.set_all_multi_qubit_gate_times(&"MultiQubitMS", 0.2); - - assert_eq!(device.single_qubit_gate_time("RotateX", &0), Some(0.07f64)); - assert_eq!( - device.single_qubit_gate_time("RotateX", &number_qubits), - None - ); - assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.1f64)); - - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.05f64)); - assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.05f64)); - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &3), None); - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); - assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); - - // test for all qubits in 2nd column - assert_eq!( - device.multi_qubit_gate_time("MultiQubitMS", &[1, 5, 9]), - Some(0.2f64), - ); - // test for all qubits in 3rd row - assert_eq!( - device.multi_qubit_gate_time("MultiQubitMS", &[8, 9, 10, 11]), - Some(0.2f64), - ); - // test a combination not covered by the standard function - assert_eq!( - device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), - None, - ); -} - -// Test set decoherence for GenericGrid -#[test] -fn test_genericgrid_setattributes() { - let number_rows = 3usize; - let number_columns = 4usize; - let number_qubits = number_rows * number_columns; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &[]; - let mut device = GenericGrid::new( - number_rows, - number_columns, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - let rates_invalid = array![[0.1], [0.2], [0.3]]; - let error = device - .clone() - .set_all_qubit_decoherence_rates(rates_invalid.clone()); - assert!(error.is_err()); - - let rates = array![[0.1, 0.1, 0.1], [0.2, 0.2, 0.2], [0.3, 0.3, 0.3]]; - device = device - .set_all_qubit_decoherence_rates(rates.clone()) - .unwrap(); - assert_eq!(device.qubit_decoherence_rates(&1), Some(rates)); - assert_eq!(device.qubit_decoherence_rates(&number_qubits), None); -} - -// Test two_qubit_edges() for GenericGrid -#[test_case(3, 4, vec![(0, 1), (0, 4), (1, 2), (1, 5), (2, 3), (2, 6), (3, 7), (4, 5), (4, 8), (5, 6), (5, 9), (6, 7), (6, 10), (7, 11), (8, 9), (9, 10), (10, 11)]; "3_4")] -#[test_case(2, 3, vec![(0, 1),(0, 3),(1, 2),(1, 4),(2, 5), (3, 4), (4, 5)]; "2_3")] -fn test_genericgrid_edges(rows: usize, columns: usize, test_edges: Vec<(usize, usize)>) { - let number_rows = rows; - let number_columns = columns; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &[]; - let device = GenericGrid::new( - number_rows, - number_columns, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - let edges = device.two_qubit_edges(); - assert_eq!(test_edges, edges); -} - -/// Test new() function for GenericDevice -#[test] -fn genericdevice_new() { - let number_qubits = 3usize; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &[]; - let device = GenericDevice::new( - number_qubits, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - // Test number of qubits - assert_eq!(device.number_qubits(), number_qubits); - // Test that available single-qubit-gate is initialized with gate time set to zero - assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.0)); - // Test that for non-available gates the returned gate time is Non - assert_eq!(device.single_qubit_gate_time("RotateY", &0), None); - - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.0)); - assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.0)); - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); - assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); - - assert_eq!( - device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), - None, - ); - - let empty_rates = array![[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]; - assert_eq!(device.qubit_decoherence_rates(&0), Some(empty_rates)); -} - -/// Test public fields of the GenericDevice -#[test] -fn genericdevice_fields() { - let number_qubits = 3usize; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let mapvec: Vec = vec![ - SingleQubitMap { - qubit: 0, - time: 0.0, - }, - SingleQubitMap { - qubit: 1, - time: 0.0, - }, - SingleQubitMap { - qubit: 2, - time: 0.0, - }, - ]; - let mut single_qubit_gate_map: HashMap> = HashMap::new(); - for gate in single_qubit_gates { - single_qubit_gate_map.insert(gate.clone(), mapvec.clone()); - } - let mut two_qubit_gate_map: HashMap> = HashMap::new(); - two_qubit_gate_map.insert( - "CNOT".to_string(), - vec![ - TwoQubitMap { - control: 0, - target: 1, - time: 0.0, - }, - TwoQubitMap { - control: 1, - target: 2, - time: 0.0, - }, - ], - ); - let multi_qubit_gates: HashMap = HashMap::new(); - - let mut decoherence_rates: HashMap> = HashMap::new(); - for qubit0 in 0..number_qubits { - decoherence_rates.insert(qubit0, Array2::::zeros((3, 3))); - } - - let device = GenericDevice { - number_qubits: number_qubits.clone(), - single_qubit_gates: single_qubit_gate_map.clone(), - two_qubit_gates: two_qubit_gate_map.clone(), - multi_qubit_gates: multi_qubit_gates, - decoherence_rates: decoherence_rates.clone(), - }; - - // Test public fields of the GenericDevice - assert_eq!(device.number_qubits, number_qubits); - assert_eq!(device.single_qubit_gates, single_qubit_gate_map); - assert_eq!(device.two_qubit_gates, two_qubit_gate_map); - assert_eq!(device.multi_qubit_gates, HashMap::new()); - assert_eq!(device.decoherence_rates, decoherence_rates); -} - -/// Test set gate time functions for GenericDevice -#[test] -fn test_genericldevice_settimes() { - let number_qubits = 3usize; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &[]; - let mut device = GenericDevice::new( - number_qubits, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - device = device.set_all_single_qubit_gate_times(&"RotateX", 0.07); - device = device.set_all_single_qubit_gate_times(&"RotateZ", 0.1); - - device = device.set_all_two_qubit_gate_times(&"CNOT", 0.05); - device = device.set_all_multi_qubit_gate_times(&"test", 0.0); - - assert_eq!(device.single_qubit_gate_time("RotateX", &0), Some(0.07f64)); - assert_eq!( - device.single_qubit_gate_time("RotateX", &number_qubits), - None - ); - assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.1f64)); - - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.05f64)); - assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.05f64)); - assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); - assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); - - assert_eq!( - device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), - None, - ); -} - -// Test set decoherence and two_qubit_edges for GenericDevice -#[test] -fn test_genericdevice_setattributes() { - let number_qubits = 3usize; - let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; - let two_qubit_gates = &["CNOT".to_string()]; - let multi_qubit_gates = &[]; - let mut device = GenericDevice::new( - number_qubits, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - let rates_invalid = array![[0.1], [0.2], [0.3]]; - let error = device - .clone() - .set_all_qubit_decoherence_rates(rates_invalid.clone()); - assert!(error.is_err()); - - let rates = array![[0.1, 0.1, 0.1], [0.2, 0.2, 0.2], [0.3, 0.3, 0.3]]; - device = device - .set_all_qubit_decoherence_rates(rates.clone()) - .unwrap(); - assert_eq!(device.qubit_decoherence_rates(&1), Some(rates)); - assert_eq!(device.qubit_decoherence_rates(&number_qubits), None); - - let test_edges = vec![(0, 1), (0, 2), (1, 2)]; - let edges = device.two_qubit_edges(); - assert_eq!(test_edges, edges); -} - -// Very basic unit test for the fields in the created custom qubit map structs -#[test] -fn test_mapstructs() { - let singlequbitstruct = SingleQubitMap { - qubit: 0, - time: 4.3, - }; - let twoqubitstruct = TwoQubitMap { - control: 0, - target: 2, - time: 0.0, - }; - let qubits: Vec = vec![0, 1, 2, 3, 4]; - let multiqubitstruct = MultiQubitMap { - qubits: qubits.clone(), - time: 5.0, - }; - assert_eq!(singlequbitstruct.qubit, 0); - assert_eq!(singlequbitstruct.time, 4.3); - assert_eq!(twoqubitstruct.control, 0); - assert_eq!(twoqubitstruct.target, 2); - assert_eq!(twoqubitstruct.time, 0.0); - assert_eq!(multiqubitstruct.qubits, qubits); - assert_eq!(multiqubitstruct.time, 5.0); -} - -// Test Clone, PartialEq and Debug for GenericDevice -#[test] -fn test_genericdevice_derive() { - let number_qubits = 1usize; - let single_qubit_gates = &[]; - let two_qubit_gates = &[]; - let multi_qubit_gates = &[]; - let device = GenericDevice::new( - number_qubits, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - // Test debug - let debug = "GenericDevice { number_qubits: 1, single_qubit_gates: {}, two_qubit_gates: {}, multi_qubit_gates: {}, decoherence_rates: {0: [[0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2} }"; - assert_eq!(format!("{:?}", device.clone()), debug); - - // Test Clone and PartialEq - assert!(device.clone() == device); -} - -// Test Clone, PartialEq and Debug for GenericChain -#[test] -fn test_genericchain_derive() { - let number_qubits = 1usize; - let single_qubit_gates = &[]; - let two_qubit_gates = &[]; - let multi_qubit_gates = &[]; - let device = GenericChain::new( - number_qubits, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - // Test debug - let debug = "GenericChain { number_qubits: 1, single_qubit_gates: {}, two_qubit_gates: {}, multi_qubit_gates: {}, decoherence_rates: {0: [[0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2} }"; - assert_eq!(format!("{:?}", device.clone()), debug); - - // Test Clone and PartialEq - assert!(device.clone() == device); -} - -// Test Clone, PartialEq and Debug for AllToAllDevice -#[test] -fn test_alltoalldevice_derive() { - let number_qubits = 1usize; - let single_qubit_gates = &[]; - let two_qubit_gates = &[]; - let multi_qubit_gates = &[]; - let device = AllToAllDevice::new( - number_qubits, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - // Test debug - let debug = "AllToAllDevice { number_qubits: 1, single_qubit_gates: {}, two_qubit_gates: {}, multi_qubit_gates: {}, decoherence_rates: {0: [[0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2} }"; - assert_eq!(format!("{:?}", device.clone()), debug); - - // Test Clone and PartialEq - assert!(device.clone() == device); -} - -// Test Clone, PartialEq and Debug for GenericGrid -#[test] -fn test_genericgrid_derive() { - let number_rows = 1usize; - let number_columns = 1usize; - let single_qubit_gates = &[]; - let two_qubit_gates = &[]; - let multi_qubit_gates = &[]; - let device = GenericGrid::new( - number_rows, - number_columns, - single_qubit_gates, - two_qubit_gates, - multi_qubit_gates, - ); - - // Test debug - let debug = "GenericGrid { number_rows: 1, number_columns: 1, single_qubit_gates: {}, two_qubit_gates: {}, multi_qubit_gates: {}, decoherence_rates: {0: [[0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2} }"; - assert_eq!(format!("{:?}", device.clone()), debug); - - // Test Clone and PartialEq - assert!(device.clone() == device); -} +// /// Test new() function for AllToAllDevice +// #[test] +// fn alltoalldevice_new() { +// let number_qubits = 3usize; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &[]; +// let device = AllToAllDevice::new( +// number_qubits, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// // Test number of qubits +// assert_eq!(device.number_qubits(), number_qubits); +// // Test that available single-qubit-gate is initialized with gate time set to zero +// assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.0)); +// // Test that for non-available gates the returned gate time is Non +// assert_eq!(device.single_qubit_gate_time("RotateY", &0), None); + +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.0)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.0)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); +// assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); + +// assert_eq!( +// device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), +// None, +// ); + +// let empty_rates = array![[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]; +// assert_eq!(device.qubit_decoherence_rates(&0), Some(empty_rates)); +// } + +// /// Test set gate time functions for AllToAllDevice +// #[test] +// fn test_alltoalldevice_settimes() { +// let number_qubits = 3usize; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &[]; +// let mut device = AllToAllDevice::new( +// number_qubits, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// device = device.set_all_single_qubit_gate_times(&"RotateX", 0.07); +// device = device.set_all_single_qubit_gate_times(&"RotateZ", 0.1); + +// device = device.set_all_two_qubit_gate_times(&"CNOT", 0.05); + +// device = device.set_all_multi_qubit_gate_times(&"test", 0.0); + +// assert_eq!(device.single_qubit_gate_time("RotateX", &0), Some(0.07f64)); +// assert_eq!( +// device.single_qubit_gate_time("RotateX", &number_qubits), +// None +// ); +// assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.1f64)); + +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.05f64)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.05f64)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); +// assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); + +// assert_eq!( +// device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), +// None, +// ); +// } + +// // Test set decoherence and two_qubit_edges for AllToAllDevice +// #[test] +// fn test_alltoalldevice_setattributes() { +// let number_qubits = 3usize; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &[]; +// let mut device = AllToAllDevice::new( +// number_qubits, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// let rates_invalid = array![[0.1], [0.2], [0.3]]; +// let error = device +// .clone() +// .set_all_qubit_decoherence_rates(rates_invalid.clone()); +// assert!(error.is_err()); + +// let rates = array![[0.1, 0.1, 0.1], [0.2, 0.2, 0.2], [0.3, 0.3, 0.3]]; +// device = device +// .set_all_qubit_decoherence_rates(rates.clone()) +// .unwrap(); +// assert_eq!(device.qubit_decoherence_rates(&1), Some(rates)); +// assert_eq!(device.qubit_decoherence_rates(&number_qubits), None); + +// let test_edges = vec![(0, 1), (0, 2), (1, 2)]; +// let edges = device.two_qubit_edges(); +// assert_eq!(test_edges, edges); +// } + +// /// Test new() function for GenericChain device +// #[test] +// fn genericchain_new() { +// let number_qubits = 3usize; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &[]; +// let device = GenericChain::new( +// number_qubits, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// // Test number of qubits +// assert_eq!(device.number_qubits(), number_qubits); +// // Test that available single-qubit-gate is initialized with gate time set to zero +// assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.0)); +// // Test that for non-available gates the returned gate time is Non +// assert_eq!(device.single_qubit_gate_time("RotateY", &0), None); + +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.0)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.0)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); +// assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); + +// assert_eq!( +// device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), +// None, +// ); + +// let empty_rates = array![[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]; +// assert_eq!(device.qubit_decoherence_rates(&0), Some(empty_rates)); +// } + +// /// Test set gate time functions for GenericChain device +// #[test] +// fn test_genericchain_settimes() { +// let number_qubits = 3usize; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &[]; +// let mut device = GenericChain::new( +// number_qubits, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// device = device.set_all_single_qubit_gate_times(&"RotateX", 0.07); +// device = device.set_all_single_qubit_gate_times(&"RotateZ", 0.1); + +// device = device.set_all_two_qubit_gate_times(&"CNOT", 0.05); + +// assert_eq!(device.single_qubit_gate_time("RotateX", &0), Some(0.07f64)); +// assert_eq!( +// device.single_qubit_gate_time("RotateX", &number_qubits), +// None +// ); +// assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.1f64)); + +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.05f64)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.05f64)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &2), None); +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); +// assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); + +// assert_eq!( +// device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), +// None, +// ); +// } + +// // Test set decoherence and two_qubit_edges for GenericChain device +// #[test] +// fn test_genericchain_setattributes() { +// let number_qubits = 3usize; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &[]; +// let mut device = GenericChain::new( +// number_qubits, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// let rates_invalid = array![[0.1], [0.2], [0.3]]; +// let error = device +// .clone() +// .set_all_qubit_decoherence_rates(rates_invalid.clone()); +// assert!(error.is_err()); + +// let rates = array![[0.1, 0.1, 0.1], [0.2, 0.2, 0.2], [0.3, 0.3, 0.3]]; +// device = device +// .set_all_qubit_decoherence_rates(rates.clone()) +// .unwrap(); +// assert_eq!(device.qubit_decoherence_rates(&1), Some(rates)); +// assert_eq!(device.qubit_decoherence_rates(&number_qubits), None); + +// let test_edges = vec![(0, 1), (1, 2)]; +// let edges = device.two_qubit_edges(); +// assert_eq!(test_edges, edges); +// } + +// /// Test new() function for GenericGrid +// #[test] +// fn genericgrid_new() { +// let number_rows = 3usize; +// let number_columns = 4usize; +// let number_qubits = number_rows * number_columns; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &[]; +// let device = GenericGrid::new( +// number_rows, +// number_columns, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// // Test number of qubits +// assert_eq!(device.number_qubits(), number_qubits); +// // Test that available single-qubit-gate is initialized with gate time set to zero +// assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.0)); +// // Test that for non-available gates the returned gate time is Non +// assert_eq!(device.single_qubit_gate_time("RotateY", &0), None); + +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.0)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.0)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &5), None); +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); +// assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); + +// assert_eq!( +// device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), +// None, +// ); + +// let empty_rates = array![[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]; +// assert_eq!(device.qubit_decoherence_rates(&0), Some(empty_rates)); +// } + +// /// Test set gate time functions for GenericGrid +// #[test] +// fn test_genericgrid_settimes() { +// let number_rows = 3usize; +// let number_columns = 4usize; +// let number_qubits = number_rows * number_columns; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &["MultiQubitMS".to_string()]; +// let mut device = GenericGrid::new( +// number_rows, +// number_columns, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// device = device.set_all_single_qubit_gate_times(&"RotateX", 0.07); +// device = device.set_all_single_qubit_gate_times(&"RotateZ", 0.1); + +// device = device.set_all_two_qubit_gate_times(&"CNOT", 0.05); +// device = device.set_all_multi_qubit_gate_times(&"MultiQubitMS", 0.2); + +// assert_eq!(device.single_qubit_gate_time("RotateX", &0), Some(0.07f64)); +// assert_eq!( +// device.single_qubit_gate_time("RotateX", &number_qubits), +// None +// ); +// assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.1f64)); + +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.05f64)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.05f64)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &3), None); +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); +// assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); + +// // test for all qubits in 2nd column +// assert_eq!( +// device.multi_qubit_gate_time("MultiQubitMS", &[1, 5, 9]), +// Some(0.2f64), +// ); +// // test for all qubits in 3rd row +// assert_eq!( +// device.multi_qubit_gate_time("MultiQubitMS", &[8, 9, 10, 11]), +// Some(0.2f64), +// ); +// // test a combination not covered by the standard function +// assert_eq!( +// device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), +// None, +// ); +// } + +// // Test set decoherence for GenericGrid +// #[test] +// fn test_genericgrid_setattributes() { +// let number_rows = 3usize; +// let number_columns = 4usize; +// let number_qubits = number_rows * number_columns; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &[]; +// let mut device = GenericGrid::new( +// number_rows, +// number_columns, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// let rates_invalid = array![[0.1], [0.2], [0.3]]; +// let error = device +// .clone() +// .set_all_qubit_decoherence_rates(rates_invalid.clone()); +// assert!(error.is_err()); + +// let rates = array![[0.1, 0.1, 0.1], [0.2, 0.2, 0.2], [0.3, 0.3, 0.3]]; +// device = device +// .set_all_qubit_decoherence_rates(rates.clone()) +// .unwrap(); +// assert_eq!(device.qubit_decoherence_rates(&1), Some(rates)); +// assert_eq!(device.qubit_decoherence_rates(&number_qubits), None); +// } + +// // Test two_qubit_edges() for GenericGrid +// #[test_case(3, 4, vec![(0, 1), (0, 4), (1, 2), (1, 5), (2, 3), (2, 6), (3, 7), (4, 5), (4, 8), (5, 6), (5, 9), (6, 7), (6, 10), (7, 11), (8, 9), (9, 10), (10, 11)]; "3_4")] +// #[test_case(2, 3, vec![(0, 1),(0, 3),(1, 2),(1, 4),(2, 5), (3, 4), (4, 5)]; "2_3")] +// fn test_genericgrid_edges(rows: usize, columns: usize, test_edges: Vec<(usize, usize)>) { +// let number_rows = rows; +// let number_columns = columns; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &[]; +// let device = GenericGrid::new( +// number_rows, +// number_columns, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// let edges = device.two_qubit_edges(); +// assert_eq!(test_edges, edges); +// } + +// /// Test new() function for GenericDevice +// #[test] +// fn genericdevice_new() { +// let number_qubits = 3usize; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &[]; +// let device = GenericDevice::new( +// number_qubits, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// // Test number of qubits +// assert_eq!(device.number_qubits(), number_qubits); +// // Test that available single-qubit-gate is initialized with gate time set to zero +// assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.0)); +// // Test that for non-available gates the returned gate time is Non +// assert_eq!(device.single_qubit_gate_time("RotateY", &0), None); + +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.0)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.0)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); +// assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); + +// assert_eq!( +// device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), +// None, +// ); + +// let empty_rates = array![[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]; +// assert_eq!(device.qubit_decoherence_rates(&0), Some(empty_rates)); +// } + +// /// Test public fields of the GenericDevice +// #[test] +// fn genericdevice_fields() { +// let number_qubits = 3usize; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let mapvec: Vec = vec![ +// SingleQubitMap { +// qubit: 0, +// time: 0.0, +// }, +// SingleQubitMap { +// qubit: 1, +// time: 0.0, +// }, +// SingleQubitMap { +// qubit: 2, +// time: 0.0, +// }, +// ]; +// let mut single_qubit_gate_map: HashMap> = HashMap::new(); +// for gate in single_qubit_gates { +// single_qubit_gate_map.insert(gate.clone(), mapvec.clone()); +// } +// let mut two_qubit_gate_map: HashMap> = HashMap::new(); +// two_qubit_gate_map.insert( +// "CNOT".to_string(), +// vec![ +// TwoQubitMap { +// control: 0, +// target: 1, +// time: 0.0, +// }, +// TwoQubitMap { +// control: 1, +// target: 2, +// time: 0.0, +// }, +// ], +// ); +// let multi_qubit_gates: HashMap = HashMap::new(); + +// let mut decoherence_rates: HashMap> = HashMap::new(); +// for qubit0 in 0..number_qubits { +// decoherence_rates.insert(qubit0, Array2::::zeros((3, 3))); +// } + +// let device = GenericDevice { +// number_qubits: number_qubits.clone(), +// single_qubit_gates: single_qubit_gate_map.clone(), +// two_qubit_gates: two_qubit_gate_map.clone(), +// multi_qubit_gates: multi_qubit_gates, +// decoherence_rates: decoherence_rates.clone(), +// }; + +// // Test public fields of the GenericDevice +// assert_eq!(device.number_qubits, number_qubits); +// assert_eq!(device.single_qubit_gates, single_qubit_gate_map); +// assert_eq!(device.two_qubit_gates, two_qubit_gate_map); +// assert_eq!(device.multi_qubit_gates, HashMap::new()); +// assert_eq!(device.decoherence_rates, decoherence_rates); +// } + +// /// Test set gate time functions for GenericDevice +// #[test] +// fn test_genericldevice_settimes() { +// let number_qubits = 3usize; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &[]; +// let mut device = GenericDevice::new( +// number_qubits, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// device = device.set_all_single_qubit_gate_times(&"RotateX", 0.07); +// device = device.set_all_single_qubit_gate_times(&"RotateZ", 0.1); + +// device = device.set_all_two_qubit_gate_times(&"CNOT", 0.05); +// device = device.set_all_multi_qubit_gate_times(&"test", 0.0); + +// assert_eq!(device.single_qubit_gate_time("RotateX", &0), Some(0.07f64)); +// assert_eq!( +// device.single_qubit_gate_time("RotateX", &number_qubits), +// None +// ); +// assert_eq!(device.single_qubit_gate_time("RotateZ", &0), Some(0.1f64)); + +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &1), Some(0.05f64)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &1, &0), Some(0.05f64)); +// assert_eq!(device.two_qubit_gate_time("CNOT", &0, &number_qubits), None); +// assert_eq!(device.two_qubit_gate_time("CZ", &0, &1), None); + +// assert_eq!( +// device.multi_qubit_gate_time("MultiQubitMS", &[0, 1, 2]), +// None, +// ); +// } + +// // Test set decoherence and two_qubit_edges for GenericDevice +// #[test] +// fn test_genericdevice_setattributes() { +// let number_qubits = 3usize; +// let single_qubit_gates = &["RotateX".to_string(), "RotateZ".to_string()]; +// let two_qubit_gates = &["CNOT".to_string()]; +// let multi_qubit_gates = &[]; +// let mut device = GenericDevice::new( +// number_qubits, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// let rates_invalid = array![[0.1], [0.2], [0.3]]; +// let error = device +// .clone() +// .set_all_qubit_decoherence_rates(rates_invalid.clone()); +// assert!(error.is_err()); + +// let rates = array![[0.1, 0.1, 0.1], [0.2, 0.2, 0.2], [0.3, 0.3, 0.3]]; +// device = device +// .set_all_qubit_decoherence_rates(rates.clone()) +// .unwrap(); +// assert_eq!(device.qubit_decoherence_rates(&1), Some(rates)); +// assert_eq!(device.qubit_decoherence_rates(&number_qubits), None); + +// let test_edges = vec![(0, 1), (0, 2), (1, 2)]; +// let edges = device.two_qubit_edges(); +// assert_eq!(test_edges, edges); +// } + +// // Very basic unit test for the fields in the created custom qubit map structs +// #[test] +// fn test_mapstructs() { +// let singlequbitstruct = SingleQubitMap { +// qubit: 0, +// time: 4.3, +// }; +// let twoqubitstruct = TwoQubitMap { +// control: 0, +// target: 2, +// time: 0.0, +// }; +// let qubits: Vec = vec![0, 1, 2, 3, 4]; +// let multiqubitstruct = MultiQubitMap { +// qubits: qubits.clone(), +// time: 5.0, +// }; +// assert_eq!(singlequbitstruct.qubit, 0); +// assert_eq!(singlequbitstruct.time, 4.3); +// assert_eq!(twoqubitstruct.control, 0); +// assert_eq!(twoqubitstruct.target, 2); +// assert_eq!(twoqubitstruct.time, 0.0); +// assert_eq!(multiqubitstruct.qubits, qubits); +// assert_eq!(multiqubitstruct.time, 5.0); +// } + +// // Test Clone, PartialEq and Debug for GenericDevice +// #[test] +// fn test_genericdevice_derive() { +// let number_qubits = 1usize; +// let single_qubit_gates = &[]; +// let two_qubit_gates = &[]; +// let multi_qubit_gates = &[]; +// let device = GenericDevice::new( +// number_qubits, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// // Test debug +// let debug = "GenericDevice { number_qubits: 1, single_qubit_gates: {}, two_qubit_gates: {}, multi_qubit_gates: {}, decoherence_rates: {0: [[0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2} }"; +// assert_eq!(format!("{:?}", device.clone()), debug); + +// // Test Clone and PartialEq +// assert!(device.clone() == device); +// } + +// // Test Clone, PartialEq and Debug for GenericChain +// #[test] +// fn test_genericchain_derive() { +// let number_qubits = 1usize; +// let single_qubit_gates = &[]; +// let two_qubit_gates = &[]; +// let multi_qubit_gates = &[]; +// let device = GenericChain::new( +// number_qubits, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// // Test debug +// let debug = "GenericChain { number_qubits: 1, single_qubit_gates: {}, two_qubit_gates: {}, multi_qubit_gates: {}, decoherence_rates: {0: [[0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2} }"; +// assert_eq!(format!("{:?}", device.clone()), debug); + +// // Test Clone and PartialEq +// assert!(device.clone() == device); +// } + +// // Test Clone, PartialEq and Debug for AllToAllDevice +// #[test] +// fn test_alltoalldevice_derive() { +// let number_qubits = 1usize; +// let single_qubit_gates = &[]; +// let two_qubit_gates = &[]; +// let multi_qubit_gates = &[]; +// let device = AllToAllDevice::new( +// number_qubits, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// // Test debug +// let debug = "AllToAllDevice { number_qubits: 1, single_qubit_gates: {}, two_qubit_gates: {}, multi_qubit_gates: {}, decoherence_rates: {0: [[0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2} }"; +// assert_eq!(format!("{:?}", device.clone()), debug); + +// // Test Clone and PartialEq +// assert!(device.clone() == device); +// } + +// // Test Clone, PartialEq and Debug for GenericGrid +// #[test] +// fn test_genericgrid_derive() { +// let number_rows = 1usize; +// let number_columns = 1usize; +// let single_qubit_gates = &[]; +// let two_qubit_gates = &[]; +// let multi_qubit_gates = &[]; +// let device = GenericGrid::new( +// number_rows, +// number_columns, +// single_qubit_gates, +// two_qubit_gates, +// multi_qubit_gates, +// ); + +// // Test debug +// let debug = "GenericGrid { number_rows: 1, number_columns: 1, single_qubit_gates: {}, two_qubit_gates: {}, multi_qubit_gates: {}, decoherence_rates: {0: [[0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0],\n [0.0, 0.0, 0.0]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2} }"; +// assert_eq!(format!("{:?}", device.clone()), debug); + +// // Test Clone and PartialEq +// assert!(device.clone() == device); +// } diff --git a/roqoqo/tests/integration/main.rs b/roqoqo/tests/integration/main.rs index 2de0dbbc..a24ea120 100644 --- a/roqoqo/tests/integration/main.rs +++ b/roqoqo/tests/integration/main.rs @@ -19,8 +19,8 @@ mod measurements; #[cfg(test)] mod circuit; -// #[cfg(test)] -// mod devices; +#[cfg(test)] +mod devices; #[cfg(test)] mod quantum_program; diff --git a/roqoqo/tests/integration/operations/measurement_operations.rs b/roqoqo/tests/integration/operations/measurement_operations.rs index 2a231001..42f5bbcf 100644 --- a/roqoqo/tests/integration/operations/measurement_operations.rs +++ b/roqoqo/tests/integration/operations/measurement_operations.rs @@ -16,7 +16,6 @@ use qoqo_calculator::{Calculator, CalculatorFloat}; use roqoqo::operations::*; use roqoqo::prelude::RoqoqoError; use roqoqo::Circuit; -use serde_json::{from_str, json}; #[cfg(feature = "serialize")] use serde_test::{assert_tokens, Configure, Token}; use std::collections::{HashMap, HashSet};