quant_iron/components/
operator.rs

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