use std::panic;
use crate::operations;
use crate::prelude::*;
use crate::Circuit;
use crate::RoqoqoError;
use ndarray::Array2;
use num_complex::Complex64;
use qoqo_calculator::CalculatorFloat;
#[cfg(feature = "overrotate")]
use rand_distr::{Distribution, Normal};
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
#[allow(clippy::upper_case_acronyms)]
#[derive(
Debug,
Clone,
PartialEq,
roqoqo_derive::InvolveQubits,
roqoqo_derive::SupportedVersion,
roqoqo_derive::Operate,
roqoqo_derive::Substitute,
roqoqo_derive::OperateMultiQubit,
roqoqo_derive::Rotate,
)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
pub struct MultiQubitMS {
qubits: Vec<usize>,
theta: CalculatorFloat,
}
#[allow(non_upper_case_globals)]
const TAGS_MultiQubitMS: &[&str; 4] = &[
"Operation",
"GateOperation",
"MultiQubitGateOperation",
"MultiQubitMS",
];
impl OperateGate for MultiQubitMS {
fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
let dim = 2_usize.pow(self.qubits.len() as u32);
let mut array: Array2<Complex64> = Array2::zeros((dim, dim));
let cos: Complex64 = Complex64::new((self.theta.float()? / 2.0).cos(), 0.0);
let sin: Complex64 = Complex64::new(0.0, -(self.theta.float()? / 2.0).sin());
for i in 0..dim {
array[(i, i)] = cos;
array[(i, dim - i - 1)] = sin;
}
Ok(array)
}
}
impl OperateMultiQubitGate for MultiQubitMS {
fn circuit(&self) -> Circuit {
let dim = self.qubits.len();
let mut circuit = Circuit::new();
for q in self.qubits.iter() {
circuit += operations::Hadamard::new(*q);
}
for q in self.qubits[1..].iter() {
circuit += operations::CNOT::new(*q - 1, *q);
}
circuit += operations::RotateZ::new(dim - 1, self.theta.clone());
for q in self.qubits[1..].iter() {
circuit += operations::CNOT::new(dim - *q - 1, dim - *q);
}
for q in self.qubits.iter() {
circuit += operations::Hadamard::new(*q);
}
circuit
}
}
#[allow(clippy::upper_case_acronyms)]
#[derive(
Debug,
Clone,
PartialEq,
roqoqo_derive::InvolveQubits,
roqoqo_derive::SupportedVersion,
roqoqo_derive::Operate,
roqoqo_derive::Substitute,
roqoqo_derive::OperateMultiQubit,
roqoqo_derive::Rotate,
)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
pub struct MultiQubitZZ {
qubits: Vec<usize>,
theta: CalculatorFloat,
}
#[allow(non_upper_case_globals)]
const TAGS_MultiQubitZZ: &[&str; 4] = &[
"Operation",
"GateOperation",
"MultiQubitGateOperation",
"MultiQubitZZ",
];
impl OperateGate for MultiQubitZZ {
fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
let dim = 2_usize.pow(self.qubits.len() as u32);
let mut array: Array2<Complex64> = Array2::zeros((dim, dim));
let cos: Complex64 = Complex64::new((self.theta.float()? / 2.0).cos(), 0.0);
let sin: Complex64 = Complex64::new(0.0, -(self.theta.float()? / 2.0).sin());
for i in 0..dim {
let prefactor: f64 = (0..self.qubits.len())
.map(|q| match i.div_euclid(2usize.pow(q as u32)) % 2 {
0 => 1.0,
1 => -1.0,
_ => panic!("Internal division error MuliQubitZZ"),
})
.product();
array[(i, i)] = cos + prefactor * sin;
}
Ok(array)
}
}
impl OperateMultiQubitGate for MultiQubitZZ {
fn circuit(&self) -> Circuit {
let dim = self.qubits.len();
let mut circuit = Circuit::new();
for q in self.qubits[1..].iter() {
circuit += operations::CNOT::new(*q - 1, *q);
}
circuit += operations::RotateZ::new(dim - 1, self.theta.clone());
for q in self.qubits[1..].iter() {
circuit += operations::CNOT::new(dim - *q - 1, dim - *q);
}
circuit
}
}
#[cfg(feature = "unstable_operation_definition")]
#[allow(clippy::upper_case_acronyms)]
#[derive(
Debug,
Clone,
PartialEq,
roqoqo_derive::OperateMultiQubit,
roqoqo_derive::Operate,
roqoqo_derive::InvolveQubits,
)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
pub struct CallDefinedGate {
gate_name: String,
qubits: Vec<usize>,
free_parameters: Vec<CalculatorFloat>,
}
#[cfg(feature = "unstable_operation_definition")]
impl Substitute for CallDefinedGate {
fn substitute_parameters(
&self,
calculator: &qoqo_calculator::Calculator,
) -> Result<Self, RoqoqoError> {
let mut new_params: Vec<CalculatorFloat> = vec![];
for param in &self.free_parameters.clone() {
new_params.push(CalculatorFloat::from(
calculator
.parse_get(param.clone())
.map_err(RoqoqoError::CalculatorError)?,
));
}
Ok(CallDefinedGate::new(
self.gate_name.clone(),
self.qubits.clone(),
new_params,
))
}
fn remap_qubits(
&self,
mapping: &std::collections::HashMap<usize, usize>,
) -> Result<Self, RoqoqoError> {
crate::operations::check_valid_mapping(mapping)?;
let mut new_qubits: Vec<usize> = Vec::new();
for q in &self.qubits {
new_qubits.push(*mapping.get(q).ok_or(Err("")).map_err(
|_x: std::result::Result<&usize, &str>| RoqoqoError::QubitMappingError {
qubit: *q,
},
)?)
}
Ok(CallDefinedGate::new(
self.gate_name.clone(),
new_qubits,
self.free_parameters.clone(),
))
}
}
#[cfg(feature = "unstable_operation_definition")]
impl super::ImplementedIn1point13 for CallDefinedGate {}
#[cfg(feature = "unstable_operation_definition")]
impl SupportedVersion for CallDefinedGate {
fn minimum_supported_roqoqo_version(&self) -> (u32, u32, u32) {
(1, 13, 0)
}
}
#[cfg(feature = "unstable_operation_definition")]
#[allow(non_upper_case_globals)]
const TAGS_CallDefinedGate: &[&str; 3] =
&["Operation", "MultiQubitGateOperation", "CallDefinedGate"];