quant_iron/components/
operator.rs

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