use crate::operations;
use crate::prelude::*;
use crate::Circuit;
use crate::RoqoqoError;
use ndarray::prelude::*;
use num_complex::Complex64;
use qoqo_calculator::CalculatorFloat;
#[cfg(feature = "overrotate")]
use rand_distr::{Distribution, Normal};
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
use std::{f64::consts::PI, panic};
#[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::Operate,
roqoqo_derive::Substitute,
roqoqo_derive::OperateMultiQubit,
)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
pub struct MultiQubitCNOT {
qubits: Vec<usize>,
}
#[allow(non_upper_case_globals)]
const TAGS_MultiQubitCNOT: &[&str; 4] = &[
"Operation",
"GateOperation",
"MultiQubitGateOperation",
"MultiQubitCNOT",
];
impl operations::ImplementedIn1point20 for MultiQubitCNOT {}
impl SupportedVersion for MultiQubitCNOT {
fn minimum_supported_roqoqo_version(&self) -> (u32, u32, u32) {
(1, 20, 0)
}
}
impl OperateGate for MultiQubitCNOT {
fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
let dim = 2_usize.pow(self.qubits.len() as u32);
let mut array = Array2::eye(dim);
array
.slice_mut(s![dim - 2.., dim - 2..])
.assign(&array![[0., 1.], [1., 0.]]);
Ok(array.map(|x| x.into()))
}
}
impl OperateMultiQubitGate for MultiQubitCNOT {
fn circuit(&self) -> Circuit {
let mut circuit = Circuit::new();
match self.qubits().len() {
2 => {
circuit += operations::CNOT::new(self.qubits[0], self.qubits[1]);
}
3 => {
circuit += operations::Hadamard::new(self.qubits[2]);
circuit += operations::CNOT::new(self.qubits[1], self.qubits[2]);
circuit +=
operations::PhaseShiftState1::new(self.qubits[2], -CalculatorFloat::FRAC_PI_4);
circuit += operations::CNOT::new(self.qubits[0], self.qubits[2]);
circuit += operations::TGate::new(self.qubits[2]);
circuit += operations::CNOT::new(self.qubits[1], self.qubits[2]);
circuit +=
operations::PhaseShiftState1::new(self.qubits[2], -CalculatorFloat::FRAC_PI_4);
circuit += operations::CNOT::new(self.qubits[0], self.qubits[2]);
circuit += operations::TGate::new(self.qubits[1]);
circuit += operations::TGate::new(self.qubits[2]);
circuit += operations::Hadamard::new(self.qubits[2]);
circuit += operations::CNOT::new(self.qubits[0], self.qubits[1]);
circuit += operations::TGate::new(self.qubits[0]);
circuit +=
operations::PhaseShiftState1::new(self.qubits[1], -CalculatorFloat::FRAC_PI_4);
circuit += operations::CNOT::new(self.qubits[0], self.qubits[1]);
}
_ => panic!(
"Only MultiQubitCNOT gates with 2 or 3 controls can be turned into a circuit."
),
}
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"];
#[allow(clippy::upper_case_acronyms)]
#[derive(
Debug,
Clone,
PartialEq,
roqoqo_derive::InvolveQubits,
roqoqo_derive::Operate,
roqoqo_derive::Substitute,
roqoqo_derive::OperateMultiQubit,
)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
pub struct QFT {
qubits: Vec<usize>,
swaps: bool,
inverse: bool,
}
const TAGS_QFT: &[&str; 4] = &[
"Operation",
"GateOperation",
"MultiQubitGateOperation",
"QFT",
];
impl operations::ImplementedIn1point20 for QFT {}
impl SupportedVersion for QFT {
fn minimum_supported_roqoqo_version(&self) -> (u32, u32, u32) {
(1, 20, 0)
}
}
impl OperateGate for QFT {
fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
let dim = self.qubits.len();
if !self.swaps && dim > 1 {
return Err(RoqoqoError::GenericError {
msg: "Unitary matrix output is only supported for QFT with swapping.".into(),
});
}
let n = 2_usize.pow(dim as u32);
let mut array = Array2::zeros((n, n));
for i in 0..n {
for j in 0..n {
let mut theta = 2. * PI * (i as f64 * j as f64) / (n as f64);
if self.inverse {
theta *= -1.;
}
array[[i, j]] = Complex64::from_polar(1. / (n as f64).sqrt(), theta);
}
}
Ok(array)
}
}
impl OperateMultiQubitGate for QFT {
fn circuit(&self) -> Circuit {
let dim = self.qubits.len();
let mut circuit = Circuit::new();
if self.swaps && self.inverse {
for i in 0..dim / 2 {
circuit += operations::SWAP::new(self.qubits[i], self.qubits[dim - i - 1]);
}
}
for i in 0..dim {
circuit += operations::Hadamard::new(self.qubits[i]);
for j in i + 1..dim {
let mut theta = PI / 2.0_f64.powi((j - i) as i32);
if self.inverse {
theta *= -1.;
}
circuit += operations::ControlledPhaseShift::new(
self.qubits[j],
self.qubits[i],
theta.into(),
);
}
}
if self.swaps && !self.inverse {
for i in 0..dim / 2 {
circuit += operations::SWAP::new(self.qubits[i], self.qubits[dim - i - 1]);
}
}
circuit
}
}