use crate::compiler::compilable::Compilable;
#[cfg(feature = "gpu")]
use crate::components::gpu_context::GpuKernelArgs;
#[cfg(feature = "gpu")]
use crate::components::gpu_context::{GPU_CONTEXT, KernelType};
use crate::{components::{state::State, pauli_string::PauliString}, errors::Error};
use dyn_clone::DynClone;
use num_complex::Complex;
#[cfg(feature = "gpu")]
use ocl::prm::Float2;
use rayon::prelude::*;
#[cfg(feature = "gpu")]
use std::f64::consts::PI;
use std::fmt::Display;
use std::{collections::HashSet, fmt::Debug};
const PARALLEL_THRESHOLD_NUM_QUBITS: usize = 10;
const OPENCL_THRESHOLD_NUM_QUBITS: usize = 15;
#[cfg(feature = "gpu")]
fn execute_on_gpu(
state: &State,
target_qubit: usize,
control_qubits: &[usize],
kernel_type: KernelType,
global_work_size: usize,
kernel_args: GpuKernelArgs,
) -> Result<Vec<Complex<f64>>, Error> {
let mut context_guard = GPU_CONTEXT.lock().map_err(|_| Error::GpuContextLockError)?;
let context = match *context_guard {
Ok(ref mut ctx) => ctx,
Err(ref e) => return Err(e.clone()), };
let num_qubits = state.num_qubits();
let num_state_elements = state.state_vector.len();
let state_buffer_cloned = context.ensure_state_buffer(num_state_elements)?.clone();
let control_qubits_i32: Vec<i32> = control_qubits.iter().map(|&q| q as i32).collect();
let control_buffer_len = control_qubits_i32.len();
let control_buffer_cloned = context.ensure_control_buffer(control_buffer_len)?.clone();
let state_vector_f32: Vec<Float2> = state
.state_vector
.iter()
.map(|c| Float2::new(c.re as f32, c.im as f32))
.collect();
state_buffer_cloned
.write(&state_vector_f32)
.enq()
.map_err(|e| Error::OpenCLError(format!("Failed to write to state buffer: {}", e)))?;
if !control_qubits_i32.is_empty() {
control_buffer_cloned
.write(&control_qubits_i32)
.enq()
.map_err(|e| Error::OpenCLError(format!("Failed to write to control buffer: {}", e)))?;
} else {
let dummy_control_data = vec![0; 1]; control_buffer_cloned
.write(&dummy_control_data)
.enq()
.map_err(|e| {
Error::OpenCLError(format!("Failed to write to dummy control buffer: {}", e))
})?;
}
let mut kernel_builder = context.pro_que.kernel_builder(kernel_type.name());
kernel_builder
.global_work_size(global_work_size)
.arg(&state_buffer_cloned) .arg(num_qubits as i32)
.arg(target_qubit as i32)
.arg(control_buffer_cloned)
.arg(control_qubits_i32.len() as i32);
match kernel_args {
GpuKernelArgs::None => {
}
GpuKernelArgs::SOrSdag { sign } => {
kernel_builder.arg(sign);
}
GpuKernelArgs::PhaseShift {
cos_angle,
sin_angle,
} => {
kernel_builder.arg(cos_angle).arg(sin_angle);
}
GpuKernelArgs::SwapTarget { q1 } => {
kernel_builder.arg(q1);
}
GpuKernelArgs::RotationGate {
cos_half_angle,
sin_half_angle,
} => {
kernel_builder.arg(cos_half_angle).arg(sin_half_angle);
}
GpuKernelArgs::Matchgate {
q1,
cos_theta_half,
sin_theta_half,
exp_i_phi1,
exp_i_phi2,
} => {
kernel_builder
.arg(q1)
.arg(cos_theta_half)
.arg(sin_theta_half)
.arg(exp_i_phi1)
.arg(exp_i_phi2);
}
}
let kernel = kernel_builder.build().map_err(|e| {
Error::OpenCLError(format!(
"Failed to build kernel '{}': {}",
kernel_type.name(),
e
))
})?;
unsafe {
kernel
.enq()
.map_err(|e| Error::OpenCLError(format!("Failed to enqueue kernel: {}", e)))?;
}
let mut state_vector_ocl_result = vec![Float2::new(0.0, 0.0); num_state_elements];
state_buffer_cloned
.read(&mut state_vector_ocl_result)
.enq()
.map_err(|e| Error::OpenCLError(format!("Failed to read state buffer: {}", e)))?;
Ok(state_vector_ocl_result
.iter()
.map(|f2| Complex::new(f2[0] as f64, f2[1] as f64))
.collect())
}
pub trait Operator: Send + Sync + Debug + DynClone + Display {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error>;
fn base_qubits(&self) -> usize;
fn to_compilable(&self) -> Option<&dyn Compilable> {
None
}
}
dyn_clone::clone_trait_object!(Operator);
fn check_controls(index: usize, control_qubits: &[usize]) -> bool {
control_qubits
.iter()
.all(|&qubit| (index >> qubit) & 1 == 1)
}
fn validate_qubits(
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
expected_targets: usize,
) -> Result<(), Error> {
if target_qubits.len() != expected_targets {
return Err(Error::InvalidNumberOfQubits(target_qubits.len()));
}
let num_qubits = state.num_qubits();
for &target_qubit in target_qubits {
if target_qubit >= num_qubits {
return Err(Error::InvalidQubitIndex(target_qubit, num_qubits));
}
}
for &control_qubit in control_qubits {
if control_qubit >= num_qubits {
return Err(Error::InvalidQubitIndex(control_qubit, num_qubits));
}
for &target_qubit in target_qubits {
if control_qubit == target_qubit {
return Err(Error::OverlappingControlAndTargetQubits(
control_qubit,
target_qubit,
));
}
}
}
if expected_targets > 1 {
if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
let mut seen_targets: HashSet<usize> = HashSet::with_capacity(target_qubits.len());
for &target_qubit in target_qubits {
if !seen_targets.insert(target_qubit) {
return Err(Error::InvalidQubitIndex(target_qubit, num_qubits));
}
}
} else {
for i in 0..target_qubits.len() {
for j in i + 1..target_qubits.len() {
if target_qubits[i] == target_qubits[j] {
return Err(Error::InvalidQubitIndex(target_qubits[i], num_qubits));
}
}
}
}
}
Ok(())
}
#[derive(Debug, Clone, Copy)]
pub struct Hadamard;
impl Operator for Hadamard {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
let target_qubit: usize = target_qubits[0];
let num_qubits: usize = state.num_qubits();
let sqrt_2_inv: f64 = 1.0 / (2.0f64).sqrt();
let dim: usize = 1 << num_qubits;
#[allow(unused_assignments)]
let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
let gpu_enabled: bool = cfg!(feature = "gpu");
if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
#[cfg(feature = "gpu")]
{
let global_work_size = if num_qubits > 0 {
1 << (num_qubits - 1)
} else {
1
};
new_state_vec = execute_on_gpu(
state,
target_qubit,
control_qubits,
KernelType::Hadamard,
global_work_size,
GpuKernelArgs::None,
)?;
}
} else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
new_state_vec = state.state_vector.clone(); if control_qubits.is_empty() {
let updates: Vec<(usize, Complex<f64>)> = (0..(1 << (num_qubits - 1)))
.into_par_iter()
.flat_map(|k| {
let i0 = (k >> target_qubit << (target_qubit + 1))
| (k & ((1 << target_qubit) - 1));
let i1 = i0 | (1 << target_qubit);
let amp0 = state.state_vector[i0];
let amp1 = state.state_vector[i1];
vec![
(i0, sqrt_2_inv * (amp0 + amp1)),
(i1, sqrt_2_inv * (amp0 - amp1)),
]
})
.collect();
for (idx, val) in updates {
new_state_vec[idx] = val;
}
} else {
let updates: Vec<(usize, Complex<f64>)> = (0..dim)
.into_par_iter()
.filter_map(|i| {
if (i >> target_qubit) & 1 == 0 {
let j = i | (1 << target_qubit); if check_controls(i, control_qubits) {
let amp_i = state.state_vector[i];
let amp_j = state.state_vector[j];
Some(vec![
(i, sqrt_2_inv * (amp_i + amp_j)),
(j, sqrt_2_inv * (amp_i - amp_j)),
])
} else {
None }
} else {
None }
})
.flatten()
.collect();
for (idx, val) in updates {
new_state_vec[idx] = val;
}
}
} else {
new_state_vec = state.state_vector.clone(); if control_qubits.is_empty() {
for k in 0..(1 << (num_qubits - 1)) {
let i0 =
(k >> target_qubit << (target_qubit + 1)) | (k & ((1 << target_qubit) - 1));
let i1 = i0 | (1 << target_qubit);
let amp0 = state.state_vector[i0];
let amp1 = state.state_vector[i1];
new_state_vec[i0] = sqrt_2_inv * (amp0 + amp1);
new_state_vec[i1] = sqrt_2_inv * (amp0 - amp1);
}
} else {
for i in 0..dim {
if (i >> target_qubit) & 1 == 0 {
let j = i | (1 << target_qubit);
if check_controls(i, control_qubits) {
let amp_i = state.state_vector[i];
let amp_j = state.state_vector[j];
new_state_vec[i] = sqrt_2_inv * (amp_i + amp_j);
new_state_vec[j] = sqrt_2_inv * (amp_i - amp_j);
}
}
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits,
})
}
fn base_qubits(&self) -> usize {
1 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl Display for Hadamard {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "H")
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Pauli {
X,
Y,
Z,
}
impl Operator for Pauli {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
let target_qubit: usize = target_qubits[0];
let num_qubits: usize = state.num_qubits();
let dim: usize = 1 << num_qubits;
let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
let i_complex: Complex<f64> = Complex::new(0.0, 1.0);
let gpu_enabled: bool = cfg!(feature = "gpu");
if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
#[cfg(feature = "gpu")]
{
let kernel_type = match self {
Pauli::X => KernelType::PauliX,
Pauli::Y => KernelType::PauliY,
Pauli::Z => KernelType::PauliZ,
};
let global_work_size = if num_qubits == 0 {
1
} else {
match self {
Pauli::Z => 1 << num_qubits, _ => 1 << (num_qubits - 1), }
};
new_state_vec = execute_on_gpu(
state,
target_qubit,
control_qubits,
kernel_type,
global_work_size,
GpuKernelArgs::None,
)?;
}
} else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
match self {
Pauli::X => {
let updates: Vec<(usize, Complex<f64>)> = (0..dim)
.into_par_iter()
.filter_map(|i| {
if check_controls(i, control_qubits) && ((i >> target_qubit) & 1 == 0) {
let j = i | (1 << target_qubit);
let amp_i = state.state_vector[i];
let amp_j = state.state_vector[j];
Some(vec![(i, amp_j), (j, amp_i)])
} else {
None
}
})
.flatten()
.collect();
for (idx, val) in updates {
new_state_vec[idx] = val;
}
}
Pauli::Y => {
let updates: Vec<(usize, Complex<f64>)> = (0..dim)
.into_par_iter()
.filter_map(|i| {
if check_controls(i, control_qubits) && ((i >> target_qubit) & 1 == 0) {
let j = i | (1 << target_qubit);
let amp_i = state.state_vector[i];
let amp_j = state.state_vector[j];
Some(vec![(i, -i_complex * amp_j), (j, i_complex * amp_i)])
} else {
None
}
})
.flatten()
.collect();
for (idx, val) in updates {
new_state_vec[idx] = val;
}
}
Pauli::Z => {
new_state_vec
.par_iter_mut()
.enumerate()
.for_each(|(i, current_amp_ref)| {
if check_controls(i, control_qubits) && ((i >> target_qubit) & 1 == 1) {
*current_amp_ref = -state.state_vector[i];
}
});
}
}
} else {
for i in 0..dim {
if check_controls(i, control_qubits) {
match self {
Pauli::X => {
if (i >> target_qubit) & 1 == 0 {
let j = i | (1 << target_qubit);
let amp_i = state.state_vector[i];
let amp_j = state.state_vector[j];
new_state_vec[i] = amp_j;
new_state_vec[j] = amp_i;
}
}
Pauli::Y => {
if (i >> target_qubit) & 1 == 0 {
let j = i | (1 << target_qubit);
let amp_i = state.state_vector[i];
let amp_j = state.state_vector[j];
new_state_vec[i] = -i_complex * amp_j;
new_state_vec[j] = i_complex * amp_i;
}
}
Pauli::Z => {
if (i >> target_qubit) & 1 == 1 {
new_state_vec[i] = -state.state_vector[i];
}
}
}
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits: state.num_qubits(),
})
}
fn base_qubits(&self) -> usize {
1 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self) }
}
impl std::fmt::Display for Pauli {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Pauli::X => write!(f, "X"),
Pauli::Y => write!(f, "Y"),
Pauli::Z => write!(f, "Z"),
}
}
}
impl Pauli {
pub fn to_pauli_string(&self, target_qubit: usize) -> PauliString {
let mut pauli_map = std::collections::HashMap::new();
pauli_map.insert(target_qubit, *self);
PauliString::with_ops(1.0.into(), pauli_map)
}
}
#[derive(Debug, Clone, Copy)]
pub struct CNOT;
impl Operator for CNOT {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
if control_qubits.len() != 1 {
return Err(Error::InvalidNumberOfQubits(control_qubits.len()));
}
let control_qubit: usize = control_qubits[0];
Pauli::X.apply(state, target_qubits, &[control_qubit])
}
fn base_qubits(&self) -> usize {
2 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for CNOT {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "CNOT")
}
}
#[derive(Debug, Clone, Copy)]
pub struct SWAP;
impl Operator for SWAP {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 2)?;
let target_qubit_1: usize = target_qubits[0];
let target_qubit_2: usize = target_qubits[1];
let num_qubits: usize = state.num_qubits();
let dim: usize = 1 << num_qubits;
#[allow(unused_assignments)] let mut new_state_vec = state.state_vector.clone(); let gpu_enabled: bool = cfg!(feature = "gpu");
if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
#[cfg(feature = "gpu")]
{
let global_work_size = if num_qubits >= 2 {
1 << (num_qubits - 2)
} else {
1
}; new_state_vec = execute_on_gpu(
state,
target_qubit_1, control_qubits,
KernelType::Swap,
global_work_size,
GpuKernelArgs::SwapTarget {
q1: target_qubit_2 as i32,
}, )?;
}
} else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
let updates: Vec<(usize, Complex<f64>)> = (0..dim)
.into_par_iter()
.filter_map(|i| {
let target_bit_1 = (i >> target_qubit_1) & 1;
let target_bit_2 = (i >> target_qubit_2) & 1;
if target_bit_1 != target_bit_2 {
let j = i ^ (1 << target_qubit_1) ^ (1 << target_qubit_2);
if i < j && check_controls(i, control_qubits) {
let amp_i = state.state_vector[i];
let amp_j = state.state_vector[j];
Some(vec![(i, amp_j), (j, amp_i)])
} else {
None
}
} else {
None
}
})
.flatten()
.collect();
for (idx, val) in updates {
new_state_vec[idx] = val;
}
} else {
for i in 0..dim {
let target_bit_1 = (i >> target_qubit_1) & 1;
let target_bit_2 = (i >> target_qubit_2) & 1;
if target_bit_1 != target_bit_2 {
let j = i ^ (1 << target_qubit_1) ^ (1 << target_qubit_2);
if i < j && check_controls(i, control_qubits) {
let amp_i = state.state_vector[i];
let amp_j = state.state_vector[j];
new_state_vec[i] = amp_j;
new_state_vec[j] = amp_i;
}
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits: state.num_qubits(),
})
}
fn base_qubits(&self) -> usize {
2 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for SWAP {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SWAP")
}
}
#[derive(Debug, Clone, Copy)]
pub struct Matchgate {
pub(crate) theta: f64,
pub(crate) phi1: f64,
pub(crate) phi2: f64,
}
impl Matchgate {
pub fn new(theta: f64, phi1: f64, phi2: f64) -> Self {
Matchgate { theta, phi1, phi2 }
}
}
impl Operator for Matchgate {
fn apply(
&self,
state: &State,
target_qubit: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubit, control_qubits, 1)?;
let q1 = target_qubit[0];
if q1 == state.num_qubits() - 1 {
return Err(Error::InvalidQubitIndex(q1, state.num_qubits()));
}
let q2 = q1 + 1;
let num_qubits = state.num_qubits();
let mut new_state_vec = state.state_vector.clone();
let gpu_enabled: bool = cfg!(feature = "gpu");
if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
#[cfg(feature = "gpu")]
{
let global_work_size = 1 << (num_qubits - 2);
let cos_theta_half = (self.theta / 2.0).cos() as f32;
let sin_theta_half = (self.theta / 2.0).sin() as f32;
let exp_i_phi1 = Complex::new(0.0, self.phi1).exp();
let exp_i_phi2 = Complex::new(0.0, self.phi2).exp();
new_state_vec = execute_on_gpu(
state,
q1,
control_qubits,
KernelType::Matchgate,
global_work_size,
GpuKernelArgs::Matchgate {
q1: q2 as i32,
cos_theta_half,
sin_theta_half,
exp_i_phi1: Float2::new(exp_i_phi1.re as f32, exp_i_phi1.im as f32),
exp_i_phi2: Float2::new(exp_i_phi2.re as f32, exp_i_phi2.im as f32),
},
)?;
}
} else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
let cos_theta_half = (self.theta / 2.0).cos();
let sin_theta_half = (self.theta / 2.0).sin();
let exp_i_phi1 = Complex::new(0.0, self.phi1).exp();
let exp_i_phi2 = Complex::new(0.0, self.phi2).exp();
let updates: Vec<(usize, Complex<f64>)> = (0..(1 << (num_qubits - 2)))
.into_par_iter()
.flat_map(|i| {
let k = ((i >> q1) << (q1 + 1)) | (i & ((1 << q1) - 1));
let l = ((k >> (q2 - 1)) << q2) | (k & ((1 << (q2 - 1)) - 1));
let i01 = l | (1 << q1);
let i10 = l | (1 << q2);
let i11 = l | (1 << q1) | (1 << q2);
let mut updates = Vec::new();
if check_controls(i01, control_qubits) {
let amp01 = state.state_vector[i01];
let amp10 = state.state_vector[i10];
let new_amp01 =
cos_theta_half * amp01 - exp_i_phi1 * sin_theta_half * amp10;
let new_amp10 =
sin_theta_half * amp01 + exp_i_phi1 * cos_theta_half * amp10;
updates.push((i01, new_amp01));
updates.push((i10, new_amp10));
}
if check_controls(i11, control_qubits) {
let new_amp11 = state.state_vector[i11] * exp_i_phi2;
updates.push((i11, new_amp11));
}
updates
})
.collect();
for (idx, val) in updates {
new_state_vec[idx] = val;
}
} else {
let cos_theta_half = (self.theta / 2.0).cos();
let sin_theta_half = (self.theta / 2.0).sin();
let exp_i_phi1 = Complex::new(0.0, self.phi1).exp();
let exp_i_phi2 = Complex::new(0.0, self.phi2).exp();
for i in 0..(1 << (num_qubits - 2)) {
let k = ((i >> q1) << (q1 + 1)) | (i & ((1 << q1) - 1));
let l = ((k >> (q2 - 1)) << q2) | (k & ((1 << (q2 - 1)) - 1));
let i01 = l | (1 << q1);
let i10 = l | (1 << q2);
let i11 = l | (1 << q1) | (1 << q2);
if check_controls(i01, control_qubits) {
let amp01 = state.state_vector[i01];
let amp10 = state.state_vector[i10];
new_state_vec[i01] =
cos_theta_half * amp01 - exp_i_phi1 * sin_theta_half * amp10;
new_state_vec[i10] =
sin_theta_half * amp01 + exp_i_phi1 * cos_theta_half * amp10;
}
if check_controls(i11, control_qubits) {
new_state_vec[i11] = state.state_vector[i11] * exp_i_phi2;
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits,
})
}
fn base_qubits(&self) -> usize {
2
}
}
impl std::fmt::Display for Matchgate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Matchgate({:.3}, {:.3}, {:.3})", self.theta, self.phi1, self.phi2)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Toffoli;
impl Operator for Toffoli {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
if control_qubits.len() != 2 {
return Err(Error::InvalidNumberOfQubits(control_qubits.len()));
}
if control_qubits[0] == control_qubits[1] {
return Err(Error::InvalidNumberOfQubits(control_qubits.len()));
}
Pauli::X.apply(state, target_qubits, control_qubits)
}
fn base_qubits(&self) -> usize {
3 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for Toffoli {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Toffoli")
}
}
#[derive(Debug, Clone, Copy)]
pub struct Identity;
impl Operator for Identity {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
Ok(state.clone())
}
fn base_qubits(&self) -> usize {
1 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for Identity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "I")
}
}
#[derive(Debug, Clone, Copy)]
pub struct PhaseS;
impl Operator for PhaseS {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
let target_qubit: usize = target_qubits[0];
let num_qubits: usize = state.num_qubits();
#[allow(unused_assignments)]
let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
let gpu_enabled: bool = cfg!(feature = "gpu");
if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
#[cfg(feature = "gpu")]
{
let global_work_size = 1 << num_qubits;
new_state_vec = execute_on_gpu(
state,
target_qubit,
control_qubits,
KernelType::PhaseSOrSdag,
global_work_size,
GpuKernelArgs::SOrSdag { sign: 1.0f32 },
)?;
}
} else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
let phase_factor = Complex::new(0.0, 1.0); new_state_vec = state.state_vector.clone(); new_state_vec
.par_iter_mut()
.enumerate()
.for_each(|(i, current_amp_ref)| {
if ((i >> target_qubit) & 1 == 1) && check_controls(i, control_qubits) {
*current_amp_ref = state.state_vector[i] * phase_factor;
}
});
} else {
let phase_factor = Complex::new(0.0, 1.0); new_state_vec = state.state_vector.clone(); for (i, current_amp_ref) in new_state_vec.iter_mut().enumerate() {
let target_bit_is_one = (i >> target_qubit) & 1 == 1;
if target_bit_is_one && check_controls(i, control_qubits) {
*current_amp_ref = state.state_vector[i] * phase_factor;
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits,
})
}
fn base_qubits(&self) -> usize {
1 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for PhaseS {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "S")
}
}
#[derive(Debug, Clone, Copy)]
pub struct PhaseT;
impl Operator for PhaseT {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
let target_qubit = target_qubits[0];
let num_qubits = state.num_qubits();
#[allow(unused_assignments)]
let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
let gpu_enabled: bool = cfg!(feature = "gpu");
if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
#[cfg(feature = "gpu")]
{
let global_work_size = 1 << num_qubits;
let angle = PI / 4.0;
new_state_vec = execute_on_gpu(
state,
target_qubit,
control_qubits,
KernelType::PhaseShift,
global_work_size,
GpuKernelArgs::PhaseShift {
cos_angle: angle.cos() as f32,
sin_angle: angle.sin() as f32,
},
)?;
}
} else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
let invsqrt2: f64 = 1.0 / (2.0f64).sqrt();
let phase_factor = Complex::new(invsqrt2, invsqrt2); new_state_vec = state.state_vector.clone(); new_state_vec
.par_iter_mut()
.enumerate()
.for_each(|(i, current_amp_ref)| {
if ((i >> target_qubit) & 1 == 1) && check_controls(i, control_qubits) {
*current_amp_ref = state.state_vector[i] * phase_factor;
}
});
} else {
let invsqrt2: f64 = 1.0 / (2.0f64).sqrt();
let phase_factor = Complex::new(invsqrt2, invsqrt2); new_state_vec = state.state_vector.clone(); for (i, current_amp_ref) in new_state_vec.iter_mut().enumerate() {
let target_bit_is_one = (i >> target_qubit) & 1 == 1;
if target_bit_is_one && check_controls(i, control_qubits) {
*current_amp_ref = state.state_vector[i] * phase_factor;
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits,
})
}
fn base_qubits(&self) -> usize {
1 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for PhaseT {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "T")
}
}
#[derive(Debug, Clone, Copy)]
pub struct PhaseSdag;
impl Operator for PhaseSdag {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
let target_qubit = target_qubits[0];
let num_qubits = state.num_qubits();
#[allow(unused_assignments)]
let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
let gpu_enabled: bool = cfg!(feature = "gpu");
if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
#[cfg(feature = "gpu")]
{
let global_work_size = 1 << num_qubits;
new_state_vec = execute_on_gpu(
state,
target_qubit,
control_qubits,
KernelType::PhaseSOrSdag,
global_work_size,
GpuKernelArgs::SOrSdag { sign: -1.0f32 },
)?;
}
} else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
let phase_factor = Complex::new(0.0, -1.0); new_state_vec = state.state_vector.clone(); new_state_vec
.par_iter_mut()
.enumerate()
.for_each(|(i, current_amp_ref)| {
if ((i >> target_qubit) & 1 == 1) && check_controls(i, control_qubits) {
*current_amp_ref = state.state_vector[i] * phase_factor;
}
});
} else {
let phase_factor = Complex::new(0.0, -1.0); new_state_vec = state.state_vector.clone(); for (i, current_amp_ref) in new_state_vec.iter_mut().enumerate() {
let target_bit_is_one = (i >> target_qubit) & 1 == 1;
if target_bit_is_one && check_controls(i, control_qubits) {
*current_amp_ref = state.state_vector[i] * phase_factor;
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits,
})
}
fn base_qubits(&self) -> usize {
1 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for PhaseSdag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "S†")
}
}
#[derive(Debug, Clone, Copy)]
pub struct PhaseTdag;
impl Operator for PhaseTdag {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
let target_qubit = target_qubits[0];
let num_qubits = state.num_qubits();
#[allow(unused_assignments)]
let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
let gpu_enabled: bool = cfg!(feature = "gpu");
if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
#[cfg(feature = "gpu")]
{
let global_work_size = 1 << num_qubits;
let angle = -PI / 4.0;
new_state_vec = execute_on_gpu(
state,
target_qubit,
control_qubits,
KernelType::PhaseShift,
global_work_size,
GpuKernelArgs::PhaseShift {
cos_angle: angle.cos() as f32,
sin_angle: angle.sin() as f32,
},
)?;
}
} else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
let invsqrt2: f64 = 1.0 / (2.0f64).sqrt();
let phase_factor = Complex::new(invsqrt2, -invsqrt2);
new_state_vec = state.state_vector.clone(); new_state_vec
.par_iter_mut()
.enumerate()
.for_each(|(i, current_amp_ref)| {
if ((i >> target_qubit) & 1 == 1) && check_controls(i, control_qubits) {
*current_amp_ref = state.state_vector[i] * phase_factor;
}
});
} else {
let invsqrt2: f64 = 1.0 / (2.0f64).sqrt();
let phase_factor = Complex::new(invsqrt2, -invsqrt2);
new_state_vec = state.state_vector.clone(); for (i, current_amp_ref) in new_state_vec.iter_mut().enumerate() {
let target_bit_is_one = (i >> target_qubit) & 1 == 1;
if target_bit_is_one && check_controls(i, control_qubits) {
*current_amp_ref = state.state_vector[i] * phase_factor;
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits,
})
}
fn base_qubits(&self) -> usize {
1 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for PhaseTdag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "T†")
}
}
#[derive(Debug, Clone, Copy)]
pub struct PhaseShift {
pub(crate) angle: f64,
}
impl PhaseShift {
pub fn new(angle: f64) -> Self {
PhaseShift { angle }
}
}
impl Operator for PhaseShift {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
let target_qubit = target_qubits[0];
let num_qubits = state.num_qubits();
#[allow(unused_assignments)]
let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
let gpu_enabled: bool = cfg!(feature = "gpu");
if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
#[cfg(feature = "gpu")]
{
let global_work_size = 1 << num_qubits;
new_state_vec = execute_on_gpu(
state,
target_qubit,
control_qubits,
KernelType::PhaseShift,
global_work_size,
GpuKernelArgs::PhaseShift {
cos_angle: self.angle.cos() as f32,
sin_angle: self.angle.sin() as f32,
},
)?;
}
} else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
let phase_factor = Complex::new(self.angle.cos(), self.angle.sin());
new_state_vec = state.state_vector.clone(); new_state_vec
.par_iter_mut()
.enumerate()
.for_each(|(i, current_amp_ref)| {
if ((i >> target_qubit) & 1 == 1) && check_controls(i, control_qubits) {
*current_amp_ref = state.state_vector[i] * phase_factor;
}
});
} else {
let phase_factor = Complex::new(self.angle.cos(), self.angle.sin());
new_state_vec = state.state_vector.clone(); for (i, current_amp_ref) in new_state_vec.iter_mut().enumerate() {
let target_bit_is_one = (i >> target_qubit) & 1 == 1;
if target_bit_is_one && check_controls(i, control_qubits) {
*current_amp_ref = state.state_vector[i] * phase_factor;
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits,
})
}
fn base_qubits(&self) -> usize {
1 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for PhaseShift {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "P({:.3})", self.angle)
}
}
#[derive(Debug, Clone, Copy)]
pub struct RotateX {
pub(crate) angle: f64,
}
impl RotateX {
pub fn new(angle: f64) -> Self {
RotateX { angle }
}
}
impl Operator for RotateX {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
let target_qubit = target_qubits[0];
let num_qubits = state.num_qubits();
#[allow(unused_assignments)]
let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
let gpu_enabled: bool = cfg!(feature = "gpu");
if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
#[cfg(feature = "gpu")]
{
let half_angle = self.angle / 2.0;
let global_work_size = if num_qubits > 0 {
1 << (num_qubits - 1)
} else {
1
};
new_state_vec = execute_on_gpu(
state,
target_qubit,
control_qubits,
KernelType::RotateX,
global_work_size,
GpuKernelArgs::RotationGate {
cos_half_angle: half_angle.cos() as f32,
sin_half_angle: half_angle.sin() as f32,
},
)?;
}
} else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
let half_angle: f64 = self.angle / 2.0;
let cos_half: f64 = half_angle.cos();
let sin_half: f64 = half_angle.sin();
let i_complex: Complex<f64> = Complex::new(0.0, 1.0);
let dim: usize = 1 << num_qubits;
new_state_vec = state.state_vector.clone();
let updates: Vec<(usize, Complex<f64>)> = (0..dim)
.into_par_iter()
.filter_map(|i| {
if ((i >> target_qubit) & 1 == 0) && check_controls(i, control_qubits) {
let j = i | (1 << target_qubit);
let amp_i = state.state_vector[i];
let amp_j = state.state_vector[j];
Some(vec![
(i, cos_half * amp_i - i_complex * sin_half * amp_j),
(j, -i_complex * sin_half * amp_i + cos_half * amp_j),
])
} else {
None
}
})
.flatten()
.collect();
for (idx, val) in updates {
new_state_vec[idx] = val;
}
} else {
let half_angle: f64 = self.angle / 2.0;
let cos_half: f64 = half_angle.cos();
let sin_half: f64 = half_angle.sin();
let i_complex: Complex<f64> = Complex::new(0.0, 1.0);
let dim: usize = 1 << num_qubits;
new_state_vec = state.state_vector.clone();
for i in 0..dim {
if (i >> target_qubit) & 1 == 0 {
let j = i | (1 << target_qubit);
if check_controls(i, control_qubits) {
let amp_i = state.state_vector[i];
let amp_j = state.state_vector[j];
new_state_vec[i] = cos_half * amp_i - i_complex * sin_half * amp_j;
new_state_vec[j] = -i_complex * sin_half * amp_i + cos_half * amp_j;
}
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits: state.num_qubits(),
})
}
fn base_qubits(&self) -> usize {
1 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for RotateX {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RX({:.3})", self.angle)
}
}
#[derive(Debug, Clone, Copy)]
pub struct RotateY {
pub(crate) angle: f64,
}
impl RotateY {
pub fn new(angle: f64) -> Self {
RotateY { angle }
}
}
impl Operator for RotateY {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
let target_qubit = target_qubits[0];
let num_qubits = state.num_qubits();
#[allow(unused_assignments)]
let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
let gpu_enabled: bool = cfg!(feature = "gpu");
if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
#[cfg(feature = "gpu")]
{
let half_angle = self.angle / 2.0;
let global_work_size = if num_qubits > 0 {
1 << (num_qubits - 1)
} else {
1
};
new_state_vec = execute_on_gpu(
state,
target_qubit,
control_qubits,
KernelType::RotateY,
global_work_size,
GpuKernelArgs::RotationGate {
cos_half_angle: half_angle.cos() as f32,
sin_half_angle: half_angle.sin() as f32,
},
)?;
}
} else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
let half_angle: f64 = self.angle / 2.0;
let cos_half: f64 = half_angle.cos();
let sin_half: f64 = half_angle.sin();
let dim: usize = 1 << num_qubits;
new_state_vec = state.state_vector.clone();
let updates: Vec<(usize, Complex<f64>)> = (0..dim)
.into_par_iter()
.filter_map(|i| {
if ((i >> target_qubit) & 1 == 0) && check_controls(i, control_qubits) {
let j = i | (1 << target_qubit);
let amp_i = state.state_vector[i];
let amp_j = state.state_vector[j];
Some(vec![
(i, cos_half * amp_i - sin_half * amp_j),
(j, sin_half * amp_i + cos_half * amp_j),
])
} else {
None
}
})
.flatten()
.collect();
for (idx, val) in updates {
new_state_vec[idx] = val;
}
} else {
let half_angle: f64 = self.angle / 2.0;
let cos_half: f64 = half_angle.cos();
let sin_half: f64 = half_angle.sin();
let dim: usize = 1 << num_qubits;
new_state_vec = state.state_vector.clone();
for i in 0..dim {
if (i >> target_qubit) & 1 == 0 {
let j = i | (1 << target_qubit);
if check_controls(i, control_qubits) {
let amp_i = state.state_vector[i];
let amp_j = state.state_vector[j];
new_state_vec[i] = cos_half * amp_i - sin_half * amp_j;
new_state_vec[j] = sin_half * amp_i + cos_half * amp_j;
}
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits: state.num_qubits(),
})
}
fn base_qubits(&self) -> usize {
1 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for RotateY {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RY({:.3})", self.angle)
}
}
#[derive(Debug, Clone, Copy)]
pub struct RotateZ {
pub(crate) angle: f64,
}
impl RotateZ {
pub fn new(angle: f64) -> Self {
RotateZ { angle }
}
}
impl Operator for RotateZ {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
let target_qubit = target_qubits[0];
let num_qubits = state.num_qubits();
#[allow(unused_assignments)]
let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
let gpu_enabled: bool = cfg!(feature = "gpu");
if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
#[cfg(feature = "gpu")]
{
let half_angle = self.angle / 2.0;
let global_work_size = 1 << num_qubits; new_state_vec = execute_on_gpu(
state,
target_qubit,
control_qubits,
KernelType::RotateZ,
global_work_size,
GpuKernelArgs::RotationGate {
cos_half_angle: half_angle.cos() as f32,
sin_half_angle: half_angle.sin() as f32,
},
)?;
}
} else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
let half_angle = self.angle / 2.0;
let phase_0 = Complex::new(half_angle.cos(), -half_angle.sin());
let phase_1 = Complex::new(half_angle.cos(), half_angle.sin());
new_state_vec = state.state_vector.clone();
new_state_vec
.par_iter_mut()
.enumerate()
.for_each(|(i, current_amp_ref)| {
if check_controls(i, control_qubits) {
let target_bit_is_one = (i >> target_qubit) & 1 == 1;
if target_bit_is_one {
*current_amp_ref = state.state_vector[i] * phase_1;
} else {
*current_amp_ref = state.state_vector[i] * phase_0;
}
}
});
} else {
let half_angle = self.angle / 2.0;
let phase_0 = Complex::new(half_angle.cos(), -half_angle.sin());
let phase_1 = Complex::new(half_angle.cos(), half_angle.sin());
new_state_vec = state.state_vector.clone();
for (i, current_amp_ref) in new_state_vec.iter_mut().enumerate() {
if check_controls(i, control_qubits) {
let target_bit_is_one = (i >> target_qubit) & 1 == 1;
if target_bit_is_one {
*current_amp_ref = state.state_vector[i] * phase_1;
} else {
*current_amp_ref = state.state_vector[i] * phase_0;
}
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits: state.num_qubits(),
})
}
fn base_qubits(&self) -> usize {
1 }
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for RotateZ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RZ({:.3})", self.angle)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Unitary2 {
pub(crate) matrix: [[Complex<f64>; 2]; 2],
pub(crate) is_ryphase: IsRyPhase
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum IsRyPhase {
RYPhase(f64, f64), RYPhaseDagger(f64, f64), None
}
impl Unitary2 {
pub fn new(matrix: [[Complex<f64>; 2]; 2]) -> Result<Self, Error> {
let tol: f64 = f64::EPSILON * 2.0; let a: Complex<f64> = matrix[0][0]; let b: Complex<f64> = matrix[0][1]; let c: Complex<f64> = matrix[1][0]; let d: Complex<f64> = matrix[1][1];
if ((a.norm_sqr() + b.norm_sqr()) - 1.0).abs() > tol {
return Err(Error::NonUnitaryMatrix);
}
if ((c.norm_sqr() + d.norm_sqr()) - 1.0).abs() > tol {
return Err(Error::NonUnitaryMatrix);
}
if (a * c.conj() + b * d.conj()).norm_sqr() > tol * tol {
return Err(Error::NonUnitaryMatrix);
}
Ok(Unitary2 { matrix, is_ryphase: IsRyPhase::None })
}
pub fn from_ry_phase(theta: f64, phi: f64) -> Self {
let cos_half_theta = (theta / 2.0).cos();
let sin_half_theta = (theta / 2.0).sin();
let expi_phi = Complex::new(0.0, phi).exp();
let matrix = [
[
Complex::new(cos_half_theta, 0.0),
-expi_phi * sin_half_theta,
],
[Complex::new(sin_half_theta, 0.0), expi_phi * cos_half_theta],
];
Unitary2 { matrix, is_ryphase: IsRyPhase::RYPhase(theta, phi) }
}
pub fn from_ry_phase_dagger(theta: f64, phi: f64) -> Self {
let cos_half_theta = (theta / 2.0).cos();
let sin_half_theta = (theta / 2.0).sin();
let expi_neg_phi = Complex::new(0.0, -phi).exp();
let matrix = [
[
Complex::new(cos_half_theta, 0.0),
Complex::new(sin_half_theta, 0.0),
],
[
-expi_neg_phi * sin_half_theta,
expi_neg_phi * cos_half_theta,
],
];
Unitary2 { matrix, is_ryphase: IsRyPhase::RYPhaseDagger(theta, phi) }
}
}
impl Operator for Unitary2 {
fn apply(
&self,
state: &State,
target_qubits: &[usize],
control_qubits: &[usize],
) -> Result<State, Error> {
validate_qubits(state, target_qubits, control_qubits, 1)?;
let t: usize = target_qubits[0];
let nq: usize = state.num_qubits();
let dim = 1 << nq;
let mut new_state_vec = state.state_vector.clone();
if nq >= PARALLEL_THRESHOLD_NUM_QUBITS {
let updates: Vec<(usize, Complex<f64>)> = (0..dim)
.into_par_iter()
.filter_map(|i| {
if ((i >> t) & 1 == 0) && check_controls(i, control_qubits) {
let j = i | (1 << t);
let ai = state.state_vector[i];
let aj = state.state_vector[j];
Some(vec![
(i, self.matrix[0][0] * ai + self.matrix[0][1] * aj),
(j, self.matrix[1][0] * ai + self.matrix[1][1] * aj),
])
} else {
None
}
})
.flatten()
.collect();
for (idx, val) in updates {
new_state_vec[idx] = val;
}
} else {
for i in 0..dim {
if (i >> t) & 1 == 0 {
let j = i | (1 << t);
if check_controls(i, control_qubits) {
let ai = state.state_vector[i];
let aj = state.state_vector[j];
new_state_vec[i] = self.matrix[0][0] * ai + self.matrix[0][1] * aj;
new_state_vec[j] = self.matrix[1][0] * ai + self.matrix[1][1] * aj;
}
}
}
}
Ok(State {
state_vector: new_state_vec,
num_qubits: nq,
})
}
fn base_qubits(&self) -> usize {
1
}
fn to_compilable(&self) -> Option<&dyn Compilable> {
Some(self)
}
}
impl std::fmt::Display for Unitary2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.is_ryphase {
IsRyPhase::RYPhase(theta, phi) => write!(f, "RYP({:.3}, {:.3})", theta, phi),
IsRyPhase::RYPhaseDagger(theta, phi) => write!(f, "RYP†({:.3}, {:.3})", theta, phi),
IsRyPhase::None => write!(f, "Unitary2"),
}
}
}