quant_iron/components/
operator.rs

1use crate::{components::state::State, errors::Error};
2use dyn_clone::DynClone;
3use num_complex::Complex;
4use rayon::prelude::*;
5use std::{collections::HashSet, fmt::Debug};
6#[cfg(feature = "gpu")]
7use crate::components::gpu_context::{GPU_CONTEXT, KernelType};
8#[cfg(feature = "gpu")]
9use ocl::prm::Float2;
10#[cfg(feature = "gpu")]
11use std::f64::consts::PI;
12#[cfg(feature = "gpu")]
13use crate::components::gpu_context::GpuKernelArgs;
14use crate::compiler::compilable::Compilable;
15
16/// Threshold for using parallel CPU implementation
17const PARALLEL_THRESHOLD_NUM_QUBITS: usize = 10;
18
19 /// Threshold for using OpenCL (GPU acceleration)
20const OPENCL_THRESHOLD_NUM_QUBITS: usize = 15;
21
22#[cfg(feature = "gpu")]
23fn execute_on_gpu(
24    state: &State,
25    target_qubit: usize,
26    control_qubits: &[usize],
27    kernel_type: KernelType,
28    global_work_size: usize,
29    kernel_args: GpuKernelArgs,
30) -> Result<Vec<Complex<f64>>, Error> {
31    let mut context_guard = GPU_CONTEXT.lock().map_err(|_| Error::GpuContextLockError)?;
32    let context = match *context_guard {
33        Ok(ref mut ctx) => ctx,
34        Err(ref e) => return Err(e.clone()), // Propagate initialisation error
35    };
36
37    let num_qubits = state.num_qubits();
38    let num_state_elements = state.state_vector.len();
39
40    // Ensure buffers are ready and get mutable references
41    let state_buffer_cloned = context.ensure_state_buffer(num_state_elements)?.clone();
42    
43    let control_qubits_i32: Vec<i32> = control_qubits.iter().map(|&q| q as i32).collect();
44    let control_buffer_len = control_qubits_i32.len();
45    let control_buffer_cloned = context.ensure_control_buffer(control_buffer_len)?.clone();
46    
47    let state_vector_f32: Vec<Float2> = state.state_vector.iter()
48        .map(|c| Float2::new(c.re as f32, c.im as f32))
49        .collect();
50    
51    // Copy data to GPU buffers
52    state_buffer_cloned.write(&state_vector_f32).enq()
53        .map_err(|e| Error::OpenCLError(format!("Failed to write to state buffer: {}", e)))?;
54
55    if !control_qubits_i32.is_empty() {
56        control_buffer_cloned.write(&control_qubits_i32).enq()
57            .map_err(|e| Error::OpenCLError(format!("Failed to write to control buffer: {}", e)))?;
58    } else {
59        // Write dummy data if no control qubits
60        let dummy_control_data = vec![0; 1]; // Dummy data for control buffer
61         control_buffer_cloned.write(&dummy_control_data).enq()
62            .map_err(|e| Error::OpenCLError(format!("Failed to write to dummy control buffer: {}", e)))?;
63    }
64
65    let mut kernel_builder = context.pro_que.kernel_builder(kernel_type.name());
66    kernel_builder.global_work_size(global_work_size)
67        .arg(&state_buffer_cloned) // Pass by reference
68        .arg(num_qubits as i32)
69        .arg(target_qubit as i32)
70        .arg(control_buffer_cloned)
71        .arg(control_qubits_i32.len() as i32);
72
73    match kernel_args {
74        GpuKernelArgs::None => {
75            // No additional arguments needed for Hadamard, PauliX, PauliY, PauliZ
76        }
77        GpuKernelArgs::SOrSdag { sign } => {
78            kernel_builder.arg(sign);
79        }
80        GpuKernelArgs::PhaseShift { cos_angle, sin_angle } => {
81            kernel_builder.arg(cos_angle).arg(sin_angle);
82        }
83        GpuKernelArgs::SwapTarget { q1 } => {
84            kernel_builder.arg(q1);
85        }
86        GpuKernelArgs::RotationGate { cos_half_angle, sin_half_angle } => {
87            kernel_builder.arg(cos_half_angle).arg(sin_half_angle);
88        }
89    }
90
91    let kernel = kernel_builder.build()
92        .map_err(|e| Error::OpenCLError(format!("Failed to build kernel '{}': {}", kernel_type.name(), e)))?;
93
94    unsafe {
95        kernel.enq().map_err(|e| Error::OpenCLError(format!("Failed to enqueue kernel: {}", e)))?;
96    }
97
98    let mut state_vector_ocl_result = vec![Float2::new(0.0, 0.0); num_state_elements];
99    // Read data back from state_buffer
100    state_buffer_cloned.read(&mut state_vector_ocl_result).enq()
101        .map_err(|e| Error::OpenCLError(format!("Failed to read state buffer: {}", e)))?;
102
103    Ok(state_vector_ocl_result.iter()
104        .map(|f2| Complex::new(f2[0] as f64, f2[1] as f64))
105        .collect())
106}
107
108/// A trait defining the interface for all operators.
109pub trait Operator: Send + Sync + Debug + DynClone {
110    /// Applies the operator to the given state's target qubits, using the control qubits if required.
111    ///
112    /// # Arguments:
113    ///
114    /// * `state` - The state to apply the operator to.
115    ///
116    /// * `target_qubits` - The target qubits to apply the operator to. If no target qubits are specified, the operator will be applied to all qubits in the state.
117    ///
118    /// * `control_qubits` - The control qubits to apply the operator to.
119    ///
120    /// # Returns:
121    ///
122    /// * The new state after applying the operator.
123    fn apply(
124        &self,
125        state: &State,
126        target_qubits: &[usize],
127        control_qubits: &[usize],
128    ) -> Result<State, Error>;
129
130    /// Returns the number of qubits that the operator acts on.
131    ///
132    /// # Returns:
133    ///
134    /// * The number of qubits that the operator acts on.
135    fn base_qubits(&self) -> usize;
136
137    /// Optionally returns an intermediate representation of the operator for compilation to OpenQASM.
138    /// 
139    /// If you are not planning to compile the operator to an IR, you can ignore this method.
140    /// If you want to compile the operator to QASM, you should implement this method.
141    /// 
142    /// # Returns:
143    ///  * An optional vector of `InstructionIR` representing the operator in an intermediate representation.
144    fn to_compilable(&self) -> Option<&dyn Compilable> {
145        // Default implementation returns None, indicating no compilable representation
146        None
147    }
148}
149
150dyn_clone::clone_trait_object!(Operator);
151
152/// Helper function to check if all control qubits are in the |1> state for a given basis state index.
153fn check_controls(index: usize, control_qubits: &[usize]) -> bool {
154    control_qubits
155        .iter()
156        .all(|&qubit| (index >> qubit) & 1 == 1)
157}
158
159/// Helper function to validate target and control qubits
160///
161/// # Arguments:
162///
163/// * `state` - The quantum state that contains information about the number of qubits.
164/// * `target_qubits` - The target qubits to validate.
165/// * `control_qubits` - The control qubits to validate.
166/// * `expected_targets` - The expected number of target qubits.
167///
168/// # Returns:
169///
170/// * `Ok(())` if all validations pass.
171/// * `Err(Error)` if any validation fails.
172fn validate_qubits(
173    state: &State,
174    target_qubits: &[usize],
175    control_qubits: &[usize],
176    expected_targets: usize,
177) -> Result<(), Error> {
178    // Check if we have the expected number of target qubits
179    if target_qubits.len() != expected_targets {
180        return Err(Error::InvalidNumberOfQubits(target_qubits.len()));
181    }
182
183    let num_qubits = state.num_qubits();
184
185    // Check if all target qubits are valid indices
186    for &target_qubit in target_qubits {
187        if target_qubit >= num_qubits {
188            return Err(Error::InvalidQubitIndex(target_qubit, num_qubits));
189        }
190    }
191
192    // Check if all control qubits are valid indices and don't overlap with target qubits
193    for &control_qubit in control_qubits {
194        if control_qubit >= num_qubits {
195            return Err(Error::InvalidQubitIndex(control_qubit, num_qubits));
196        }
197
198        for &target_qubit in target_qubits {
199            if control_qubit == target_qubit {
200                return Err(Error::OverlappingControlAndTargetQubits(
201                    control_qubit,
202                    target_qubit,
203                ));
204            }
205        }
206    }
207
208    // Special check for multiple target qubits to ensure no duplicates
209    if expected_targets > 1 {
210        if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
211            // Use HashSet for efficient duplicate detection in larger systems
212            let mut seen_targets: HashSet<usize> = HashSet::with_capacity(target_qubits.len());
213            for &target_qubit in target_qubits {
214                if !seen_targets.insert(target_qubit) {
215                    return Err(Error::InvalidQubitIndex(target_qubit, num_qubits));
216                }
217            }
218        } else {
219            // Use nested loops for smaller systems
220            for i in 0..target_qubits.len() {
221                for j in i + 1..target_qubits.len() {
222                    if target_qubits[i] == target_qubits[j] {
223                        return Err(Error::InvalidQubitIndex(target_qubits[i], num_qubits));
224                    }
225                }
226            }
227        }
228    }
229
230    Ok(())
231}
232
233/// Defines a Hadamard operator.
234///
235/// A single-qubit operator that transforms the state of a qubit into a superposition of its basis states.
236#[derive(Debug, Clone, Copy)]
237pub struct Hadamard;
238
239impl Operator for Hadamard {
240    /// Applies the Hadamard operator to the given state's target qubit.
241    ///
242    /// # Arguments:
243    ///
244    /// * `state` - The state to apply the operator to.
245    ///
246    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
247    ///
248    /// * `control_qubits` - The control qubits for the operator. If not empty, the operator will be applied conditionally based on the control qubits. Otherwise, it will be applied unconditionally.
249    ///
250    /// # Returns:
251    ///
252    /// * The new state after applying the Hadamard operator.
253    ///
254    /// # Errors:
255    ///
256    /// * `Error::InvalidNumberOfQubits` - If the target qubits is not 1.
257    ///
258    /// * `Error::InvalidQubitIndex` - If the target qubit or control qubit index is invalid for the number of qubits in the state.
259    ///
260    /// * `Error::OverlappingControlAndTargetQubits` - If the control qubit and target qubit indices overlap.
261    fn apply(
262        &self,
263        state: &State,
264        target_qubits: &[usize],
265        control_qubits: &[usize],
266    ) -> Result<State, Error> {
267        // Validation
268        validate_qubits(state, target_qubits, control_qubits, 1)?;
269
270        let target_qubit: usize = target_qubits[0];
271        let num_qubits: usize = state.num_qubits();
272
273        // Apply potentially controlled Hadamard operator
274        let sqrt_2_inv: f64 = 1.0 / (2.0f64).sqrt();
275        let dim: usize = 1 << num_qubits;
276        #[allow(unused_assignments)]
277        let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
278        let gpu_enabled: bool = cfg!(feature = "gpu");
279
280        if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
281            #[cfg(feature = "gpu")]
282            {
283                let global_work_size = if num_qubits > 0 { 1 << (num_qubits - 1) } else { 1 };
284                new_state_vec = execute_on_gpu(
285                    state,
286                    target_qubit,
287                    control_qubits,
288                    KernelType::Hadamard,
289                    global_work_size,
290                    GpuKernelArgs::None,
291                )?;
292            }
293        } else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
294            // Rayon CPU Parallel implementation
295            new_state_vec = state.state_vector.clone(); // Initialise for CPU path
296            if control_qubits.is_empty() {
297                // Parallel uncontrolled Hadamard
298                let updates: Vec<(usize, Complex<f64>)> = (0..(1 << (num_qubits - 1)))
299                    .into_par_iter()
300                    .flat_map(|k| {
301                        let i0 = (k >> target_qubit << (target_qubit + 1))
302                            | (k & ((1 << target_qubit) - 1));
303                        let i1 = i0 | (1 << target_qubit);
304                        let amp0 = state.state_vector[i0];
305                        let amp1 = state.state_vector[i1];
306                        vec![
307                            (i0, sqrt_2_inv * (amp0 + amp1)),
308                            (i1, sqrt_2_inv * (amp0 - amp1)),
309                        ]
310                    })
311                    .collect();
312                for (idx, val) in updates {
313                    new_state_vec[idx] = val;
314                }
315            } else {
316                // Rayon CPU Parallel controlled Hadamard
317                let updates: Vec<(usize, Complex<f64>)> = (0..dim)
318                    .into_par_iter()
319                    .filter_map(|i| {
320                        if (i >> target_qubit) & 1 == 0 { // Process pairs (i, j) where i has 0 at target_qubit
321                            let j = i | (1 << target_qubit); // j has 1 at target_qubit
322                            if check_controls(i, control_qubits) { // Check controls based on i
323                                let amp_i = state.state_vector[i];
324                                let amp_j = state.state_vector[j];
325                                Some(vec![
326                                    (i, sqrt_2_inv * (amp_i + amp_j)),
327                                    (j, sqrt_2_inv * (amp_i - amp_j)),
328                                ])
329                            } else {
330                                None // Controls not met for this pair
331                            }
332                        } else {
333                            None // Already processed as part of a pair starting with 0 at target_qubit
334                        }
335                    })
336                    .flatten()
337                    .collect();
338                for (idx, val) in updates {
339                    new_state_vec[idx] = val;
340                }
341            }
342        } else {
343            // Sequential CPU implementation
344            new_state_vec = state.state_vector.clone(); // initialise for CPU path
345            if control_qubits.is_empty() {
346                // Sequential uncontrolled Hadamard
347                for k in 0..(1 << (num_qubits - 1)) {
348                    let i0 =
349                        (k >> target_qubit << (target_qubit + 1)) | (k & ((1 << target_qubit) - 1));
350                    let i1 = i0 | (1 << target_qubit);
351                    let amp0 = state.state_vector[i0];
352                    let amp1 = state.state_vector[i1];
353                    new_state_vec[i0] = sqrt_2_inv * (amp0 + amp1);
354                    new_state_vec[i1] = sqrt_2_inv * (amp0 - amp1);
355                }
356            } else {
357                // Sequential controlled Hadamard
358                for i in 0..dim {
359                    if (i >> target_qubit) & 1 == 0 {
360                        let j = i | (1 << target_qubit);
361                        if check_controls(i, control_qubits) {
362                            let amp_i = state.state_vector[i];
363                            let amp_j = state.state_vector[j];
364                            new_state_vec[i] = sqrt_2_inv * (amp_i + amp_j);
365                            new_state_vec[j] = sqrt_2_inv * (amp_i - amp_j);
366                        }
367                    }
368                }
369            }
370        }
371
372        Ok(State {
373            state_vector: new_state_vec,
374            num_qubits,
375        })
376    }
377
378    fn base_qubits(&self) -> usize {
379        1 // Hadamard acts on 1 qubit only
380    }
381
382    fn to_compilable(&self) -> Option<&dyn Compilable> {
383        Some(self)
384    }
385}
386
387/// Defines the Pauli operators: X, Y, Z.
388#[derive(Debug, Clone, Copy, PartialEq)]
389pub enum Pauli {
390    /// Pauli-X operator (NOT gate)
391    X,
392    /// Pauli-Y operator
393    Y,
394    /// Pauli-Z operator
395    Z,
396}
397
398impl Operator for Pauli {
399    /// Applies the Pauli operator to the given state's target qubit.
400    ///
401    /// # Arguments:
402    ///
403    /// * `state` - The state to apply the operator to.
404    ///
405    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
406    ///
407    /// * `control_qubits` - The control qubits for the operator. If not empty, the operator will be applied conditionally based on the control qubits. Otherwise, it will be applied unconditionally.
408    ///
409    /// # Returns:
410    ///
411    /// * The new state after applying the Pauli operator.
412    ///
413    /// # Errors:
414    ///
415    /// * `Error::InvalidNumberOfQubits` - If the target qubits is not 1.
416    ///
417    /// * `Error::InvalidQubitIndex` - If the target qubit index is invalid for the number of qubits in the state.
418    ///
419    /// * `Error::OverlappingControlAndTargetQubits` - If the control qubit and target qubit indices overlap.
420    fn apply(
421        &self,
422        state: &State,
423        target_qubits: &[usize],
424        control_qubits: &[usize],
425    ) -> Result<State, Error> {
426        // Validation
427        validate_qubits(state, target_qubits, control_qubits, 1)?;
428
429        let target_qubit: usize = target_qubits[0];
430        let num_qubits: usize = state.num_qubits();
431
432        // Apply potentially controlled Pauli operator
433        let dim: usize = 1 << num_qubits;
434        let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
435        let i_complex: Complex<f64> = Complex::new(0.0, 1.0);
436        let gpu_enabled: bool = cfg!(feature = "gpu");
437
438        if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
439            #[cfg(feature = "gpu")]
440            {
441                let kernel_type = match self {
442                    Pauli::X => KernelType::PauliX,
443                    Pauli::Y => KernelType::PauliY,
444                    Pauli::Z => KernelType::PauliZ,
445                };
446                let global_work_size = if num_qubits == 0 {
447                    1
448                } else {
449                    match self {
450                        Pauli::Z => 1 << num_qubits, // N work items for Pauli Z
451                        _ => 1 << (num_qubits - 1),   // N/2 work items for Pauli X, Y
452                    }
453                };
454                new_state_vec = execute_on_gpu(
455                    state,
456                    target_qubit,
457                    control_qubits,
458                    kernel_type,
459                    global_work_size,
460                    GpuKernelArgs::None,
461                )?;
462            }
463        } else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
464            // Parallel implementation
465            match self {
466                Pauli::X => {
467                    let updates: Vec<(usize, Complex<f64>)> = (0..dim)
468                        .into_par_iter()
469                        .filter_map(|i| {
470                            if check_controls(i, control_qubits) && ((i >> target_qubit) & 1 == 0) {
471                                let j = i | (1 << target_qubit);
472                                let amp_i = state.state_vector[i];
473                                let amp_j = state.state_vector[j];
474                                Some(vec![(i, amp_j), (j, amp_i)])
475                            } else {
476                                None
477                            }
478                        })
479                        .flatten()
480                        .collect();
481                    for (idx, val) in updates {
482                        new_state_vec[idx] = val;
483                    }
484                }
485                Pauli::Y => {
486                    let updates: Vec<(usize, Complex<f64>)> = (0..dim)
487                        .into_par_iter()
488                        .filter_map(|i| {
489                            if check_controls(i, control_qubits) && ((i >> target_qubit) & 1 == 0) {
490                                let j = i | (1 << target_qubit);
491                                let amp_i = state.state_vector[i];
492                                let amp_j = state.state_vector[j];
493                                Some(vec![(i, -i_complex * amp_j), (j, i_complex * amp_i)])
494                            } else {
495                                None
496                            }
497                        })
498                        .flatten()
499                        .collect();
500                    for (idx, val) in updates {
501                        new_state_vec[idx] = val;
502                    }
503                }
504                Pauli::Z => {
505                    new_state_vec
506                        .par_iter_mut()
507                        .enumerate()
508                        .for_each(|(i, current_amp_ref)| {
509                            if check_controls(i, control_qubits) && ((i >> target_qubit) & 1 == 1) {
510                                *current_amp_ref = -state.state_vector[i];
511                            }
512                        });
513                }
514            }
515        } else {
516            // Sequential implementation
517            for i in 0..dim {
518                if check_controls(i, control_qubits) {
519                    match self {
520                        Pauli::X => {
521                            if (i >> target_qubit) & 1 == 0 {
522                                let j = i | (1 << target_qubit);
523                                let amp_i = state.state_vector[i];
524                                let amp_j = state.state_vector[j];
525                                new_state_vec[i] = amp_j;
526                                new_state_vec[j] = amp_i;
527                            }
528                        }
529                        Pauli::Y => {
530                            if (i >> target_qubit) & 1 == 0 {
531                                let j = i | (1 << target_qubit);
532                                let amp_i = state.state_vector[i];
533                                let amp_j = state.state_vector[j];
534                                new_state_vec[i] = -i_complex * amp_j;
535                                new_state_vec[j] = i_complex * amp_i;
536                            }
537                        }
538                        Pauli::Z => {
539                            if (i >> target_qubit) & 1 == 1 {
540                                new_state_vec[i] = -state.state_vector[i];
541                            }
542                        }
543                    }
544                }
545            }
546        }
547
548        Ok(State {
549            state_vector: new_state_vec,
550            num_qubits: state.num_qubits(),
551        })
552    }
553
554    fn base_qubits(&self) -> usize {
555        1 // Pauli operators act on 1 qubit only
556    }
557
558    fn to_compilable(&self) -> Option<&dyn Compilable> {
559        Some(self) // Manual implementation for enum
560    }
561}
562
563impl std::fmt::Display for Pauli {
564    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
565        match self {
566            Pauli::X => write!(f, "X"),
567            Pauli::Y => write!(f, "Y"),
568            Pauli::Z => write!(f, "Z"),
569        }
570    }
571}
572
573/// Defines a CNOT operator.
574///
575/// A two-qubit operator that flips the target qubit if the control qubit is in the |1> state.
576#[derive(Debug, Clone, Copy)]
577pub struct CNOT;
578
579impl Operator for CNOT {
580    /// Applies the CNOT operator to the given state's target qubit, using the control qubit.
581    ///
582    /// # Arguments:
583    ///
584    /// * `state` - The state to apply the operator to.
585    ///
586    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
587    ///
588    /// * `control_qubits` - The control qubits for the operator. This should be a single qubit.
589    ///
590    /// # Returns:
591    ///
592    /// * The new state after applying the CNOT operator.
593    ///
594    /// # Errors:
595    ///
596    /// * `Error::InvalidNumberOfQubits` - If the target or control qubits is not 1.
597    ///
598    /// * `Error::InvalidQubitIndex` - If the target or control qubit index is invalid for the number of qubits in the state.
599    ///
600    /// * `Error::OverlappingControlAndTargetQubits` - If the control qubit and target qubit indices overlap.
601    fn apply(
602        &self,
603        state: &State,
604        target_qubits: &[usize],
605        control_qubits: &[usize],
606    ) -> Result<State, Error> {
607        // Validation
608        validate_qubits(state, target_qubits, control_qubits, 1)?;
609
610        // Additional validation for CNOT: exactly one control qubit
611        if control_qubits.len() != 1 {
612            return Err(Error::InvalidNumberOfQubits(control_qubits.len()));
613        }
614
615        let control_qubit: usize = control_qubits[0];
616
617        // Apply CNOT operator (same as Pauli-X with 1 control qubit)
618        Pauli::X.apply(state, target_qubits, &[control_qubit])
619    }
620
621    fn base_qubits(&self) -> usize {
622        2 // CNOT acts on 2 qubits (1 control, 1 target)
623    }
624
625    fn to_compilable(&self) -> Option<&dyn Compilable> {
626        Some(self)
627    }
628}
629
630/// Defines a SWAP operator.
631///
632/// A two-qubit operator that swaps the states of the two qubits.
633#[derive(Debug, Clone, Copy)]
634pub struct SWAP;
635
636impl Operator for SWAP {
637    /// Applies the SWAP operator to the given state's target qubits.
638    ///
639    /// # Arguments:
640    ///
641    /// * `state` - The state to apply the operator to.
642    ///
643    /// * `target_qubits` - The target qubits to apply the operator to. This should be two qubits.
644    ///
645    /// * `control_qubits` - The control qubits. If empty, the swap is unconditional. Otherwise, the swap occurs only if all control qubits are |1> for the relevant basis states.
646    /// # Returns:
647    ///
648    /// * The new state after applying the SWAP operator.
649    ///
650    /// # Errors:
651    ///
652    /// * `Error::InvalidNumberOfQubits` - If the target qubits are not 2 different qubits.
653    ///
654    /// * `Error::InvalidQubitIndex` - If the target qubit indices are invalid for the number of qubits in the state.
655    ///
656    /// * `Error::InvalidQubitIndex` - If the target qubit indices are not different.
657    ///
658    /// * `Error::OverlappingControlAndTargetQubits` - If the control qubit and target qubit indices overlap.
659    fn apply(
660        &self,
661        state: &State,
662        target_qubits: &[usize],
663        control_qubits: &[usize],
664    ) -> Result<State, Error> {
665        // Validation
666        validate_qubits(state, target_qubits, control_qubits, 2)?;
667
668        let target_qubit_1: usize = target_qubits[0];
669        let target_qubit_2: usize = target_qubits[1];
670        let num_qubits: usize = state.num_qubits();
671
672        // Apply potentially controlled SWAP operator
673        let dim: usize = 1 << num_qubits;
674        #[allow(unused_assignments)] // new_state_vec might be reassigned by GPU path
675        let mut new_state_vec = state.state_vector.clone(); // Start with a copy
676        let gpu_enabled: bool = cfg!(feature = "gpu");
677
678        if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
679            #[cfg(feature = "gpu")]
680            {
681                // For SWAP, global_work_size is 2^(N-2) because each work item handles
682                // a pair of states differing at target_qubit_1 and target_qubit_2.
683                // The kernel iterates over the 2^(N-2) combinations of other qubits.
684                let global_work_size = if num_qubits >= 2 { 1 << (num_qubits - 2) } else { 1 }; // Handle N=0,1 edge cases for work size
685                new_state_vec = execute_on_gpu(
686                    state,
687                    target_qubit_1, // target_a in kernel
688                    control_qubits,
689                    KernelType::Swap,
690                    global_work_size,
691                    GpuKernelArgs::SwapTarget { q1: target_qubit_2 as i32 }, // target_b in kernel
692                )?;
693            }
694        } else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
695            // Parallel implementation
696            let updates: Vec<(usize, Complex<f64>)> = (0..dim)
697                .into_par_iter()
698                .filter_map(|i| {
699                    let target_bit_1 = (i >> target_qubit_1) & 1;
700                    let target_bit_2 = (i >> target_qubit_2) & 1;
701
702                    if target_bit_1 != target_bit_2 {
703                        let j = i ^ (1 << target_qubit_1) ^ (1 << target_qubit_2);
704                        if i < j && check_controls(i, control_qubits) {
705                            let amp_i = state.state_vector[i];
706                            let amp_j = state.state_vector[j];
707                            Some(vec![(i, amp_j), (j, amp_i)])
708                        } else {
709                            None
710                        }
711                    } else {
712                        None
713                    }
714                })
715                .flatten()
716                .collect();
717            for (idx, val) in updates {
718                new_state_vec[idx] = val;
719            }
720        } else {
721            // Sequential implementation
722            for i in 0..dim {
723                let target_bit_1 = (i >> target_qubit_1) & 1;
724                let target_bit_2 = (i >> target_qubit_2) & 1;
725
726                if target_bit_1 != target_bit_2 {
727                    let j = i ^ (1 << target_qubit_1) ^ (1 << target_qubit_2);
728                    if i < j {
729                        if check_controls(i, control_qubits) {
730                            let amp_i = state.state_vector[i];
731                            let amp_j = state.state_vector[j];
732                            new_state_vec[i] = amp_j;
733                            new_state_vec[j] = amp_i;
734                        }
735                    }
736                }
737            }
738        }
739
740        Ok(State {
741            state_vector: new_state_vec,
742            num_qubits: state.num_qubits(),
743        })
744    }
745
746    fn base_qubits(&self) -> usize {
747        2 // SWAP acts on 2 qubits
748    }
749
750    fn to_compilable(&self) -> Option<&dyn Compilable> {
751        Some(self)
752    }
753}
754
755/// Defines a Toffoli operator.
756///
757/// A three-qubit operator that flips the target qubit if both control qubits are in the |1> state. Also known as CCNOT (Controlled-Controlled-NOT).
758#[derive(Debug, Clone, Copy)]
759pub struct Toffoli;
760
761impl Operator for Toffoli {
762    /// Applies the Toffoli operator to the given state's target qubit, using the control qubits.
763    ///
764    /// # Arguments:
765    ///
766    /// * `state` - The state to apply the operator to.
767    ///
768    /// * `target_qubits` - The target qubit to apply the operator to. This should be a single qubit.
769    ///
770    /// * `control_qubits` - The control qubits for the operator. This should be two qubits.
771    ///
772    /// # Returns:
773    ///
774    /// * The new state after applying the Toffoli operator.
775    ///
776    /// # Errors:
777    ///
778    /// * `Error::InvalidNumberOfQubits` - If the target or control qubits are not 1 and 2 respectively, or if the control qubits are not different.
779    ///
780    /// * `Error::InvalidQubitIndex` - If the target or control qubit indices are invalid for the number of qubits in the state.
781    ///
782    /// * `Error::OverlappingControlAndTargetQubits` - If the control qubit and target qubit indices overlap.
783    fn apply(
784        &self,
785        state: &State,
786        target_qubits: &[usize],
787        control_qubits: &[usize],
788    ) -> Result<State, Error> {
789        // Validation
790        validate_qubits(state, target_qubits, control_qubits, 1)?;
791
792        // Additional validation for Toffoli: exactly two control qubits
793        if control_qubits.len() != 2 {
794            return Err(Error::InvalidNumberOfQubits(control_qubits.len()));
795        }
796
797        // Additional validation for Toffoli: control qubits must be different
798        if control_qubits[0] == control_qubits[1] {
799            return Err(Error::InvalidNumberOfQubits(control_qubits.len()));
800        }
801
802        Pauli::X.apply(state, target_qubits, control_qubits)
803    }
804
805    fn base_qubits(&self) -> usize {
806        3 // Toffoli acts on 3 qubits (2 control, 1 target)
807    }
808
809    fn to_compilable(&self) -> Option<&dyn Compilable> {
810        Some(self)
811    }
812}
813
814/// Defines an identity operator
815///
816/// A single-qubit operator that does not change the state of the qubit.
817#[derive(Debug, Clone, Copy)]
818pub struct Identity;
819
820impl Operator for Identity {
821    /// Applies the identity operator to the given state's target qubit.
822    ///
823    /// # Arguments:
824    ///
825    /// * `state` - The state to apply the operator to.
826    ///
827    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
828    ///
829    /// * `control_qubits` - The control qubits for the operator. If not empty, the operator will be applied conditionally based on the control qubits. Otherwise, it will be applied unconditionally.
830    ///
831    /// # Returns:
832    ///
833    /// * The new state after applying the identity operator.
834    fn apply(
835        &self,
836        state: &State,
837        target_qubits: &[usize],
838        control_qubits: &[usize],
839    ) -> Result<State, Error> {
840        // Validation
841        validate_qubits(state, target_qubits, control_qubits, 1)?;
842
843        // Apply identity operator (no change)
844        Ok(state.clone())
845    }
846
847    fn base_qubits(&self) -> usize {
848        1 // Identity acts on 1 qubit only
849    }
850
851    fn to_compilable(&self) -> Option<&dyn Compilable> {
852        Some(self)
853    }
854}
855
856/// Defines a Phase S operator.
857///
858/// A single-qubit operator that applies a phase shift to the |1> state. Also known as the S gate or Phase gate.
859#[derive(Debug, Clone, Copy)]
860pub struct PhaseS;
861
862impl Operator for PhaseS {
863    /// Applies the Phase S operator to the given state's target qubit.
864    ///
865    /// # Arguments:
866    ///
867    /// * `state` - The state to apply the operator to.
868    ///
869    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
870    ///
871    /// * `control_qubits` - The control qubits for the operator. If not empty, the operator will be applied conditionally based on the control qubits. Otherwise, it will be applied unconditionally.
872    ///
873    /// # Returns:
874    ///
875    /// * The new state after applying the Phase S operator.
876    fn apply(
877        &self,
878        state: &State,
879        target_qubits: &[usize],
880        control_qubits: &[usize],
881    ) -> Result<State, Error> {
882        // Validation
883        validate_qubits(state, target_qubits, control_qubits, 1)?;
884
885        let target_qubit: usize = target_qubits[0];
886        let num_qubits: usize = state.num_qubits();
887
888        // Apply potentially controlled Phase S operator
889        #[allow(unused_assignments)]
890        let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
891        let gpu_enabled: bool = cfg!(feature = "gpu");
892
893        if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
894            #[cfg(feature = "gpu")]
895            {
896                let global_work_size = 1 << num_qubits;
897                new_state_vec = execute_on_gpu(
898                    state,
899                    target_qubit,
900                    control_qubits,
901                    KernelType::PhaseSOrSdag,
902                    global_work_size,
903                    GpuKernelArgs::SOrSdag { sign: 1.0f32 },
904                )?;
905            }
906        } else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
907            let phase_factor = Complex::new(0.0, 1.0); // Phase shift of pi/2 (i)
908            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
909            new_state_vec
910                .par_iter_mut()
911                .enumerate()
912                .for_each(|(i, current_amp_ref)| {
913                    if ((i >> target_qubit) & 1 == 1) && check_controls(i, control_qubits) {
914                        *current_amp_ref = state.state_vector[i] * phase_factor;
915                    }
916                });
917        } else {
918            let phase_factor = Complex::new(0.0, 1.0); // Phase shift of pi/2 (i)
919            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
920            let dim: usize = 1 << num_qubits;
921            for i in 0..dim {
922                let target_bit_is_one = (i >> target_qubit) & 1 == 1;
923                if target_bit_is_one && check_controls(i, control_qubits) {
924                    new_state_vec[i] = state.state_vector[i] * phase_factor;
925                }
926            }
927        }
928
929        Ok(State {
930            state_vector: new_state_vec,
931            num_qubits,
932        })
933    }
934
935    fn base_qubits(&self) -> usize {
936        1 // Phase S acts on 1 qubit only
937    }
938
939    fn to_compilable(&self) -> Option<&dyn Compilable> {
940        Some(self)
941    }
942}
943
944/// Defines a Phase T operator.
945///
946/// A single-qubit operator that applies a phase shift to the |1> state. Also known as the T gate or π/8 gate.
947#[derive(Debug, Clone, Copy)]
948pub struct PhaseT;
949
950impl Operator for PhaseT {
951    /// Applies the Phase T operator to the given state's target qubit.
952    ///
953    /// # Arguments:
954    ///
955    /// * `state` - The state to apply the operator to.
956    ///
957    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
958    ///
959    /// * `control_qubits` - The control qubits for the operator. If not empty, the operator will be applied conditionally based on the control qubits. Otherwise, it will be applied unconditionally.
960    ///
961    /// # Returns:
962    ///
963    /// * The new state after applying the Phase T operator.
964    fn apply(
965        &self,
966        state: &State,
967        target_qubits: &[usize],
968        control_qubits: &[usize],
969    ) -> Result<State, Error> {
970        // Validation
971        validate_qubits(state, target_qubits, control_qubits, 1)?;
972
973        let target_qubit = target_qubits[0];
974        let num_qubits = state.num_qubits();
975
976        // Apply potentially controlled Phase T operator
977        #[allow(unused_assignments)]
978        let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
979        let gpu_enabled: bool = cfg!(feature = "gpu");
980
981        if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
982            #[cfg(feature = "gpu")]
983            {
984                let global_work_size = 1 << num_qubits;
985                let angle = PI / 4.0;
986                new_state_vec = execute_on_gpu(
987                    state,
988                    target_qubit,
989                    control_qubits,
990                    KernelType::PhaseShift,
991                    global_work_size,
992                    GpuKernelArgs::PhaseShift {
993                        cos_angle: angle.cos() as f32,
994                        sin_angle: angle.sin() as f32,
995                    },
996                )?;
997            }
998        } else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
999            let invsqrt2: f64 = 1.0 / (2.0f64).sqrt();
1000            let phase_factor = Complex::new(invsqrt2, invsqrt2); // Phase shift of pi/4 (exp(i*pi/4))
1001            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1002            new_state_vec
1003                .par_iter_mut()
1004                .enumerate()
1005                .for_each(|(i, current_amp_ref)| {
1006                    if ((i >> target_qubit) & 1 == 1) && check_controls(i, control_qubits) {
1007                        *current_amp_ref = state.state_vector[i] * phase_factor;
1008                    }
1009                });
1010        } else {
1011            let invsqrt2: f64 = 1.0 / (2.0f64).sqrt();
1012            let phase_factor = Complex::new(invsqrt2, invsqrt2); // Phase shift of pi/4 (exp(i*pi/4))
1013            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1014            let dim: usize = 1 << num_qubits;
1015            for i in 0..dim {
1016                let target_bit_is_one = (i >> target_qubit) & 1 == 1;
1017                if target_bit_is_one && check_controls(i, control_qubits) {
1018                    new_state_vec[i] = state.state_vector[i] * phase_factor;
1019                }
1020            }
1021        }
1022
1023        Ok(State {
1024            state_vector: new_state_vec,
1025            num_qubits,
1026        })
1027    }
1028
1029    fn base_qubits(&self) -> usize {
1030        1 // Phase T acts on 1 qubit only
1031    }
1032
1033    fn to_compilable(&self) -> Option<&dyn Compilable> {
1034        Some(self)
1035    }
1036}
1037
1038/// Defines a Phase Sdag operator.
1039///
1040/// A single-qubit operator that applies a phase shift to the |1> state. Also known as the S† gate or Phase† gate. Inverse of S gate.
1041#[derive(Debug, Clone, Copy)]
1042pub struct PhaseSdag;
1043
1044impl Operator for PhaseSdag {
1045    /// Applies the Phase Sdag operator to the given state's target qubit.
1046    ///
1047    /// # Arguments:
1048    ///
1049    /// * `state` - The state to apply the operator to.
1050    ///
1051    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
1052    ///
1053    /// * `control_qubits` - The control qubits for the operator. If not empty, the operator will be applied conditionally based on the control qubits. Otherwise, it will be applied unconditionally.
1054    ///
1055    /// # Returns:
1056    ///
1057    /// * The new state after applying the Phase Sdag operator.
1058    fn apply(
1059        &self,
1060        state: &State,
1061        target_qubits: &[usize],
1062        control_qubits: &[usize],
1063    ) -> Result<State, Error> {
1064        // Validation
1065        validate_qubits(state, target_qubits, control_qubits, 1)?;
1066
1067        let target_qubit = target_qubits[0];
1068        let num_qubits = state.num_qubits();
1069
1070        // Apply potentially controlled Phase Sdag operator
1071        #[allow(unused_assignments)]
1072        let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
1073        let gpu_enabled: bool = cfg!(feature = "gpu");
1074
1075        if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
1076            #[cfg(feature = "gpu")]
1077            {
1078                let global_work_size = 1 << num_qubits;
1079                new_state_vec = execute_on_gpu(
1080                    state,
1081                    target_qubit,
1082                    control_qubits,
1083                    KernelType::PhaseSOrSdag,
1084                    global_work_size,
1085                    GpuKernelArgs::SOrSdag { sign: -1.0f32 },
1086                )?;
1087            }
1088        } else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
1089            let phase_factor = Complex::new(0.0, -1.0); // Phase shift of -pi/2 (-i)
1090            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1091            new_state_vec
1092                .par_iter_mut()
1093                .enumerate()
1094                .for_each(|(i, current_amp_ref)| {
1095                    if ((i >> target_qubit) & 1 == 1) && check_controls(i, control_qubits) {
1096                        *current_amp_ref = state.state_vector[i] * phase_factor;
1097                    }
1098                });
1099        } else {
1100            let phase_factor = Complex::new(0.0, -1.0); // Phase shift of -pi/2 (-i)
1101            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1102            let dim: usize = 1 << num_qubits;
1103            for i in 0..dim {
1104                let target_bit_is_one = (i >> target_qubit) & 1 == 1;
1105                if target_bit_is_one && check_controls(i, control_qubits) {
1106                    new_state_vec[i] = state.state_vector[i] * phase_factor;
1107                }
1108            }
1109        }
1110
1111        Ok(State {
1112            state_vector: new_state_vec,
1113            num_qubits,
1114        })
1115    }
1116
1117    fn base_qubits(&self) -> usize {
1118        1 // Phase Sdag acts on 1 qubit only
1119    }
1120
1121    fn to_compilable(&self) -> Option<&dyn Compilable> {
1122        Some(self)
1123    }
1124}
1125
1126/// Defines a Phase Tdag operator.
1127///
1128/// A single-qubit operator that applies a phase shift to the |1> state. Also known as the T† gate or π/8† gate. Inverse of T gate.
1129#[derive(Debug, Clone, Copy)]
1130pub struct PhaseTdag;
1131
1132impl Operator for PhaseTdag {
1133    /// Applies the Phase Tdag operator to the given state's target qubit.
1134    ///
1135    /// # Arguments:
1136    ///
1137    /// * `state` - The state to apply the operator to.
1138    ///
1139    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
1140    ///
1141    /// * `control_qubits` - The control qubits for the operator. If not empty, the operator will be applied conditionally based on the control qubits. Otherwise, it will be applied unconditionally.
1142    ///
1143    /// # Returns:
1144    ///
1145    /// * The new state after applying the Phase Tdag operator.
1146    fn apply(
1147        &self,
1148        state: &State,
1149        target_qubits: &[usize],
1150        control_qubits: &[usize],
1151    ) -> Result<State, Error> {
1152        // Validation
1153        validate_qubits(state, target_qubits, control_qubits, 1)?;
1154
1155        let target_qubit = target_qubits[0];
1156        let num_qubits = state.num_qubits();
1157
1158        // Apply potentially controlled Phase Tdag operator
1159        #[allow(unused_assignments)]
1160        let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
1161        let gpu_enabled: bool = cfg!(feature = "gpu");
1162
1163        if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
1164            #[cfg(feature = "gpu")]
1165            {
1166                let global_work_size = 1 << num_qubits;
1167                let angle = -PI / 4.0;
1168                new_state_vec = execute_on_gpu(
1169                    state,
1170                    target_qubit,
1171                    control_qubits,
1172                    KernelType::PhaseShift,
1173                    global_work_size,
1174                    GpuKernelArgs::PhaseShift {
1175                        cos_angle: angle.cos() as f32,
1176                        sin_angle: angle.sin() as f32,
1177                    },
1178                )?;
1179            }
1180        } else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
1181            let invsqrt2: f64 = 1.0 / (2.0f64).sqrt();
1182            let phase_factor = Complex::new(invsqrt2, -invsqrt2);
1183            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1184            new_state_vec
1185                .par_iter_mut()
1186                .enumerate()
1187                .for_each(|(i, current_amp_ref)| {
1188                    if ((i >> target_qubit) & 1 == 1) && check_controls(i, control_qubits) {
1189                        *current_amp_ref = state.state_vector[i] * phase_factor;
1190                    }
1191                });
1192        } else {
1193            let invsqrt2: f64 = 1.0 / (2.0f64).sqrt();
1194            let phase_factor = Complex::new(invsqrt2, -invsqrt2);
1195            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1196            let dim: usize = 1 << num_qubits;
1197            for i in 0..dim {
1198                let target_bit_is_one = (i >> target_qubit) & 1 == 1;
1199                if target_bit_is_one && check_controls(i, control_qubits) {
1200                    new_state_vec[i] = state.state_vector[i] * phase_factor;
1201                }
1202            }
1203        }
1204
1205        Ok(State {
1206            state_vector: new_state_vec,
1207            num_qubits,
1208        })
1209    }
1210
1211    fn base_qubits(&self) -> usize {
1212        1 // Phase Tdag acts on 1 qubit only
1213    }
1214
1215    fn to_compilable(&self) -> Option<&dyn Compilable> {
1216        Some(self)
1217    }
1218}
1219
1220/// Defines the phase shift operator
1221///
1222/// A single-qubit operator that applies a phase shift of the provided angle to the |1> state. Also known as the phase shift gate.
1223#[derive(Debug, Clone, Copy)]
1224pub struct PhaseShift {
1225    pub(crate) angle: f64,
1226}
1227
1228impl PhaseShift {
1229    /// Creates a new PhaseShift operator with the given angle.
1230    ///
1231    /// # Arguments:
1232    ///
1233    /// * `angle` - The angle of the phase shift in radians.
1234    pub fn new(angle: f64) -> Self {
1235        PhaseShift { angle }
1236    }
1237}
1238
1239impl Operator for PhaseShift {
1240    /// Applies the phase shift operator to the given state's target qubit.
1241    ///
1242    /// # Arguments:
1243    ///
1244    /// * `state` - The state to apply the operator to.
1245    ///
1246    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
1247    ///
1248    /// * `control_qubits` - The control qubits for the operator. If not empty, the operator will be applied conditionally based on the control qubits. Otherwise, it will be applied unconditionally.
1249    ///
1250    /// # Returns:
1251    ///
1252    /// * The new state after applying the phase shift operator.
1253    ///
1254    /// # Errors:
1255    ///
1256    /// * `Error::InvalidNumberOfQubits` - If the target qubits is not 1.
1257    ///
1258    /// * `Error::InvalidQubitIndex` - If the target qubit index or control qubit index is invalid for the number of qubits in the state.
1259    ///
1260    /// * `Error::OverlappingControlAndTargetQubits` - If the control qubit and target qubit indices overlap.
1261    fn apply(
1262        &self,
1263        state: &State,
1264        target_qubits: &[usize],
1265        control_qubits: &[usize],
1266    ) -> Result<State, Error> {
1267        // Validation
1268        validate_qubits(state, target_qubits, control_qubits, 1)?;
1269
1270        let target_qubit = target_qubits[0];
1271        let num_qubits = state.num_qubits();
1272
1273        // Apply potentially controlled Phase Shift operator
1274        #[allow(unused_assignments)]
1275        let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
1276        let gpu_enabled: bool = cfg!(feature = "gpu");
1277
1278        if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
1279            #[cfg(feature = "gpu")]
1280            {
1281                let global_work_size = 1 << num_qubits;
1282                new_state_vec = execute_on_gpu(
1283                    state,
1284                    target_qubit,
1285                    control_qubits,
1286                    KernelType::PhaseShift,
1287                    global_work_size,
1288                    GpuKernelArgs::PhaseShift {
1289                        cos_angle: self.angle.cos() as f32,
1290                        sin_angle: self.angle.sin() as f32,
1291                    },
1292                )?;
1293            }
1294        } else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
1295            let phase_factor = Complex::new(self.angle.cos(), self.angle.sin());
1296            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1297            new_state_vec
1298                .par_iter_mut()
1299                .enumerate()
1300                .for_each(|(i, current_amp_ref)| {
1301                    if ((i >> target_qubit) & 1 == 1) && check_controls(i, control_qubits) {
1302                        *current_amp_ref = state.state_vector[i] * phase_factor;
1303                    }
1304                });
1305        } else {
1306            let phase_factor = Complex::new(self.angle.cos(), self.angle.sin());
1307            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1308            let dim: usize = 1 << num_qubits;
1309            for i in 0..dim {
1310                let target_bit_is_one = (i >> target_qubit) & 1 == 1;
1311                if target_bit_is_one && check_controls(i, control_qubits) {
1312                    new_state_vec[i] = state.state_vector[i] * phase_factor;
1313                }
1314            }
1315        }
1316
1317        Ok(State {
1318            state_vector: new_state_vec,
1319            num_qubits,
1320        })
1321    }
1322
1323    fn base_qubits(&self) -> usize {
1324        1 // Phase shift acts on 1 qubit only
1325    }
1326
1327    fn to_compilable(&self) -> Option<&dyn Compilable> {
1328        Some(self)
1329    }
1330}
1331
1332/// Defines the rotate-X operator
1333///
1334/// A single-qubit operator that applies a rotation around the X axis of the Bloch sphere by the given angle. Also known as the RX gate.
1335#[derive(Debug, Clone, Copy)]
1336pub struct RotateX {
1337    pub(crate) angle: f64,
1338}
1339
1340impl RotateX {
1341    /// Creates a new RotateX operator with the given angle.
1342    ///
1343    /// # Arguments:
1344    ///
1345    /// * `angle` - The angle of rotation in radians.
1346    pub fn new(angle: f64) -> Self {
1347        RotateX { angle }
1348    }
1349}
1350
1351impl Operator for RotateX {
1352    /// Applies the RotateX operator to the given state's target qubit.
1353    ///
1354    /// # Arguments:
1355    ///
1356    /// * `state` - The state to apply the operator to.
1357    ///
1358    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
1359    ///
1360    /// * `control_qubits` - The control qubits for the operator. If not empty, the operator will be applied conditionally based on the control qubits. Otherwise, it will be applied unconditionally.
1361    ///
1362    /// # Returns:
1363    ///
1364    /// * The new state after applying the RotateX operator.
1365    fn apply(
1366        &self,
1367        state: &State,
1368        target_qubits: &[usize],
1369        control_qubits: &[usize],
1370    ) -> Result<State, Error> {
1371        // Validation
1372        validate_qubits(state, target_qubits, control_qubits, 1)?;
1373
1374        let target_qubit = target_qubits[0];
1375        let num_qubits = state.num_qubits();
1376
1377        // Apply potentially controlled RotateX operator
1378        #[allow(unused_assignments)]
1379        let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
1380        let gpu_enabled: bool = cfg!(feature = "gpu");
1381
1382        if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
1383            #[cfg(feature = "gpu")]
1384            {
1385                let half_angle = self.angle / 2.0;
1386                let global_work_size = if num_qubits > 0 { 1 << (num_qubits - 1) } else { 1 };
1387                new_state_vec = execute_on_gpu(
1388                    state,
1389                    target_qubit,
1390                    control_qubits,
1391                    KernelType::RotateX,
1392                    global_work_size,
1393                    GpuKernelArgs::RotationGate {
1394                        cos_half_angle: half_angle.cos() as f32,
1395                        sin_half_angle: half_angle.sin() as f32,
1396                    },
1397                )?;
1398            }
1399        } else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
1400            // Parallel implementation
1401            let half_angle: f64 = self.angle / 2.0;
1402            let cos_half: f64 = half_angle.cos();
1403            let sin_half: f64 = half_angle.sin();
1404            let i_complex: Complex<f64> = Complex::new(0.0, 1.0);
1405            let dim: usize = 1 << num_qubits;
1406            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1407
1408            let updates: Vec<(usize, Complex<f64>)> = (0..dim)
1409                .into_par_iter()
1410                .filter_map(|i| {
1411                    if ((i >> target_qubit) & 1 == 0) && check_controls(i, control_qubits) {
1412                        let j = i | (1 << target_qubit);
1413                        let amp_i = state.state_vector[i];
1414                        let amp_j = state.state_vector[j];
1415                        Some(vec![
1416                            (i, cos_half * amp_i - i_complex * sin_half * amp_j),
1417                            (j, -i_complex * sin_half * amp_i + cos_half * amp_j),
1418                        ])
1419                    } else {
1420                        None
1421                    }
1422                })
1423                .flatten()
1424                .collect();
1425            for (idx, val) in updates {
1426                new_state_vec[idx] = val;
1427            }
1428        } else {
1429            // Sequential implementation
1430            let half_angle: f64 = self.angle / 2.0;
1431            let cos_half: f64 = half_angle.cos();
1432            let sin_half: f64 = half_angle.sin();
1433            let i_complex: Complex<f64> = Complex::new(0.0, 1.0);
1434            let dim: usize = 1 << num_qubits;
1435            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1436
1437            for i in 0..dim {
1438                if (i >> target_qubit) & 1 == 0 {
1439                    let j = i | (1 << target_qubit);
1440                    if check_controls(i, control_qubits) {
1441                        let amp_i = state.state_vector[i];
1442                        let amp_j = state.state_vector[j];
1443                        new_state_vec[i] = cos_half * amp_i - i_complex * sin_half * amp_j;
1444                        new_state_vec[j] = -i_complex * sin_half * amp_i + cos_half * amp_j;
1445                    }
1446                }
1447            }
1448        }
1449
1450        Ok(State {
1451            state_vector: new_state_vec,
1452            num_qubits: state.num_qubits(),
1453        })
1454    }
1455
1456    fn base_qubits(&self) -> usize {
1457        1 // RotateX acts on 1 qubit only
1458    }
1459
1460    fn to_compilable(&self) -> Option<&dyn Compilable> {
1461        Some(self)
1462    }
1463}
1464
1465/// Defines the rotate-Y operator
1466///
1467/// A single-qubit operator that applies a rotation around the Y axis of the Bloch sphere by the given angle. Also known as the RY gate.
1468#[derive(Debug, Clone, Copy)]
1469pub struct RotateY {
1470    pub(crate) angle: f64,
1471}
1472
1473impl RotateY {
1474    /// Creates a new RotateY operator with the given angle.
1475    ///
1476    /// # Arguments:
1477    ///
1478    /// * `angle` - The angle of rotation in radians.
1479    pub fn new(angle: f64) -> Self {
1480        RotateY { angle }
1481    }
1482}
1483
1484impl Operator for RotateY {
1485    /// Applies the RotateY operator to the given state's target qubit.
1486    ///
1487    /// # Arguments:
1488    ///
1489    /// * `state` - The state to apply the operator to.
1490    ///
1491    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
1492    ///
1493    /// * `control_qubits` - The control qubits for the operator. If not empty, the operator will be applied conditionally based on the control qubits. Otherwise, it will be applied unconditionally.
1494    ///
1495    /// # Returns:
1496    ///
1497    /// * The new state after applying the RotateY operator.
1498    fn apply(
1499        &self,
1500        state: &State,
1501        target_qubits: &[usize],
1502        control_qubits: &[usize],
1503    ) -> Result<State, Error> {
1504        // Validation
1505        validate_qubits(state, target_qubits, control_qubits, 1)?;
1506
1507        let target_qubit = target_qubits[0];
1508        let num_qubits = state.num_qubits();
1509
1510        // Apply potentially controlled RotateY operator
1511        #[allow(unused_assignments)]
1512        let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
1513        let gpu_enabled: bool = cfg!(feature = "gpu");
1514
1515        if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
1516            #[cfg(feature = "gpu")]
1517            {
1518                let half_angle = self.angle / 2.0;
1519                let global_work_size = if num_qubits > 0 { 1 << (num_qubits - 1) } else { 1 };
1520                new_state_vec = execute_on_gpu(
1521                    state,
1522                    target_qubit,
1523                    control_qubits,
1524                    KernelType::RotateY,
1525                    global_work_size,
1526                    GpuKernelArgs::RotationGate {
1527                        cos_half_angle: half_angle.cos() as f32,
1528                        sin_half_angle: half_angle.sin() as f32,
1529                    },
1530                )?;
1531            }
1532        } else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
1533            // Parallel implementation
1534            let half_angle: f64 = self.angle / 2.0;
1535            let cos_half: f64 = half_angle.cos();
1536            let sin_half: f64 = half_angle.sin();
1537            let dim: usize = 1 << num_qubits;
1538            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1539
1540            let updates: Vec<(usize, Complex<f64>)> = (0..dim)
1541                .into_par_iter()
1542                .filter_map(|i| {
1543                    if ((i >> target_qubit) & 1 == 0) && check_controls(i, control_qubits) {
1544                        let j = i | (1 << target_qubit);
1545                        let amp_i = state.state_vector[i];
1546                        let amp_j = state.state_vector[j];
1547                        Some(vec![
1548                            (i, cos_half * amp_i - sin_half * amp_j),
1549                            (j, sin_half * amp_i + cos_half * amp_j),
1550                        ])
1551                    } else {
1552                        None
1553                    }
1554                })
1555                .flatten()
1556                .collect();
1557            for (idx, val) in updates {
1558                new_state_vec[idx] = val;
1559            }
1560        } else {
1561            // Sequential implementation
1562            let half_angle: f64 = self.angle / 2.0;
1563            let cos_half: f64 = half_angle.cos();
1564            let sin_half: f64 = half_angle.sin();
1565            let dim: usize = 1 << num_qubits;
1566            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1567
1568            for i in 0..dim {
1569                if (i >> target_qubit) & 1 == 0 {
1570                    let j = i | (1 << target_qubit);
1571                    if check_controls(i, control_qubits) {
1572                        let amp_i = state.state_vector[i];
1573                        let amp_j = state.state_vector[j];
1574                        new_state_vec[i] = cos_half * amp_i - sin_half * amp_j;
1575                        new_state_vec[j] = sin_half * amp_i + cos_half * amp_j;
1576                    }
1577                }
1578            }
1579        }
1580
1581        Ok(State {
1582            state_vector: new_state_vec,
1583            num_qubits: state.num_qubits(),
1584        })
1585    }
1586
1587    fn base_qubits(&self) -> usize {
1588        1 // RotateY acts on 1 qubit only
1589    }
1590
1591    fn to_compilable(&self) -> Option<&dyn Compilable> {
1592        Some(self)
1593    }
1594}
1595
1596/// Defines the rotate-Z operator
1597///
1598/// A single-qubit operator that applies a rotation around the Z axis of the Bloch sphere by the given angle. Also known as the RZ gate.
1599#[derive(Debug, Clone, Copy)]
1600pub struct RotateZ {
1601    pub(crate) angle: f64,
1602}
1603
1604impl RotateZ {
1605    /// Creates a new RotateZ operator with the given angle.
1606    ///
1607    /// # Arguments:
1608    ///
1609    /// * `angle` - The angle of rotation in radians.
1610    pub fn new(angle: f64) -> Self {
1611        RotateZ { angle }
1612    }
1613}
1614
1615impl Operator for RotateZ {
1616    /// Applies the RotateZ operator to the given state's target qubit.
1617    ///
1618    /// # Arguments:
1619    ///
1620    /// * `state` - The state to apply the operator to.
1621    ///
1622    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
1623    ///
1624    /// * `control_qubits` - The control qubits for the operator. If not empty, the operator will be applied conditionally based on the control qubits. Otherwise, it will be applied unconditionally.
1625    ///
1626    /// # Returns:
1627    ///
1628    /// * The new state after applying the RotateZ operator.
1629    fn apply(
1630        &self,
1631        state: &State,
1632        target_qubits: &[usize],
1633        control_qubits: &[usize],
1634    ) -> Result<State, Error> {
1635        // Validation
1636        validate_qubits(state, target_qubits, control_qubits, 1)?;
1637
1638        let target_qubit = target_qubits[0];
1639        let num_qubits = state.num_qubits();
1640
1641        // Apply potentially controlled RotateZ operator
1642        #[allow(unused_assignments)]
1643        let mut new_state_vec: Vec<Complex<f64>> = state.state_vector.clone();
1644        let gpu_enabled: bool = cfg!(feature = "gpu");
1645
1646        if num_qubits >= OPENCL_THRESHOLD_NUM_QUBITS && gpu_enabled {
1647            #[cfg(feature = "gpu")]
1648            {
1649                let half_angle = self.angle / 2.0;
1650                let global_work_size = 1 << num_qubits; // N work items for RZ
1651                new_state_vec = execute_on_gpu(
1652                    state,
1653                    target_qubit,
1654                    control_qubits,
1655                    KernelType::RotateZ,
1656                    global_work_size,
1657                    GpuKernelArgs::RotationGate {
1658                        cos_half_angle: half_angle.cos() as f32,
1659                        sin_half_angle: half_angle.sin() as f32,
1660                    },
1661                )?;
1662            }
1663        } else if num_qubits >= PARALLEL_THRESHOLD_NUM_QUBITS {
1664            // Parallel implementation
1665            let half_angle = self.angle / 2.0;
1666            let phase_0 = Complex::new(half_angle.cos(), -half_angle.sin());
1667            let phase_1 = Complex::new(half_angle.cos(), half_angle.sin());
1668            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1669
1670            new_state_vec
1671                .par_iter_mut()
1672                .enumerate()
1673                .for_each(|(i, current_amp_ref)| {
1674                    if check_controls(i, control_qubits) {
1675                        let target_bit_is_one = (i >> target_qubit) & 1 == 1;
1676                        if target_bit_is_one {
1677                            *current_amp_ref = state.state_vector[i] * phase_1;
1678                        } else {
1679                            *current_amp_ref = state.state_vector[i] * phase_0;
1680                        }
1681                    }
1682                });
1683        } else {
1684            // Sequential implementation
1685            let half_angle = self.angle / 2.0;
1686            let phase_0 = Complex::new(half_angle.cos(), -half_angle.sin());
1687            let phase_1 = Complex::new(half_angle.cos(), half_angle.sin());
1688            let dim: usize = 1 << num_qubits;
1689            new_state_vec = state.state_vector.clone(); // Ensure cloned for CPU path
1690
1691            for i in 0..dim {
1692                if check_controls(i, control_qubits) {
1693                    let target_bit_is_one = (i >> target_qubit) & 1 == 1;
1694                    if target_bit_is_one {
1695                        new_state_vec[i] = state.state_vector[i] * phase_1;
1696                    } else {
1697                        new_state_vec[i] = state.state_vector[i] * phase_0;
1698                    }
1699                }
1700            }
1701        }
1702
1703        Ok(State {
1704            state_vector: new_state_vec,
1705            num_qubits: state.num_qubits(),
1706        })
1707    }
1708
1709    fn base_qubits(&self) -> usize {
1710        1 // RotateZ acts on 1 qubit only
1711    }
1712
1713    fn to_compilable(&self) -> Option<&dyn Compilable> {
1714        Some(self)
1715    }
1716}
1717
1718/// An arbitrary 2×2 unitary operator.
1719///
1720/// This operator can be applied to a single qubit in a quantum state. It is represented by a 2×2 unitary matrix.
1721#[derive(Debug, Clone, Copy)]
1722pub struct Unitary2 {
1723    /// The 2×2 unitary matrix representing the operator.
1724    pub(crate) matrix: [[Complex<f64>; 2]; 2],
1725}
1726
1727impl Unitary2 {
1728    /// Creates a new Unitary2 operator with the given 2×2 unitary matrix.
1729    ///
1730    /// # Arguments:
1731    ///
1732    /// * `matrix` - A 2×2 unitary matrix represented as a 2D array of complex numbers.
1733    ///
1734    /// # Returns:
1735    ///
1736    /// * `Result<Self, Error>` - A result containing the new Unitary2 operator or an error if the matrix is not unitary.
1737    ///
1738    /// # Errors:
1739    ///
1740    /// * `Error::NonUnitaryMatrix` - If the provided matrix is not unitary.
1741    pub fn new(matrix: [[Complex<f64>; 2]; 2]) -> Result<Self, Error> {
1742        // Faster 2×2 unitary check: U U_dagger = I (rows are orthonormal)
1743        let tol: f64 = f64::EPSILON * 2.0; // Tolerance for floating point comparisons
1744        let a: Complex<f64> = matrix[0][0]; // U_00
1745        let b: Complex<f64> = matrix[0][1]; // U_01
1746        let c: Complex<f64> = matrix[1][0]; // U_10
1747        let d: Complex<f64> = matrix[1][1]; // U_11
1748
1749        // Check if each row has norm 1
1750        // Row 0: |a|^2 + |b|^2 == 1
1751        if ((a.norm_sqr() + b.norm_sqr()) - 1.0).abs() > tol {
1752            return Err(Error::NonUnitaryMatrix);
1753        }
1754        // Row 1: |c|^2 + |d|^2 == 1
1755        if ((c.norm_sqr() + d.norm_sqr()) - 1.0).abs() > tol {
1756            return Err(Error::NonUnitaryMatrix);
1757        }
1758
1759        // Check if rows are orthogonal
1760        // Row 0 dot Row 1_conj: a*c_conj + b*d_conj == 0
1761        if (a * c.conj() + b * d.conj()).norm_sqr() > tol * tol {
1762            // Compare norm_sqr with tol^2
1763            return Err(Error::NonUnitaryMatrix);
1764        }
1765
1766        Ok(Unitary2 { matrix })
1767    }
1768}
1769
1770impl Operator for Unitary2 {
1771    /// Applies the Unitary2 operator to the given state's target qubit.
1772    ///
1773    /// # Arguments:
1774    ///
1775    /// * `state` - The state to apply the operator to.
1776    ///
1777    /// * `target_qubits` - The target qubits to apply the operator to. This should be a single qubit.
1778    ///
1779    /// * `control_qubits` - The control qubits for the operator. If not empty, the operator will be applied conditionally based on the control qubits. Otherwise, it will be applied unconditionally.
1780    ///
1781    /// # Returns:
1782    ///
1783    /// * The new state after applying the Unitary2 operator.
1784    fn apply(
1785        &self,
1786        state: &State,
1787        target_qubits: &[usize],
1788        control_qubits: &[usize],
1789    ) -> Result<State, Error> {
1790        // Validation
1791        validate_qubits(state, target_qubits, control_qubits, 1)?;
1792
1793        let t: usize = target_qubits[0];
1794        let nq: usize = state.num_qubits();
1795
1796        // Apply 2×2 block on each basis‐pair
1797        let dim = 1 << nq;
1798        let mut new_state_vec = state.state_vector.clone();
1799
1800        if nq >= PARALLEL_THRESHOLD_NUM_QUBITS {
1801            // Parallel implementation
1802            let updates: Vec<(usize, Complex<f64>)> = (0..dim)
1803                .into_par_iter()
1804                .filter_map(|i| {
1805                    if ((i >> t) & 1 == 0) && check_controls(i, control_qubits) {
1806                        let j = i | (1 << t);
1807                        let ai = state.state_vector[i];
1808                        let aj = state.state_vector[j];
1809                        Some(vec![
1810                            (i, self.matrix[0][0] * ai + self.matrix[0][1] * aj),
1811                            (j, self.matrix[1][0] * ai + self.matrix[1][1] * aj),
1812                        ])
1813                    } else {
1814                        None
1815                    }
1816                })
1817                .flatten()
1818                .collect();
1819            for (idx, val) in updates {
1820                new_state_vec[idx] = val;
1821            }
1822        } else {
1823            // Sequential implementation
1824            for i in 0..dim {
1825                if (i >> t) & 1 == 0 {
1826                    let j = i | (1 << t);
1827                    if check_controls(i, control_qubits) {
1828                        let ai = state.state_vector[i];
1829                        let aj = state.state_vector[j];
1830                        new_state_vec[i] = self.matrix[0][0] * ai + self.matrix[0][1] * aj;
1831                        new_state_vec[j] = self.matrix[1][0] * ai + self.matrix[1][1] * aj;
1832                    }
1833                }
1834            }
1835        }
1836
1837        Ok(State {
1838            state_vector: new_state_vec,
1839            num_qubits: nq,
1840        })
1841    }
1842
1843    fn base_qubits(&self) -> usize {
1844        1
1845    }
1846
1847    fn to_compilable(&self) -> Option<&dyn Compilable> {
1848        Some(self)
1849    }
1850}