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}