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}