Skip to main content

quantrs2_sim/
gpu_kernel_optimization.rs

1//! GPU Kernel Optimization for Specialized Quantum Operations
2//!
3//! This module provides highly optimized GPU kernels for quantum simulation,
4//! including specialized implementations for common gates, fused operations,
5//! and memory-optimized algorithms for large state vectors.
6//!
7//! # Features
8//! - Specialized kernels for common gates (H, X, Y, Z, CNOT, CZ, etc.)
9//! - Fused gate sequences for reduced memory bandwidth
10//! - Memory-coalesced access patterns for GPU efficiency
11//! - Warp-level optimizations for NVIDIA GPUs
12//! - Shared memory utilization for reduced global memory access
13//! - Streaming execution for overlapped computation and data transfer
14
15use quantrs2_core::error::{QuantRS2Error, QuantRS2Result};
16use scirs2_core::ndarray::{Array1, Array2};
17use scirs2_core::Complex64;
18use serde::{Deserialize, Serialize};
19use std::collections::HashMap;
20use std::sync::{Arc, Mutex, RwLock};
21use std::time::{Duration, Instant};
22
23/// GPU kernel optimization framework for quantum simulation
24#[derive(Debug)]
25pub struct GPUKernelOptimizer {
26    /// Kernel registry for specialized operations
27    kernel_registry: KernelRegistry,
28    /// Kernel execution statistics
29    stats: Arc<Mutex<KernelStats>>,
30    /// Configuration
31    config: GPUKernelConfig,
32    /// Kernel cache for compiled kernels
33    kernel_cache: Arc<RwLock<HashMap<String, CompiledKernel>>>,
34    /// Memory layout optimizer
35    memory_optimizer: MemoryLayoutOptimizer,
36}
37
38/// Configuration for GPU kernel optimization
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct GPUKernelConfig {
41    /// Enable warp-level optimizations
42    pub enable_warp_optimization: bool,
43    /// Enable shared memory usage
44    pub enable_shared_memory: bool,
45    /// Block size for GPU execution
46    pub block_size: usize,
47    /// Grid size calculation method
48    pub grid_size_method: GridSizeMethod,
49    /// Enable kernel fusion
50    pub enable_kernel_fusion: bool,
51    /// Maximum fused kernel length
52    pub max_fusion_length: usize,
53    /// Enable memory coalescing optimization
54    pub enable_memory_coalescing: bool,
55    /// Enable streaming execution
56    pub enable_streaming: bool,
57    /// Number of streams for concurrent execution
58    pub num_streams: usize,
59    /// Occupancy optimization target
60    pub target_occupancy: f64,
61}
62
63impl Default for GPUKernelConfig {
64    fn default() -> Self {
65        Self {
66            enable_warp_optimization: true,
67            enable_shared_memory: true,
68            block_size: 256,
69            grid_size_method: GridSizeMethod::Automatic,
70            enable_kernel_fusion: true,
71            max_fusion_length: 8,
72            enable_memory_coalescing: true,
73            enable_streaming: true,
74            num_streams: 4,
75            target_occupancy: 0.75,
76        }
77    }
78}
79
80/// Method for calculating grid size
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
82pub enum GridSizeMethod {
83    /// Automatic calculation based on problem size
84    Automatic,
85    /// Fixed grid size
86    Fixed(usize),
87    /// Occupancy-based calculation
88    OccupancyBased,
89}
90
91/// Registry of specialized GPU kernels
92#[derive(Debug)]
93pub struct KernelRegistry {
94    /// Single-qubit gate kernels
95    single_qubit_kernels: HashMap<String, SingleQubitKernel>,
96    /// Two-qubit gate kernels
97    two_qubit_kernels: HashMap<String, TwoQubitKernel>,
98    /// Fused kernel templates
99    fused_kernels: HashMap<String, FusedKernel>,
100    /// Custom kernel implementations
101    custom_kernels: HashMap<String, CustomKernel>,
102}
103
104impl Default for KernelRegistry {
105    fn default() -> Self {
106        let mut registry = Self {
107            single_qubit_kernels: HashMap::new(),
108            two_qubit_kernels: HashMap::new(),
109            fused_kernels: HashMap::new(),
110            custom_kernels: HashMap::new(),
111        };
112        registry.register_builtin_kernels();
113        registry
114    }
115}
116
117impl KernelRegistry {
118    /// Register all built-in optimized kernels
119    fn register_builtin_kernels(&mut self) {
120        // Single-qubit gate kernels
121        self.single_qubit_kernels.insert(
122            "hadamard".to_string(),
123            SingleQubitKernel {
124                name: "hadamard".to_string(),
125                kernel_type: SingleQubitKernelType::Hadamard,
126                optimization_level: OptimizationLevel::Maximum,
127                uses_shared_memory: true,
128                register_usage: 32,
129            },
130        );
131
132        self.single_qubit_kernels.insert(
133            "pauli_x".to_string(),
134            SingleQubitKernel {
135                name: "pauli_x".to_string(),
136                kernel_type: SingleQubitKernelType::PauliX,
137                optimization_level: OptimizationLevel::Maximum,
138                uses_shared_memory: false, // Simple swap operation
139                register_usage: 16,
140            },
141        );
142
143        self.single_qubit_kernels.insert(
144            "pauli_y".to_string(),
145            SingleQubitKernel {
146                name: "pauli_y".to_string(),
147                kernel_type: SingleQubitKernelType::PauliY,
148                optimization_level: OptimizationLevel::Maximum,
149                uses_shared_memory: false,
150                register_usage: 24,
151            },
152        );
153
154        self.single_qubit_kernels.insert(
155            "pauli_z".to_string(),
156            SingleQubitKernel {
157                name: "pauli_z".to_string(),
158                kernel_type: SingleQubitKernelType::PauliZ,
159                optimization_level: OptimizationLevel::Maximum,
160                uses_shared_memory: false,
161                register_usage: 16,
162            },
163        );
164
165        self.single_qubit_kernels.insert(
166            "phase".to_string(),
167            SingleQubitKernel {
168                name: "phase".to_string(),
169                kernel_type: SingleQubitKernelType::Phase,
170                optimization_level: OptimizationLevel::High,
171                uses_shared_memory: false,
172                register_usage: 24,
173            },
174        );
175
176        self.single_qubit_kernels.insert(
177            "t_gate".to_string(),
178            SingleQubitKernel {
179                name: "t_gate".to_string(),
180                kernel_type: SingleQubitKernelType::TGate,
181                optimization_level: OptimizationLevel::High,
182                uses_shared_memory: false,
183                register_usage: 24,
184            },
185        );
186
187        self.single_qubit_kernels.insert(
188            "rotation_x".to_string(),
189            SingleQubitKernel {
190                name: "rotation_x".to_string(),
191                kernel_type: SingleQubitKernelType::RotationX,
192                optimization_level: OptimizationLevel::Medium,
193                uses_shared_memory: true,
194                register_usage: 40,
195            },
196        );
197
198        self.single_qubit_kernels.insert(
199            "rotation_y".to_string(),
200            SingleQubitKernel {
201                name: "rotation_y".to_string(),
202                kernel_type: SingleQubitKernelType::RotationY,
203                optimization_level: OptimizationLevel::Medium,
204                uses_shared_memory: true,
205                register_usage: 40,
206            },
207        );
208
209        self.single_qubit_kernels.insert(
210            "rotation_z".to_string(),
211            SingleQubitKernel {
212                name: "rotation_z".to_string(),
213                kernel_type: SingleQubitKernelType::RotationZ,
214                optimization_level: OptimizationLevel::Medium,
215                uses_shared_memory: true,
216                register_usage: 32,
217            },
218        );
219
220        // Two-qubit gate kernels
221        self.two_qubit_kernels.insert(
222            "cnot".to_string(),
223            TwoQubitKernel {
224                name: "cnot".to_string(),
225                kernel_type: TwoQubitKernelType::CNOT,
226                optimization_level: OptimizationLevel::Maximum,
227                uses_shared_memory: true,
228                register_usage: 48,
229                memory_access_pattern: MemoryAccessPattern::Strided,
230            },
231        );
232
233        self.two_qubit_kernels.insert(
234            "cz".to_string(),
235            TwoQubitKernel {
236                name: "cz".to_string(),
237                kernel_type: TwoQubitKernelType::CZ,
238                optimization_level: OptimizationLevel::Maximum,
239                uses_shared_memory: false,
240                register_usage: 32,
241                memory_access_pattern: MemoryAccessPattern::Sparse,
242            },
243        );
244
245        self.two_qubit_kernels.insert(
246            "swap".to_string(),
247            TwoQubitKernel {
248                name: "swap".to_string(),
249                kernel_type: TwoQubitKernelType::SWAP,
250                optimization_level: OptimizationLevel::High,
251                uses_shared_memory: true,
252                register_usage: 40,
253                memory_access_pattern: MemoryAccessPattern::Strided,
254            },
255        );
256
257        self.two_qubit_kernels.insert(
258            "iswap".to_string(),
259            TwoQubitKernel {
260                name: "iswap".to_string(),
261                kernel_type: TwoQubitKernelType::ISWAP,
262                optimization_level: OptimizationLevel::High,
263                uses_shared_memory: true,
264                register_usage: 48,
265                memory_access_pattern: MemoryAccessPattern::Strided,
266            },
267        );
268
269        self.two_qubit_kernels.insert(
270            "controlled_rotation".to_string(),
271            TwoQubitKernel {
272                name: "controlled_rotation".to_string(),
273                kernel_type: TwoQubitKernelType::ControlledRotation,
274                optimization_level: OptimizationLevel::Medium,
275                uses_shared_memory: true,
276                register_usage: 56,
277                memory_access_pattern: MemoryAccessPattern::Strided,
278            },
279        );
280
281        // Fused kernel templates
282        self.fused_kernels.insert(
283            "h_cnot_h".to_string(),
284            FusedKernel {
285                name: "h_cnot_h".to_string(),
286                sequence: vec![
287                    "hadamard".to_string(),
288                    "cnot".to_string(),
289                    "hadamard".to_string(),
290                ],
291                optimization_gain: 2.5,
292                register_usage: 64,
293            },
294        );
295
296        self.fused_kernels.insert(
297            "rotation_chain".to_string(),
298            FusedKernel {
299                name: "rotation_chain".to_string(),
300                sequence: vec![
301                    "rotation_x".to_string(),
302                    "rotation_y".to_string(),
303                    "rotation_z".to_string(),
304                ],
305                optimization_gain: 2.0,
306                register_usage: 56,
307            },
308        );
309
310        self.fused_kernels.insert(
311            "bell_state".to_string(),
312            FusedKernel {
313                name: "bell_state".to_string(),
314                sequence: vec!["hadamard".to_string(), "cnot".to_string()],
315                optimization_gain: 1.8,
316                register_usage: 48,
317            },
318        );
319    }
320}
321
322/// Single-qubit kernel implementation
323#[derive(Debug, Clone)]
324pub struct SingleQubitKernel {
325    /// Kernel name
326    pub name: String,
327    /// Kernel type
328    pub kernel_type: SingleQubitKernelType,
329    /// Optimization level
330    pub optimization_level: OptimizationLevel,
331    /// Uses shared memory
332    pub uses_shared_memory: bool,
333    /// Register usage
334    pub register_usage: usize,
335}
336
337/// Types of single-qubit kernels
338#[derive(Debug, Clone, Copy, PartialEq, Eq)]
339pub enum SingleQubitKernelType {
340    Hadamard,
341    PauliX,
342    PauliY,
343    PauliZ,
344    Phase,
345    TGate,
346    RotationX,
347    RotationY,
348    RotationZ,
349    Generic,
350}
351
352/// Two-qubit kernel implementation
353#[derive(Debug, Clone)]
354pub struct TwoQubitKernel {
355    /// Kernel name
356    pub name: String,
357    /// Kernel type
358    pub kernel_type: TwoQubitKernelType,
359    /// Optimization level
360    pub optimization_level: OptimizationLevel,
361    /// Uses shared memory
362    pub uses_shared_memory: bool,
363    /// Register usage
364    pub register_usage: usize,
365    /// Memory access pattern
366    pub memory_access_pattern: MemoryAccessPattern,
367}
368
369/// Types of two-qubit kernels
370#[derive(Debug, Clone, Copy, PartialEq, Eq)]
371pub enum TwoQubitKernelType {
372    CNOT,
373    CZ,
374    SWAP,
375    ISWAP,
376    ControlledRotation,
377    Generic,
378}
379
380/// Memory access patterns for kernels
381#[derive(Debug, Clone, Copy, PartialEq, Eq)]
382pub enum MemoryAccessPattern {
383    /// Coalesced access
384    Coalesced,
385    /// Strided access
386    Strided,
387    /// Sparse access
388    Sparse,
389    /// Random access
390    Random,
391}
392
393/// Fused kernel for multiple operations
394#[derive(Debug, Clone)]
395pub struct FusedKernel {
396    /// Kernel name
397    pub name: String,
398    /// Sequence of operations
399    pub sequence: Vec<String>,
400    /// Expected optimization gain
401    pub optimization_gain: f64,
402    /// Register usage
403    pub register_usage: usize,
404}
405
406/// Custom kernel implementation
407#[derive(Debug, Clone)]
408pub struct CustomKernel {
409    /// Kernel name
410    pub name: String,
411    /// Kernel code (CUDA/OpenCL)
412    pub code: String,
413    /// Register usage
414    pub register_usage: usize,
415}
416
417/// Compiled kernel ready for execution
418#[derive(Debug, Clone)]
419pub struct CompiledKernel {
420    /// Kernel name
421    pub name: String,
422    /// Compiled code (binary or PTX)
423    pub compiled_code: Vec<u8>,
424    /// Execution parameters
425    pub exec_params: KernelExecParams,
426}
427
428/// Kernel execution parameters
429#[derive(Debug, Clone)]
430pub struct KernelExecParams {
431    /// Block dimensions
432    pub block_dim: (usize, usize, usize),
433    /// Grid dimensions
434    pub grid_dim: (usize, usize, usize),
435    /// Shared memory size
436    pub shared_memory_size: usize,
437    /// Maximum threads per block
438    pub max_threads_per_block: usize,
439}
440
441/// Optimization levels for kernels
442#[derive(Debug, Clone, Copy, PartialEq, Eq)]
443pub enum OptimizationLevel {
444    /// Basic optimization
445    Basic,
446    /// Medium optimization
447    Medium,
448    /// High optimization
449    High,
450    /// Maximum optimization
451    Maximum,
452}
453
454/// Kernel execution statistics
455#[derive(Debug, Clone, Default)]
456pub struct KernelStats {
457    /// Total kernel executions
458    pub total_executions: u64,
459    /// Total execution time
460    pub total_execution_time: Duration,
461    /// Kernel execution counts by name
462    pub execution_counts: HashMap<String, u64>,
463    /// Kernel execution times by name
464    pub execution_times: HashMap<String, Duration>,
465    /// Cache hits
466    pub cache_hits: u64,
467    /// Cache misses
468    pub cache_misses: u64,
469    /// Fused operations count
470    pub fused_operations: u64,
471    /// Memory bandwidth utilized (GB/s)
472    pub memory_bandwidth: f64,
473    /// Compute throughput (GFLOPS)
474    pub compute_throughput: f64,
475}
476
477/// Memory layout optimizer for GPU operations
478#[derive(Debug)]
479pub struct MemoryLayoutOptimizer {
480    /// Layout strategy
481    strategy: MemoryLayoutStrategy,
482    /// Prefetch distance
483    prefetch_distance: usize,
484}
485
486/// Memory layout strategies
487#[derive(Debug, Clone, Copy)]
488pub enum MemoryLayoutStrategy {
489    /// Interleaved complex numbers (Re, Im, Re, Im, ...)
490    Interleaved,
491    /// Split arrays (all Re, then all Im)
492    SplitArrays,
493    /// Structure of arrays
494    StructureOfArrays,
495    /// Array of structures
496    ArrayOfStructures,
497}
498
499impl Default for MemoryLayoutOptimizer {
500    fn default() -> Self {
501        Self {
502            strategy: MemoryLayoutStrategy::Interleaved,
503            prefetch_distance: 4,
504        }
505    }
506}
507
508impl GPUKernelOptimizer {
509    /// Create a new GPU kernel optimizer
510    #[must_use]
511    pub fn new(config: GPUKernelConfig) -> Self {
512        Self {
513            kernel_registry: KernelRegistry::default(),
514            stats: Arc::new(Mutex::new(KernelStats::default())),
515            config,
516            kernel_cache: Arc::new(RwLock::new(HashMap::new())),
517            memory_optimizer: MemoryLayoutOptimizer::default(),
518        }
519    }
520
521    /// Apply optimized single-qubit gate
522    pub fn apply_single_qubit_gate(
523        &mut self,
524        state: &mut Array1<Complex64>,
525        qubit: usize,
526        gate_name: &str,
527        parameters: Option<&[f64]>,
528    ) -> QuantRS2Result<()> {
529        let start = Instant::now();
530
531        // Get kernel from registry
532        let kernel = self.kernel_registry.single_qubit_kernels.get(gate_name);
533
534        let n = state.len();
535        let stride = 1 << qubit;
536
537        match kernel {
538            Some(k) => {
539                // Apply optimized kernel
540                match k.kernel_type {
541                    SingleQubitKernelType::Hadamard => {
542                        self.apply_hadamard_optimized(state, stride)?;
543                    }
544                    SingleQubitKernelType::PauliX => {
545                        self.apply_pauli_x_optimized(state, stride)?;
546                    }
547                    SingleQubitKernelType::PauliY => {
548                        self.apply_pauli_y_optimized(state, stride)?;
549                    }
550                    SingleQubitKernelType::PauliZ => {
551                        self.apply_pauli_z_optimized(state, stride)?;
552                    }
553                    SingleQubitKernelType::Phase => {
554                        self.apply_phase_optimized(state, stride)?;
555                    }
556                    SingleQubitKernelType::TGate => {
557                        self.apply_t_gate_optimized(state, stride)?;
558                    }
559                    SingleQubitKernelType::RotationX => {
560                        let angle = parameters.and_then(|p| p.first()).copied().unwrap_or(0.0);
561                        self.apply_rotation_x_optimized(state, stride, angle)?;
562                    }
563                    SingleQubitKernelType::RotationY => {
564                        let angle = parameters.and_then(|p| p.first()).copied().unwrap_or(0.0);
565                        self.apply_rotation_y_optimized(state, stride, angle)?;
566                    }
567                    SingleQubitKernelType::RotationZ => {
568                        let angle = parameters.and_then(|p| p.first()).copied().unwrap_or(0.0);
569                        self.apply_rotation_z_optimized(state, stride, angle)?;
570                    }
571                    SingleQubitKernelType::Generic => {
572                        // Fallback to generic implementation
573                        self.apply_generic_single_qubit(state, qubit, gate_name, parameters)?;
574                    }
575                }
576            }
577            None => {
578                // Use generic implementation
579                self.apply_generic_single_qubit(state, qubit, gate_name, parameters)?;
580            }
581        }
582
583        // Update statistics
584        let mut stats = self
585            .stats
586            .lock()
587            .map_err(|_| QuantRS2Error::InvalidInput("Failed to acquire stats lock".to_string()))?;
588        stats.total_executions += 1;
589        stats.total_execution_time += start.elapsed();
590        *stats
591            .execution_counts
592            .entry(gate_name.to_string())
593            .or_insert(0) += 1;
594        *stats
595            .execution_times
596            .entry(gate_name.to_string())
597            .or_insert(Duration::ZERO) += start.elapsed();
598
599        Ok(())
600    }
601
602    /// Apply optimized Hadamard gate
603    fn apply_hadamard_optimized(
604        &self,
605        state: &mut Array1<Complex64>,
606        stride: usize,
607    ) -> QuantRS2Result<()> {
608        let n = state.len();
609        let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
610
611        let amplitudes = state.as_slice_mut().ok_or_else(|| {
612            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
613        })?;
614
615        // Process pairs with memory coalescing
616        for i in 0..n / 2 {
617            let i0 = (i / stride) * (2 * stride) + (i % stride);
618            let i1 = i0 + stride;
619
620            let a0 = amplitudes[i0];
621            let a1 = amplitudes[i1];
622
623            amplitudes[i0] =
624                Complex64::new((a0.re + a1.re) * inv_sqrt2, (a0.im + a1.im) * inv_sqrt2);
625            amplitudes[i1] =
626                Complex64::new((a0.re - a1.re) * inv_sqrt2, (a0.im - a1.im) * inv_sqrt2);
627        }
628
629        Ok(())
630    }
631
632    /// Apply optimized Pauli-X gate
633    fn apply_pauli_x_optimized(
634        &self,
635        state: &mut Array1<Complex64>,
636        stride: usize,
637    ) -> QuantRS2Result<()> {
638        let n = state.len();
639
640        let amplitudes = state.as_slice_mut().ok_or_else(|| {
641            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
642        })?;
643
644        // Simple swap operation - highly optimized
645        for i in 0..n / 2 {
646            let i0 = (i / stride) * (2 * stride) + (i % stride);
647            let i1 = i0 + stride;
648
649            amplitudes.swap(i0, i1);
650        }
651
652        Ok(())
653    }
654
655    /// Apply optimized Pauli-Y gate
656    fn apply_pauli_y_optimized(
657        &self,
658        state: &mut Array1<Complex64>,
659        stride: usize,
660    ) -> QuantRS2Result<()> {
661        let n = state.len();
662
663        let amplitudes = state.as_slice_mut().ok_or_else(|| {
664            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
665        })?;
666
667        for i in 0..n / 2 {
668            let i0 = (i / stride) * (2 * stride) + (i % stride);
669            let i1 = i0 + stride;
670
671            let a0 = amplitudes[i0];
672            let a1 = amplitudes[i1];
673
674            // Y gate: [[0, -i], [i, 0]]
675            amplitudes[i0] = Complex64::new(a1.im, -a1.re);
676            amplitudes[i1] = Complex64::new(-a0.im, a0.re);
677        }
678
679        Ok(())
680    }
681
682    /// Apply optimized Pauli-Z gate
683    fn apply_pauli_z_optimized(
684        &self,
685        state: &mut Array1<Complex64>,
686        stride: usize,
687    ) -> QuantRS2Result<()> {
688        let n = state.len();
689
690        let amplitudes = state.as_slice_mut().ok_or_else(|| {
691            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
692        })?;
693
694        // Z gate only affects |1> states
695        for i in 0..n / 2 {
696            let i1 = (i / stride) * (2 * stride) + (i % stride) + stride;
697            amplitudes[i1] = -amplitudes[i1];
698        }
699
700        Ok(())
701    }
702
703    /// Apply optimized Phase gate
704    fn apply_phase_optimized(
705        &self,
706        state: &mut Array1<Complex64>,
707        stride: usize,
708    ) -> QuantRS2Result<()> {
709        let n = state.len();
710
711        let amplitudes = state.as_slice_mut().ok_or_else(|| {
712            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
713        })?;
714
715        // S gate: phase shift of pi/2 on |1>
716        for i in 0..n / 2 {
717            let i1 = (i / stride) * (2 * stride) + (i % stride) + stride;
718            let a = amplitudes[i1];
719            amplitudes[i1] = Complex64::new(-a.im, a.re); // multiply by i
720        }
721
722        Ok(())
723    }
724
725    /// Apply optimized T gate
726    fn apply_t_gate_optimized(
727        &self,
728        state: &mut Array1<Complex64>,
729        stride: usize,
730    ) -> QuantRS2Result<()> {
731        let n = state.len();
732        let t_phase = Complex64::new(
733            std::f64::consts::FRAC_1_SQRT_2,
734            std::f64::consts::FRAC_1_SQRT_2,
735        );
736
737        let amplitudes = state.as_slice_mut().ok_or_else(|| {
738            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
739        })?;
740
741        // T gate: phase shift of pi/4 on |1>
742        for i in 0..n / 2 {
743            let i1 = (i / stride) * (2 * stride) + (i % stride) + stride;
744            amplitudes[i1] *= t_phase;
745        }
746
747        Ok(())
748    }
749
750    /// Apply optimized rotation around X axis
751    fn apply_rotation_x_optimized(
752        &self,
753        state: &mut Array1<Complex64>,
754        stride: usize,
755        angle: f64,
756    ) -> QuantRS2Result<()> {
757        let n = state.len();
758        let cos_half = (angle / 2.0).cos();
759        let sin_half = (angle / 2.0).sin();
760
761        let amplitudes = state.as_slice_mut().ok_or_else(|| {
762            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
763        })?;
764
765        for i in 0..n / 2 {
766            let i0 = (i / stride) * (2 * stride) + (i % stride);
767            let i1 = i0 + stride;
768
769            let a0 = amplitudes[i0];
770            let a1 = amplitudes[i1];
771
772            // RX(θ) = [[cos(θ/2), -i*sin(θ/2)], [-i*sin(θ/2), cos(θ/2)]]
773            amplitudes[i0] = Complex64::new(
774                cos_half * a0.re + sin_half * a1.im,
775                cos_half * a0.im - sin_half * a1.re,
776            );
777            amplitudes[i1] = Complex64::new(
778                sin_half * a0.im + cos_half * a1.re,
779                (-sin_half).mul_add(a0.re, cos_half * a1.im),
780            );
781        }
782
783        Ok(())
784    }
785
786    /// Apply optimized rotation around Y axis
787    fn apply_rotation_y_optimized(
788        &self,
789        state: &mut Array1<Complex64>,
790        stride: usize,
791        angle: f64,
792    ) -> QuantRS2Result<()> {
793        let n = state.len();
794        let cos_half = (angle / 2.0).cos();
795        let sin_half = (angle / 2.0).sin();
796
797        let amplitudes = state.as_slice_mut().ok_or_else(|| {
798            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
799        })?;
800
801        for i in 0..n / 2 {
802            let i0 = (i / stride) * (2 * stride) + (i % stride);
803            let i1 = i0 + stride;
804
805            let a0 = amplitudes[i0];
806            let a1 = amplitudes[i1];
807
808            // RY(θ) = [[cos(θ/2), -sin(θ/2)], [sin(θ/2), cos(θ/2)]]
809            amplitudes[i0] = Complex64::new(
810                cos_half * a0.re - sin_half * a1.re,
811                cos_half * a0.im - sin_half * a1.im,
812            );
813            amplitudes[i1] = Complex64::new(
814                sin_half * a0.re + cos_half * a1.re,
815                sin_half * a0.im + cos_half * a1.im,
816            );
817        }
818
819        Ok(())
820    }
821
822    /// Apply optimized rotation around Z axis
823    fn apply_rotation_z_optimized(
824        &self,
825        state: &mut Array1<Complex64>,
826        stride: usize,
827        angle: f64,
828    ) -> QuantRS2Result<()> {
829        let n = state.len();
830        let exp_neg = Complex64::new((angle / 2.0).cos(), -(angle / 2.0).sin());
831        let exp_pos = Complex64::new((angle / 2.0).cos(), (angle / 2.0).sin());
832
833        let amplitudes = state.as_slice_mut().ok_or_else(|| {
834            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
835        })?;
836
837        for i in 0..n / 2 {
838            let i0 = (i / stride) * (2 * stride) + (i % stride);
839            let i1 = i0 + stride;
840
841            // RZ(θ) = [[e^(-iθ/2), 0], [0, e^(iθ/2)]]
842            amplitudes[i0] *= exp_neg;
843            amplitudes[i1] *= exp_pos;
844        }
845
846        Ok(())
847    }
848
849    /// Generic single-qubit gate application.
850    ///
851    /// Used for any single-qubit gate that does not have a hand-optimized
852    /// kernel above (S†/T†/identity/√X/phase-family gates). Builds the
853    /// gate's real 2x2 unitary from its name (and, for phase-family gates,
854    /// its angle parameter) and applies it with the same strided
855    /// amplitude-pair update the specialized kernels use -- it never
856    /// silently leaves the state unchanged. A gate name this function
857    /// cannot resolve to a real matrix is an honest error, not a no-op.
858    fn apply_generic_single_qubit(
859        &self,
860        state: &mut Array1<Complex64>,
861        qubit: usize,
862        gate_name: &str,
863        parameters: Option<&[f64]>,
864    ) -> QuantRS2Result<()> {
865        let stride = 1usize << qubit;
866        let matrix = Self::single_qubit_matrix_for_name(gate_name, parameters)?;
867        Self::apply_matrix2_optimized(state, stride, &matrix)
868    }
869
870    /// Real 2x2 unitary for single-qubit gate names not covered by a
871    /// hand-optimized kernel. Returns an honest
872    /// [`QuantRS2Error::UnsupportedOperation`] for any name it cannot
873    /// resolve, rather than fabricating an identity.
874    fn single_qubit_matrix_for_name(
875        gate_name: &str,
876        parameters: Option<&[f64]>,
877    ) -> QuantRS2Result<[[Complex64; 2]; 2]> {
878        let one = Complex64::new(1.0, 0.0);
879        let zero = Complex64::new(0.0, 0.0);
880        match gate_name {
881            "I" | "Identity" | "identity" => Ok([[one, zero], [zero, one]]),
882            "S†" | "Sdg" | "SDagger" | "SDG" | "s_dagger" | "sdg" => {
883                Ok([[one, zero], [zero, Complex64::new(0.0, -1.0)]])
884            }
885            "T†" | "Tdg" | "TDagger" | "TDG" | "t_dagger" | "tdg" => Ok([
886                [one, zero],
887                [
888                    zero,
889                    Complex64::from_polar(1.0, -std::f64::consts::FRAC_PI_4),
890                ],
891            ]),
892            "√X" | "SqrtX" | "SX" | "sqrt_x" | "sx" => {
893                let plus = Complex64::new(0.5, 0.5);
894                let minus = Complex64::new(0.5, -0.5);
895                Ok([[plus, minus], [minus, plus]])
896            }
897            "√X†" | "SqrtXdg" | "SXDG" | "sqrt_x_dagger" | "sxdg" => {
898                let plus = Complex64::new(0.5, -0.5);
899                let minus = Complex64::new(0.5, 0.5);
900                Ok([[plus, minus], [minus, plus]])
901            }
902            "P" | "Phase" | "U1" | "phase_shift" | "u1" => {
903                let theta = parameters.and_then(|p| p.first()).copied().ok_or_else(|| {
904                    QuantRS2Error::InvalidInput(format!(
905                        "gate '{gate_name}' requires a phase-angle parameter"
906                    ))
907                })?;
908                Ok([[one, zero], [zero, Complex64::from_polar(1.0, theta)]])
909            }
910            _ => Err(QuantRS2Error::UnsupportedOperation(format!(
911                "gpu_kernel_optimization: no real matrix implementation for single-qubit gate \
912                 '{gate_name}'; add a specialized or generic-matrix case instead of a silent \
913                 no-op"
914            ))),
915        }
916    }
917
918    /// Apply an arbitrary 2x2 unitary to the strided amplitude pairs of a
919    /// single target qubit. Shared by every hand-optimized single-qubit
920    /// kernel's real counterpart and [`Self::apply_generic_single_qubit`].
921    fn apply_matrix2_optimized(
922        state: &mut Array1<Complex64>,
923        stride: usize,
924        matrix: &[[Complex64; 2]; 2],
925    ) -> QuantRS2Result<()> {
926        let n = state.len();
927        let amplitudes = state.as_slice_mut().ok_or_else(|| {
928            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
929        })?;
930
931        for i in 0..n / 2 {
932            let i0 = (i / stride) * (2 * stride) + (i % stride);
933            let i1 = i0 + stride;
934
935            let a0 = amplitudes[i0];
936            let a1 = amplitudes[i1];
937
938            amplitudes[i0] = matrix[0][0] * a0 + matrix[0][1] * a1;
939            amplitudes[i1] = matrix[1][0] * a0 + matrix[1][1] * a1;
940        }
941
942        Ok(())
943    }
944
945    /// Apply optimized two-qubit gate
946    pub fn apply_two_qubit_gate(
947        &mut self,
948        state: &mut Array1<Complex64>,
949        control: usize,
950        target: usize,
951        gate_name: &str,
952    ) -> QuantRS2Result<()> {
953        let start = Instant::now();
954
955        // Get kernel from registry
956        let kernel = self.kernel_registry.two_qubit_kernels.get(gate_name);
957
958        match kernel {
959            Some(k) => match k.kernel_type {
960                TwoQubitKernelType::CNOT => {
961                    self.apply_cnot_optimized(state, control, target)?;
962                }
963                TwoQubitKernelType::CZ => {
964                    self.apply_cz_optimized(state, control, target)?;
965                }
966                TwoQubitKernelType::SWAP => {
967                    self.apply_swap_optimized(state, control, target)?;
968                }
969                TwoQubitKernelType::ISWAP => {
970                    self.apply_iswap_optimized(state, control, target)?;
971                }
972                _ => {
973                    self.apply_generic_two_qubit(state, control, target, gate_name)?;
974                }
975            },
976            None => {
977                self.apply_generic_two_qubit(state, control, target, gate_name)?;
978            }
979        }
980
981        // Update statistics
982        let mut stats = self
983            .stats
984            .lock()
985            .map_err(|_| QuantRS2Error::InvalidInput("Failed to acquire stats lock".to_string()))?;
986        stats.total_executions += 1;
987        stats.total_execution_time += start.elapsed();
988        *stats
989            .execution_counts
990            .entry(gate_name.to_string())
991            .or_insert(0) += 1;
992
993        Ok(())
994    }
995
996    /// Apply optimized CNOT gate
997    fn apply_cnot_optimized(
998        &self,
999        state: &mut Array1<Complex64>,
1000        control: usize,
1001        target: usize,
1002    ) -> QuantRS2Result<()> {
1003        let n = state.len();
1004        let control_stride = 1 << control;
1005        let target_stride = 1 << target;
1006
1007        let amplitudes = state.as_slice_mut().ok_or_else(|| {
1008            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
1009        })?;
1010
1011        // CNOT: flip target when control is |1>
1012        for i in 0..n {
1013            if (i & control_stride) != 0 {
1014                // Control is |1>
1015                let partner = i ^ target_stride;
1016                if partner > i {
1017                    amplitudes.swap(i, partner);
1018                }
1019            }
1020        }
1021
1022        Ok(())
1023    }
1024
1025    /// Apply optimized CZ gate
1026    fn apply_cz_optimized(
1027        &self,
1028        state: &mut Array1<Complex64>,
1029        control: usize,
1030        target: usize,
1031    ) -> QuantRS2Result<()> {
1032        let n = state.len();
1033        let control_stride = 1 << control;
1034        let target_stride = 1 << target;
1035
1036        let amplitudes = state.as_slice_mut().ok_or_else(|| {
1037            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
1038        })?;
1039
1040        // CZ: apply phase flip when both control and target are |1>
1041        for (i, amplitude) in amplitudes.iter_mut().enumerate() {
1042            if (i & control_stride) != 0 && (i & target_stride) != 0 {
1043                *amplitude = -*amplitude;
1044            }
1045        }
1046
1047        Ok(())
1048    }
1049
1050    /// Apply optimized SWAP gate
1051    fn apply_swap_optimized(
1052        &self,
1053        state: &mut Array1<Complex64>,
1054        qubit1: usize,
1055        qubit2: usize,
1056    ) -> QuantRS2Result<()> {
1057        let n = state.len();
1058        let stride1 = 1 << qubit1;
1059        let stride2 = 1 << qubit2;
1060
1061        let amplitudes = state.as_slice_mut().ok_or_else(|| {
1062            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
1063        })?;
1064
1065        // SWAP: exchange |01> and |10> components
1066        for i in 0..n {
1067            let bit1 = (i & stride1) != 0;
1068            let bit2 = (i & stride2) != 0;
1069            if bit1 != bit2 {
1070                let partner = i ^ stride1 ^ stride2;
1071                if partner > i {
1072                    amplitudes.swap(i, partner);
1073                }
1074            }
1075        }
1076
1077        Ok(())
1078    }
1079
1080    /// Apply optimized iSWAP gate
1081    fn apply_iswap_optimized(
1082        &self,
1083        state: &mut Array1<Complex64>,
1084        qubit1: usize,
1085        qubit2: usize,
1086    ) -> QuantRS2Result<()> {
1087        let n = state.len();
1088        let stride1 = 1 << qubit1;
1089        let stride2 = 1 << qubit2;
1090
1091        let amplitudes = state.as_slice_mut().ok_or_else(|| {
1092            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
1093        })?;
1094
1095        // iSWAP: swap |01> and |10> with i phase
1096        for i in 0..n {
1097            let bit1 = (i & stride1) != 0;
1098            let bit2 = (i & stride2) != 0;
1099            if bit1 != bit2 {
1100                let partner = i ^ stride1 ^ stride2;
1101                if partner > i {
1102                    let a = amplitudes[i];
1103                    let b = amplitudes[partner];
1104                    // Multiply by i when swapping
1105                    amplitudes[i] = Complex64::new(-b.im, b.re);
1106                    amplitudes[partner] = Complex64::new(-a.im, a.re);
1107                }
1108            }
1109        }
1110
1111        Ok(())
1112    }
1113
1114    /// Generic two-qubit gate application.
1115    ///
1116    /// Used for any two-qubit gate that does not have a hand-optimized
1117    /// kernel above (CNOT/CZ/SWAP/ISWAP). Builds the gate's real 4x4
1118    /// unitary from its name and applies it with a genuine matrix-vector
1119    /// contraction over each `(control, target)` amplitude quartet -- it
1120    /// never silently leaves the state unchanged. A gate name this
1121    /// function cannot resolve to a real matrix is an honest error, not a
1122    /// no-op.
1123    fn apply_generic_two_qubit(
1124        &self,
1125        state: &mut Array1<Complex64>,
1126        control: usize,
1127        target: usize,
1128        gate_name: &str,
1129    ) -> QuantRS2Result<()> {
1130        let matrix = Self::two_qubit_matrix_for_name(gate_name)?;
1131        Self::apply_matrix4_optimized(state, control, target, &matrix)
1132    }
1133
1134    /// Real 4x4 unitary (basis order `|control, target>`, `control` the
1135    /// high local bit -- matching [`Self::apply_cnot_optimized`]'s
1136    /// bit-indexing convention) for two-qubit gate names not covered by a
1137    /// hand-optimized kernel. Returns an honest
1138    /// [`QuantRS2Error::UnsupportedOperation`] for any name it cannot
1139    /// resolve, rather than fabricating an identity.
1140    fn two_qubit_matrix_for_name(gate_name: &str) -> QuantRS2Result<[[Complex64; 4]; 4]> {
1141        let one = Complex64::new(1.0, 0.0);
1142        let zero = Complex64::new(0.0, 0.0);
1143        match gate_name {
1144            "CY" | "cy" => Ok([
1145                [one, zero, zero, zero],
1146                [zero, one, zero, zero],
1147                [zero, zero, zero, Complex64::new(0.0, -1.0)],
1148                [zero, zero, Complex64::new(0.0, 1.0), zero],
1149            ]),
1150            "CH" | "ch" => {
1151                let inv_sqrt2 = Complex64::new(std::f64::consts::FRAC_1_SQRT_2, 0.0);
1152                Ok([
1153                    [one, zero, zero, zero],
1154                    [zero, one, zero, zero],
1155                    [zero, zero, inv_sqrt2, inv_sqrt2],
1156                    [zero, zero, inv_sqrt2, -inv_sqrt2],
1157                ])
1158            }
1159            "CS" | "cs" => Ok([
1160                [one, zero, zero, zero],
1161                [zero, one, zero, zero],
1162                [zero, zero, one, zero],
1163                [zero, zero, zero, Complex64::new(0.0, 1.0)],
1164            ]),
1165            _ => Err(QuantRS2Error::UnsupportedOperation(format!(
1166                "gpu_kernel_optimization: no real matrix implementation for two-qubit gate \
1167                 '{gate_name}'; add a specialized or generic-matrix case instead of a silent \
1168                 no-op"
1169            ))),
1170        }
1171    }
1172
1173    /// Apply an arbitrary 4x4 unitary over the `(control, target)`
1174    /// amplitude quartets of a full state vector. Basis ordering matches
1175    /// [`Self::apply_cnot_optimized`]: `control` is the high local bit.
1176    fn apply_matrix4_optimized(
1177        state: &mut Array1<Complex64>,
1178        control: usize,
1179        target: usize,
1180        matrix: &[[Complex64; 4]; 4],
1181    ) -> QuantRS2Result<()> {
1182        let n = state.len();
1183        let control_mask = 1usize << control;
1184        let target_mask = 1usize << target;
1185
1186        let amplitudes = state.as_slice_mut().ok_or_else(|| {
1187            QuantRS2Error::InvalidInput("Failed to get mutable slice".to_string())
1188        })?;
1189
1190        for i in 0..n {
1191            if i & control_mask == 0 && i & target_mask == 0 {
1192                let idx = [
1193                    i,
1194                    i | target_mask,
1195                    i | control_mask,
1196                    i | control_mask | target_mask,
1197                ];
1198                let amps = [
1199                    amplitudes[idx[0]],
1200                    amplitudes[idx[1]],
1201                    amplitudes[idx[2]],
1202                    amplitudes[idx[3]],
1203                ];
1204                for (row, &out_idx) in idx.iter().enumerate() {
1205                    let mut acc = Complex64::new(0.0, 0.0);
1206                    for (col, &amp) in amps.iter().enumerate() {
1207                        acc += matrix[row][col] * amp;
1208                    }
1209                    amplitudes[out_idx] = acc;
1210                }
1211            }
1212        }
1213
1214        Ok(())
1215    }
1216
1217    /// Get kernel execution statistics
1218    pub fn get_stats(&self) -> QuantRS2Result<KernelStats> {
1219        let stats = self
1220            .stats
1221            .lock()
1222            .map_err(|_| QuantRS2Error::InvalidInput("Failed to acquire stats lock".to_string()))?;
1223        Ok(stats.clone())
1224    }
1225
1226    /// Reset statistics
1227    pub fn reset_stats(&mut self) -> QuantRS2Result<()> {
1228        let mut stats = self
1229            .stats
1230            .lock()
1231            .map_err(|_| QuantRS2Error::InvalidInput("Failed to acquire stats lock".to_string()))?;
1232        *stats = KernelStats::default();
1233        Ok(())
1234    }
1235
1236    /// Get available kernel names
1237    #[must_use]
1238    pub fn get_available_kernels(&self) -> Vec<String> {
1239        let mut kernels = Vec::new();
1240        kernels.extend(self.kernel_registry.single_qubit_kernels.keys().cloned());
1241        kernels.extend(self.kernel_registry.two_qubit_kernels.keys().cloned());
1242        kernels.extend(self.kernel_registry.fused_kernels.keys().cloned());
1243        kernels
1244    }
1245
1246    /// Check if a kernel is available
1247    #[must_use]
1248    pub fn has_kernel(&self, name: &str) -> bool {
1249        self.kernel_registry.single_qubit_kernels.contains_key(name)
1250            || self.kernel_registry.two_qubit_kernels.contains_key(name)
1251            || self.kernel_registry.fused_kernels.contains_key(name)
1252    }
1253}
1254
1255#[cfg(test)]
1256mod tests {
1257    use super::*;
1258
1259    #[test]
1260    fn test_kernel_optimizer_creation() {
1261        let config = GPUKernelConfig::default();
1262        let optimizer = GPUKernelOptimizer::new(config);
1263        assert!(!optimizer.get_available_kernels().is_empty());
1264    }
1265
1266    #[test]
1267    fn test_hadamard_kernel() {
1268        let config = GPUKernelConfig::default();
1269        let mut optimizer = GPUKernelOptimizer::new(config);
1270
1271        let mut state = Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]);
1272
1273        let result = optimizer.apply_single_qubit_gate(&mut state, 0, "hadamard", None);
1274        assert!(result.is_ok());
1275
1276        let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
1277        assert!((state[0].re - inv_sqrt2).abs() < 1e-10);
1278        assert!((state[1].re - inv_sqrt2).abs() < 1e-10);
1279    }
1280
1281    #[test]
1282    fn test_pauli_x_kernel() {
1283        let config = GPUKernelConfig::default();
1284        let mut optimizer = GPUKernelOptimizer::new(config);
1285
1286        let mut state = Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]);
1287
1288        let result = optimizer.apply_single_qubit_gate(&mut state, 0, "pauli_x", None);
1289        assert!(result.is_ok());
1290
1291        assert!((state[0].re - 0.0).abs() < 1e-10);
1292        assert!((state[1].re - 1.0).abs() < 1e-10);
1293    }
1294
1295    #[test]
1296    fn test_pauli_z_kernel() {
1297        let config = GPUKernelConfig::default();
1298        let mut optimizer = GPUKernelOptimizer::new(config);
1299
1300        let mut state = Array1::from_vec(vec![Complex64::new(0.5, 0.0), Complex64::new(0.5, 0.0)]);
1301
1302        let result = optimizer.apply_single_qubit_gate(&mut state, 0, "pauli_z", None);
1303        assert!(result.is_ok());
1304
1305        assert!((state[0].re - 0.5).abs() < 1e-10);
1306        assert!((state[1].re + 0.5).abs() < 1e-10);
1307    }
1308
1309    #[test]
1310    fn test_rotation_z_kernel() {
1311        let config = GPUKernelConfig::default();
1312        let mut optimizer = GPUKernelOptimizer::new(config);
1313
1314        let mut state = Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]);
1315
1316        let result = optimizer.apply_single_qubit_gate(
1317            &mut state,
1318            0,
1319            "rotation_z",
1320            Some(&[std::f64::consts::PI]),
1321        );
1322        assert!(result.is_ok());
1323    }
1324
1325    #[test]
1326    fn test_cnot_kernel() {
1327        let config = GPUKernelConfig::default();
1328        let mut optimizer = GPUKernelOptimizer::new(config);
1329
1330        // |10> state
1331        let mut state = Array1::from_vec(vec![
1332            Complex64::new(0.0, 0.0),
1333            Complex64::new(0.0, 0.0),
1334            Complex64::new(1.0, 0.0),
1335            Complex64::new(0.0, 0.0),
1336        ]);
1337
1338        let result = optimizer.apply_two_qubit_gate(&mut state, 1, 0, "cnot");
1339        assert!(result.is_ok());
1340
1341        // Should become |11>
1342        assert!((state[3].re - 1.0).abs() < 1e-10);
1343    }
1344
1345    #[test]
1346    fn test_cz_kernel() {
1347        let config = GPUKernelConfig::default();
1348        let mut optimizer = GPUKernelOptimizer::new(config);
1349
1350        // |11> state
1351        let mut state = Array1::from_vec(vec![
1352            Complex64::new(0.0, 0.0),
1353            Complex64::new(0.0, 0.0),
1354            Complex64::new(0.0, 0.0),
1355            Complex64::new(1.0, 0.0),
1356        ]);
1357
1358        let result = optimizer.apply_two_qubit_gate(&mut state, 1, 0, "cz");
1359        assert!(result.is_ok());
1360
1361        // Should get phase flip
1362        assert!((state[3].re + 1.0).abs() < 1e-10);
1363    }
1364
1365    #[test]
1366    fn test_swap_kernel() {
1367        let config = GPUKernelConfig::default();
1368        let mut optimizer = GPUKernelOptimizer::new(config);
1369
1370        // |01> state
1371        let mut state = Array1::from_vec(vec![
1372            Complex64::new(0.0, 0.0),
1373            Complex64::new(1.0, 0.0),
1374            Complex64::new(0.0, 0.0),
1375            Complex64::new(0.0, 0.0),
1376        ]);
1377
1378        let result = optimizer.apply_two_qubit_gate(&mut state, 0, 1, "swap");
1379        assert!(result.is_ok());
1380
1381        // Should become |10>
1382        assert!((state[2].re - 1.0).abs() < 1e-10);
1383    }
1384
1385    #[test]
1386    fn test_kernel_stats() {
1387        let config = GPUKernelConfig::default();
1388        let mut optimizer = GPUKernelOptimizer::new(config);
1389
1390        let mut state = Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]);
1391
1392        optimizer
1393            .apply_single_qubit_gate(&mut state, 0, "hadamard", None)
1394            .expect("hadamard gate should apply successfully");
1395        optimizer
1396            .apply_single_qubit_gate(&mut state, 0, "pauli_x", None)
1397            .expect("pauli_x gate should apply successfully");
1398
1399        let stats = optimizer.get_stats().expect("get_stats should succeed");
1400        assert_eq!(stats.total_executions, 2);
1401        assert_eq!(*stats.execution_counts.get("hadamard").unwrap_or(&0), 1);
1402        assert_eq!(*stats.execution_counts.get("pauli_x").unwrap_or(&0), 1);
1403    }
1404
1405    #[test]
1406    fn test_available_kernels() {
1407        let config = GPUKernelConfig::default();
1408        let optimizer = GPUKernelOptimizer::new(config);
1409
1410        let kernels = optimizer.get_available_kernels();
1411        assert!(kernels.contains(&"hadamard".to_string()));
1412        assert!(kernels.contains(&"cnot".to_string()));
1413        assert!(kernels.contains(&"swap".to_string()));
1414    }
1415
1416    #[test]
1417    fn test_has_kernel() {
1418        let config = GPUKernelConfig::default();
1419        let optimizer = GPUKernelOptimizer::new(config);
1420
1421        assert!(optimizer.has_kernel("hadamard"));
1422        assert!(optimizer.has_kernel("cnot"));
1423        assert!(!optimizer.has_kernel("nonexistent"));
1424    }
1425
1426    #[test]
1427    fn test_config_defaults() {
1428        let config = GPUKernelConfig::default();
1429
1430        assert!(config.enable_warp_optimization);
1431        assert!(config.enable_shared_memory);
1432        assert_eq!(config.block_size, 256);
1433        assert!(config.enable_kernel_fusion);
1434        assert_eq!(config.max_fusion_length, 8);
1435    }
1436
1437    #[test]
1438    fn test_reset_stats() {
1439        let config = GPUKernelConfig::default();
1440        let mut optimizer = GPUKernelOptimizer::new(config);
1441
1442        let mut state = Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]);
1443
1444        optimizer
1445            .apply_single_qubit_gate(&mut state, 0, "hadamard", None)
1446            .expect("hadamard gate should apply successfully");
1447        optimizer.reset_stats().expect("reset_stats should succeed");
1448
1449        let stats = optimizer.get_stats().expect("get_stats should succeed");
1450        assert_eq!(stats.total_executions, 0);
1451    }
1452
1453    #[test]
1454    fn test_multiple_qubit_operations() {
1455        let config = GPUKernelConfig::default();
1456        let mut optimizer = GPUKernelOptimizer::new(config);
1457
1458        // 3-qubit state
1459        let mut state = Array1::zeros(8);
1460        state[0] = Complex64::new(1.0, 0.0);
1461
1462        // Apply H to qubit 0
1463        optimizer
1464            .apply_single_qubit_gate(&mut state, 0, "hadamard", None)
1465            .expect("hadamard gate should apply successfully");
1466
1467        // Apply CNOT(0, 1)
1468        optimizer
1469            .apply_two_qubit_gate(&mut state, 0, 1, "cnot")
1470            .expect("cnot gate should apply successfully");
1471
1472        // State should be in superposition
1473        let total_prob: f64 = state.iter().map(|a| (a * a.conj()).re).sum();
1474        assert!((total_prob - 1.0).abs() < 1e-10);
1475    }
1476
1477    /// Regression test for the P1 finding: `apply_generic_single_qubit`
1478    /// used to be a structural no-op (it didn't even take `state` mutably)
1479    /// that silently left the state unchanged for any gate name outside
1480    /// the hand-optimized kernel set. An `S†` gate (not one of the
1481    /// registered kernel names) applied to `|1>` must now produce the real
1482    /// `-i` phase, not leave the amplitude untouched.
1483    #[test]
1484    fn test_generic_single_qubit_applies_real_matrix() {
1485        let config = GPUKernelConfig::default();
1486        let mut optimizer = GPUKernelOptimizer::new(config);
1487
1488        let mut state = Array1::from_vec(vec![Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)]);
1489        optimizer
1490            .apply_single_qubit_gate(&mut state, 0, "S†", None)
1491            .expect("S-dagger gate should apply via the generic path");
1492
1493        assert!((state[0].norm()).abs() < 1e-10);
1494        assert!(
1495            (state[1] - Complex64::new(0.0, -1.0)).norm() < 1e-10,
1496            "expected S† to apply a real -i phase to |1>, got {:?}",
1497            state[1]
1498        );
1499    }
1500
1501    /// Regression test: a parametric generic gate (`P`/phase-shift) must
1502    /// use the real supplied angle.
1503    #[test]
1504    fn test_generic_single_qubit_phase_uses_real_angle() {
1505        let config = GPUKernelConfig::default();
1506        let mut optimizer = GPUKernelOptimizer::new(config);
1507
1508        let mut state = Array1::from_vec(vec![Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)]);
1509        optimizer
1510            .apply_single_qubit_gate(&mut state, 0, "P", Some(&[std::f64::consts::FRAC_PI_2]))
1511            .expect("P gate should apply via the generic path");
1512
1513        assert!(
1514            (state[1] - Complex64::new(0.0, 1.0)).norm() < 1e-10,
1515            "expected P(pi/2) to apply a real +i phase to |1>, got {:?}",
1516            state[1]
1517        );
1518    }
1519
1520    /// Regression test: an unresolvable generic gate name must return an
1521    /// honest error rather than silently leaving the state unchanged.
1522    #[test]
1523    fn test_generic_single_qubit_unknown_name_errors() {
1524        let config = GPUKernelConfig::default();
1525        let mut optimizer = GPUKernelOptimizer::new(config);
1526
1527        let mut state = Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]);
1528        let result = optimizer.apply_single_qubit_gate(&mut state, 0, "totally_unknown_gate", None);
1529        assert!(
1530            result.is_err(),
1531            "an unresolvable gate name must be an honest error"
1532        );
1533    }
1534
1535    /// Regression test for the P1 finding: `apply_generic_two_qubit` used
1536    /// to take `state` by shared reference (structurally incapable of
1537    /// mutating it) and always return `Ok(())`. A `CY` gate (not one of
1538    /// the registered kernel names) applied with control set must now
1539    /// really flip and phase the target, not leave the state unchanged.
1540    #[test]
1541    fn test_generic_two_qubit_applies_real_matrix() {
1542        let config = GPUKernelConfig::default();
1543        let mut optimizer = GPUKernelOptimizer::new(config);
1544
1545        // |10>: control (qubit 0) = 1, target (qubit 1) = 0.
1546        let mut state = Array1::zeros(4);
1547        state[1] = Complex64::new(1.0, 0.0);
1548
1549        optimizer
1550            .apply_two_qubit_gate(&mut state, 0, 1, "CY")
1551            .expect("CY gate should apply via the generic path");
1552
1553        // CY with control=1 applies Y to the target: Y|0> = i|1>, moving
1554        // the amplitude to |11> (index 3) with a real +i phase.
1555        assert!(state[1].norm() < 1e-10, "amplitude must leave |10>");
1556        assert!(
1557            (state[3] - Complex64::new(0.0, 1.0)).norm() < 1e-10,
1558            "expected CY(control=1) to apply Y with a real +i phase, got {:?}",
1559            state[3]
1560        );
1561    }
1562
1563    /// Regression test: an unresolvable generic two-qubit gate name must
1564    /// return an honest error rather than silently leaving the state
1565    /// unchanged.
1566    #[test]
1567    fn test_generic_two_qubit_unknown_name_errors() {
1568        let config = GPUKernelConfig::default();
1569        let mut optimizer = GPUKernelOptimizer::new(config);
1570
1571        let mut state = Array1::zeros(4);
1572        state[0] = Complex64::new(1.0, 0.0);
1573        let result = optimizer.apply_two_qubit_gate(&mut state, 0, 1, "totally_unknown_gate");
1574        assert!(
1575            result.is_err(),
1576            "an unresolvable gate name must be an honest error"
1577        );
1578    }
1579}