roqoqo_aqt/interface.rs
1// Copyright © 2021-2024 HQS Quantum Simulations GmbH. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4// in compliance with the License. You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software distributed under the
9// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
10// express or implied. See the License for the specific language governing permissions and
11// limitations under the License.
12
13use roqoqo::operations::*;
14use roqoqo::Circuit;
15use roqoqo::RoqoqoBackendError;
16
17// Pragma operations that are ignored by backend and do not throw an error
18const ALLOWED_OPERATIONS: &[&str; 12] = &[
19    "PragmaSetNumberOfMeasurements",
20    "PragmaBoostNoise",
21    "PragmaStopParallelBlock",
22    "PragmaGlobalPhase",
23    "DefinitionBit",
24    "DefinitionFloat",
25    "DefinitionComplex",
26    "InputSymbolic",
27    "InputBit",
28    "PragmaRepeatedMeasurement",
29    "PragmaStartDecompositionBlock",
30    "PragmaStopDecompositionBlock",
31    // "PragmaLoop",                  // CHECK
32    // "PhaseShiftedControlledPhase", // CHECK
33];
34
35/// Representation for AQT backend instructions serialized to Json
36#[derive(PartialEq, Debug, Clone, serde::Serialize, serde::Deserialize)]
37#[serde(tag = "operation")]
38pub enum AqtInstruction {
39    /// Instruction involving RZ gate
40    RZ {
41        /// angle of rotation in PI radians [0 - 2]
42        phi: f64,
43        /// qubit where gate is applied
44        qubit: u32,
45    },
46    /// Instruction involving R gate
47    R {
48        /// polar angle of rotation in PI radians [0 - 1]
49        phi: f64,
50        /// radial angle of rotation in PI radians [0 - 2]
51        theta: f64,
52        /// qubit where gate is applied
53        qubit: u32,
54    },
55    /// Instruction involving MolmerSorensenXX gate
56    RXX {
57        /// qubits where gate is applied
58        qubits: Vec<u32>,
59        /// angle of rotation in PI radians [0 - 2]
60        theta: f64,
61    },
62    /// Instruction to measure all qubits
63    MEASURE,
64}
65
66/// Converts all operations in a [roqoqo::Circuit] into instructions for AQT Hardware or AQT Simulators
67///
68/// # Arguments
69///
70/// `circuit` - The [roqoqo::Circuit] that is converted
71///
72/// # Returns
73///
74/// `Vec<AqtInstruction>` - List of converted instructions
75/// `RoqoqoBackendError::OperationNotInBackend` - Error when [roqoqo::operations::Operation] can not be converted
76pub fn call_circuit(circuit: &Circuit) -> Result<Vec<AqtInstruction>, RoqoqoBackendError> {
77    let mut circuit_vec: Vec<AqtInstruction> = Vec::new();
78    for op in circuit.iter() {
79        if let Some(instruction) = call_operation(op)? {
80            circuit_vec.push(instruction);
81        }
82    }
83    Ok(circuit_vec)
84}
85
86/// Converts a [roqoqo::operations::Operation] into an instruction for AQT Hardware or AQT Simulators.
87/// *Note* - Any measurment operation, regardless of the specific qubits defined, will always measure all the qubits.
88///
89/// # Arguments
90///
91/// `operation` - The [roqoqo::operations::Operation] that is converted
92///
93/// # Returns
94///
95/// `AqtInstruction` - Converted instruction
96/// `RoqoqoBackendError::OperationNotInBackend` - Error when [roqoqo::operations::Operation] can not be converted
97pub fn call_operation(operation: &Operation) -> Result<Option<AqtInstruction>, RoqoqoBackendError> {
98    match operation {
99        Operation::RotateZ(op) => Ok(Some(AqtInstruction::RZ {
100            phi: *op.theta().float()? / std::f64::consts::PI,
101            qubit: *op.qubit() as u32,
102        })),
103        Operation::RotateX(op) => Ok(Some(AqtInstruction::R {
104            phi: 0.0,
105            theta: *op.theta().float()? / std::f64::consts::PI,
106            qubit: *op.qubit() as u32,
107        })),
108        Operation::RotateY(op) => Ok(Some(AqtInstruction::R {
109            phi: 0.5,
110            theta: *op.theta().float()? / std::f64::consts::PI,
111            qubit: *op.qubit() as u32,
112        })),
113        Operation::PauliZ(op) => Ok(Some(AqtInstruction::RZ {
114            phi: 1.0,
115            qubit: *op.qubit() as u32,
116        })),
117        Operation::PauliX(op) => Ok(Some(AqtInstruction::R {
118            phi: 0.0,
119            theta: 1.0,
120            qubit: *op.qubit() as u32,
121        })),
122        Operation::PauliY(op) => Ok(Some(AqtInstruction::R {
123            phi: 0.5,
124            theta: 1.0,
125            qubit: *op.qubit() as u32,
126        })),
127        // Variable MSXX is different in qoqo and aqt
128        Operation::VariableMSXX(op) => Ok(Some(AqtInstruction::RXX {
129            qubits: vec![*op.control() as u32, *op.target() as u32],
130            theta: *op.theta().float()? / 2.0,
131        })),
132        Operation::MolmerSorensenXX(op) => Ok(Some(AqtInstruction::RXX {
133            qubits: vec![*op.control() as u32, *op.target() as u32],
134            theta: 0.5,
135        })),
136        // AQT device
137        Operation::PragmaRepeatedMeasurement(_op) => Ok(Some(AqtInstruction::MEASURE)),
138        Operation::MeasureQubit(_op) => Ok(Some(AqtInstruction::MEASURE)),
139        _ => {
140            if ALLOWED_OPERATIONS.contains(&operation.hqslang()) {
141                Ok(None)
142            } else {
143                Err(RoqoqoBackendError::OperationNotInBackend {
144                    backend: "AQT",
145                    hqslang: operation.hqslang(),
146                })
147            }
148        }
149    }
150}