quantrs2_sim/cuquantum/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::error::{Result, SimulatorError};
6use quantrs2_circuit::prelude::Circuit;
7use scirs2_core::ndarray::{Array1, Array2};
8use scirs2_core::Complex64;
9use std::collections::HashMap;
10
11use super::types::{
12    CircuitComplexity, ComputePrecision, ContractionPath, CuQuantumConfig, CuQuantumError,
13    CuQuantumResult, CuQuantumSimulator, CuStateVecSimulator, CuTensorNetSimulator, CudaDeviceInfo,
14    GateFusionLevel, GpuResourcePlanner, Observable, PerformanceEstimator, RecommendedBackend,
15    SimulationStats, TensorNetworkState,
16};
17
18impl From<CuQuantumError> for SimulatorError {
19    fn from(err: CuQuantumError) -> Self {
20        SimulatorError::GpuError(err.to_string())
21    }
22}
23#[cfg(test)]
24mod tests {
25    use super::*;
26    #[test]
27    fn test_config_default() {
28        let config = CuQuantumConfig::default();
29        assert_eq!(config.device_id, -1);
30        assert!(!config.multi_gpu);
31        assert_eq!(config.precision, ComputePrecision::Double);
32    }
33    #[test]
34    fn test_config_large_circuit() {
35        let config = CuQuantumConfig::large_circuit();
36        assert!(config.memory_optimization);
37        assert_eq!(config.gate_fusion_level, GateFusionLevel::Aggressive);
38    }
39    #[test]
40    fn test_config_variational() {
41        let config = CuQuantumConfig::variational();
42        assert!(config.async_execution);
43        assert_eq!(config.gate_fusion_level, GateFusionLevel::Moderate);
44    }
45    #[test]
46    fn test_config_multi_gpu() {
47        let config = CuQuantumConfig::multi_gpu(4);
48        assert!(config.multi_gpu);
49        assert_eq!(config.num_gpus, 4);
50    }
51    #[test]
52    fn test_device_info_mock() {
53        let info = CuStateVecSimulator::get_device_info(-1).expect("Should get mock device info");
54        assert!(info.total_memory > 0);
55        assert!(info.compute_capability.0 >= 7);
56    }
57    #[test]
58    fn test_device_max_qubits() {
59        let info = CudaDeviceInfo {
60            device_id: 0,
61            name: "Test A100".to_string(),
62            total_memory: 40 * 1024 * 1024 * 1024,
63            free_memory: 32 * 1024 * 1024 * 1024,
64            compute_capability: (8, 0),
65            sm_count: 108,
66            max_threads_per_block: 1024,
67            warp_size: 32,
68            has_tensor_cores: true,
69        };
70        let max_qubits = info.max_statevec_qubits();
71        assert!(
72            max_qubits >= 30,
73            "Expected >= 30 qubits, got {}",
74            max_qubits
75        );
76        assert!(
77            max_qubits <= 35,
78            "Expected <= 35 qubits, got {}",
79            max_qubits
80        );
81        let info_small = CudaDeviceInfo {
82            device_id: 0,
83            name: "Test RTX 3080".to_string(),
84            total_memory: 12 * 1024 * 1024 * 1024,
85            free_memory: 10 * 1024 * 1024 * 1024,
86            compute_capability: (8, 6),
87            sm_count: 68,
88            max_threads_per_block: 1024,
89            warp_size: 32,
90            has_tensor_cores: true,
91        };
92        let max_qubits_small = info_small.max_statevec_qubits();
93        assert!(
94            max_qubits_small >= 28,
95            "Expected >= 28 qubits for 12GB GPU, got {}",
96            max_qubits_small
97        );
98    }
99    #[test]
100    fn test_custatevec_simulator_creation() {
101        let config = CuQuantumConfig::default();
102        let sim = CuStateVecSimulator::new(config);
103        assert!(sim.is_ok());
104    }
105    #[test]
106    fn test_cutensornet_simulator_creation() {
107        let config = CuQuantumConfig::default();
108        let sim = CuTensorNetSimulator::new(config);
109        assert!(sim.is_ok());
110    }
111    #[test]
112    fn test_simulation_stats() {
113        let mut stats = SimulationStats::default();
114        stats.total_simulations = 10;
115        stats.total_gates = 100;
116        stats.total_time_ms = 500.0;
117        stats.total_flops = 1e9;
118        assert_eq!(stats.avg_gates_per_sim(), 10.0);
119        assert_eq!(stats.avg_time_per_sim(), 50.0);
120        assert!((stats.throughput_gflops() - 2.0).abs() < 0.01);
121    }
122    #[test]
123    fn test_contraction_path() {
124        let mut path = ContractionPath::new();
125        path.add_contraction(0, 1);
126        path.add_contraction(0, 2);
127        assert_eq!(path.contractions.len(), 2);
128        assert!(path.total_cost() > 0.0);
129    }
130    #[test]
131    fn test_unified_simulator_creation() {
132        let config = CuQuantumConfig::default();
133        let sim = CuQuantumSimulator::new(config);
134        assert!(sim.is_ok());
135    }
136    #[test]
137    fn test_observable_creation() {
138        let obs = Observable::PauliZ(vec![0, 1]);
139        match obs {
140            Observable::PauliZ(qubits) => assert_eq!(qubits.len(), 2),
141            _ => panic!("Wrong observable type"),
142        }
143    }
144    #[test]
145    fn test_cuquantum_result_from_state_vector() {
146        use scirs2_core::ndarray::Array1;
147        use scirs2_core::Complex64;
148        let mut state = Array1::zeros(4);
149        state[0] = Complex64::new(1.0, 0.0);
150        let result = CuQuantumResult::from_state_vector(state.clone(), 2);
151        assert_eq!(result.num_qubits, 2);
152        assert!(result.state_vector.is_some());
153        assert!(result.counts.is_empty());
154        let probs = result.probabilities().expect("Should have probabilities");
155        assert_eq!(probs.len(), 4);
156        assert!((probs[0] - 1.0).abs() < 1e-10);
157        assert!(probs[1] < 1e-10);
158        assert!(probs[2] < 1e-10);
159        assert!(probs[3] < 1e-10);
160    }
161    #[test]
162    fn test_cuquantum_result_from_counts() {
163        let mut counts = HashMap::new();
164        counts.insert("00".to_string(), 500);
165        counts.insert("11".to_string(), 500);
166        let result = CuQuantumResult::from_counts(counts.clone(), 2);
167        assert_eq!(result.num_qubits, 2);
168        assert!(result.state_vector.is_none());
169        assert_eq!(result.counts.len(), 2);
170        assert_eq!(*result.counts.get("00").unwrap_or(&0), 500);
171        assert_eq!(*result.counts.get("11").unwrap_or(&0), 500);
172    }
173    #[test]
174    fn test_cuquantum_result_expectation_z() {
175        use scirs2_core::ndarray::Array1;
176        use scirs2_core::Complex64;
177        let mut state_zero = Array1::zeros(2);
178        state_zero[0] = Complex64::new(1.0, 0.0);
179        let result_zero = CuQuantumResult::from_state_vector(state_zero, 1);
180        let exp_z = result_zero
181            .expectation_z(0)
182            .expect("Should compute expectation");
183        assert!(
184            (exp_z - 1.0).abs() < 1e-10,
185            "Expected +1 for |0⟩, got {}",
186            exp_z
187        );
188        let mut state_one = Array1::zeros(2);
189        state_one[1] = Complex64::new(1.0, 0.0);
190        let result_one = CuQuantumResult::from_state_vector(state_one, 1);
191        let exp_z_one = result_one
192            .expectation_z(0)
193            .expect("Should compute expectation");
194        assert!(
195            (exp_z_one - (-1.0)).abs() < 1e-10,
196            "Expected -1 for |1⟩, got {}",
197            exp_z_one
198        );
199        let mut state_plus = Array1::zeros(2);
200        let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
201        state_plus[0] = Complex64::new(inv_sqrt2, 0.0);
202        state_plus[1] = Complex64::new(inv_sqrt2, 0.0);
203        let result_plus = CuQuantumResult::from_state_vector(state_plus, 1);
204        let exp_z_plus = result_plus
205            .expectation_z(0)
206            .expect("Should compute expectation");
207        assert!(
208            exp_z_plus.abs() < 1e-10,
209            "Expected 0 for |+⟩, got {}",
210            exp_z_plus
211        );
212    }
213    #[test]
214    fn test_custatevec_circuit_simulation() {
215        use quantrs2_circuit::prelude::Circuit;
216        let config = CuQuantumConfig::default();
217        let mut sim = CuStateVecSimulator::new(config).expect("Should create simulator");
218        let circuit: Circuit<2> = Circuit::new();
219        let result = sim.simulate(&circuit);
220        // On non-macOS with cuquantum feature, simulation is not yet implemented
221        #[cfg(all(feature = "cuquantum", not(target_os = "macos")))]
222        {
223            assert!(
224                result.is_err(),
225                "Expected error for unimplemented cuStateVec"
226            );
227        }
228        #[cfg(any(target_os = "macos", not(feature = "cuquantum")))]
229        {
230            let result = result.expect("Should simulate circuit");
231            assert_eq!(result.num_qubits, 2);
232            assert!(result.state_vector.is_some());
233            let sv = result
234                .state_vector
235                .as_ref()
236                .expect("Should have state vector");
237            assert_eq!(sv.len(), 4);
238            assert!((sv[0].norm() - 1.0).abs() < 1e-10);
239        }
240    }
241    #[test]
242    fn test_custatevec_statistics() {
243        use quantrs2_circuit::prelude::Circuit;
244        let config = CuQuantumConfig::default();
245        let mut sim = CuStateVecSimulator::new(config).expect("Should create simulator");
246        let stats = sim.stats();
247        assert_eq!(stats.total_simulations, 0);
248        let circuit: Circuit<2> = Circuit::new();
249        let result = sim.simulate(&circuit);
250        let stats_after = sim.stats();
251        // On non-macOS with cuquantum feature, simulation fails so stats won't increment
252        #[cfg(all(feature = "cuquantum", not(target_os = "macos")))]
253        {
254            assert!(result.is_err());
255            assert_eq!(stats_after.total_simulations, 0);
256        }
257        #[cfg(any(target_os = "macos", not(feature = "cuquantum")))]
258        {
259            let _ = result;
260            assert_eq!(stats_after.total_simulations, 1);
261        }
262        sim.reset_stats();
263        let stats_reset = sim.stats();
264        assert_eq!(stats_reset.total_simulations, 0);
265    }
266    #[test]
267    fn test_unified_simulator_auto_selection() {
268        use quantrs2_circuit::prelude::Circuit;
269        let config_small = CuQuantumConfig::default();
270        let mut sim_small = CuQuantumSimulator::new(config_small).expect("Should create simulator");
271        let circuit: Circuit<2> = Circuit::new();
272        let result = sim_small.simulate(&circuit);
273        // On non-macOS with cuquantum feature, simulation is not yet implemented
274        #[cfg(all(feature = "cuquantum", not(target_os = "macos")))]
275        {
276            assert!(
277                result.is_err(),
278                "Expected error for unimplemented cuQuantum"
279            );
280        }
281        #[cfg(any(target_os = "macos", not(feature = "cuquantum")))]
282        {
283            let result = result.expect("Should simulate");
284            assert_eq!(result.num_qubits, 2);
285            let mut config_large = CuQuantumConfig::default();
286            config_large.max_statevec_qubits = 10;
287            let mut sim_large =
288                CuQuantumSimulator::new(config_large).expect("Should create simulator");
289            let result_large = sim_large.simulate(&circuit).expect("Should simulate");
290            assert_eq!(result_large.num_qubits, 2);
291        }
292    }
293    #[test]
294    fn test_cutensornet_network_building() {
295        use quantrs2_circuit::prelude::Circuit;
296        let config = CuQuantumConfig::default();
297        let mut sim = CuTensorNetSimulator::new(config).expect("Should create simulator");
298        let circuit: Circuit<4> = Circuit::new();
299        sim.build_network(&circuit)
300            .expect("Should build tensor network");
301        assert!(sim.tensor_network.is_some());
302    }
303    #[test]
304    fn test_cutensornet_contraction() {
305        use quantrs2_circuit::prelude::Circuit;
306        let config = CuQuantumConfig::default();
307        let mut sim = CuTensorNetSimulator::new(config).expect("Should create simulator");
308        let circuit: Circuit<3> = Circuit::new();
309        sim.build_network(&circuit)
310            .expect("Should build tensor network");
311        let result = sim.contract(&[0, 1, 2]);
312        // On non-macOS with cuquantum feature, contraction is not yet implemented
313        #[cfg(all(feature = "cuquantum", not(target_os = "macos")))]
314        {
315            assert!(
316                result.is_err(),
317                "Expected error for unimplemented cuTensorNet"
318            );
319        }
320        #[cfg(any(target_os = "macos", not(feature = "cuquantum")))]
321        {
322            let amplitudes = result.expect("Should contract network");
323            assert_eq!(amplitudes.len(), 8);
324        }
325    }
326    #[test]
327    fn test_cutensornet_expectation_value() {
328        use quantrs2_circuit::prelude::Circuit;
329        let config = CuQuantumConfig::default();
330        let mut sim = CuTensorNetSimulator::new(config).expect("Should create simulator");
331        let circuit: Circuit<2> = Circuit::new();
332        sim.build_network(&circuit)
333            .expect("Should build tensor network");
334        let observable = Observable::PauliZ(vec![0]);
335        let result = sim.expectation_value(&observable);
336        // On non-macOS with cuquantum feature, expectation value is not yet implemented
337        #[cfg(all(feature = "cuquantum", not(target_os = "macos")))]
338        {
339            assert!(
340                result.is_err(),
341                "Expected error for unimplemented cuTensorNet"
342            );
343        }
344        #[cfg(any(target_os = "macos", not(feature = "cuquantum")))]
345        {
346            let exp_val = result.expect("Should compute expectation");
347            assert!((exp_val - 0.5).abs() < 1e-10);
348        }
349    }
350    #[test]
351    fn test_tensor_network_state_creation() {
352        use quantrs2_circuit::prelude::Circuit;
353        let circuit: Circuit<3> = Circuit::new();
354        let network = TensorNetworkState::from_circuit(&circuit).expect("Should create network");
355        assert!(
356            network.num_tensors() >= 3,
357            "Should have at least 3 tensors (one per qubit)"
358        );
359    }
360    #[test]
361    fn test_contraction_algorithms() {
362        use quantrs2_circuit::prelude::Circuit;
363        let config = CuQuantumConfig::default();
364        let mut sim = CuTensorNetSimulator::new(config).expect("Should create simulator");
365        let circuit: Circuit<4> = Circuit::new();
366        sim.build_network(&circuit)
367            .expect("Should build tensor network");
368        let path = sim
369            .find_contraction_order()
370            .expect("Should find contraction order");
371        assert!(!path.contractions.is_empty() || path.total_cost() >= 0.0);
372    }
373    #[test]
374    fn test_device_info_methods() {
375        let info = CudaDeviceInfo {
376            device_id: 0,
377            name: "Test Device".to_string(),
378            total_memory: 80 * 1024 * 1024 * 1024,
379            free_memory: 70 * 1024 * 1024 * 1024,
380            compute_capability: (8, 0),
381            sm_count: 108,
382            max_threads_per_block: 1024,
383            warp_size: 32,
384            has_tensor_cores: true,
385        };
386        let max_qubits = info.max_statevec_qubits();
387        assert!(
388            max_qubits >= 31,
389            "Expected >= 31 qubits for A100, got {}",
390            max_qubits
391        );
392    }
393    #[test]
394    fn test_is_available() {
395        let available = CuStateVecSimulator::is_available();
396        #[cfg(not(feature = "cuquantum"))]
397        assert!(!available);
398    }
399    #[test]
400    fn test_observable_variants() {
401        let obs_z = Observable::PauliZ(vec![0, 1]);
402        let obs_x = Observable::PauliX(vec![0]);
403        let obs_y = Observable::PauliY(vec![1]);
404        let mut hermitian = Array2::zeros((2, 2));
405        hermitian[[0, 0]] = Complex64::new(1.0, 0.0);
406        hermitian[[1, 1]] = Complex64::new(-1.0, 0.0);
407        let obs_h = Observable::Hermitian(hermitian);
408        let obs_sum = Observable::Sum(vec![
409            Observable::PauliZ(vec![0]),
410            Observable::PauliZ(vec![1]),
411        ]);
412        let obs_prod = Observable::Product(vec![
413            Observable::PauliX(vec![0]),
414            Observable::PauliY(vec![1]),
415        ]);
416        assert!(matches!(obs_z, Observable::PauliZ(_)));
417        assert!(matches!(obs_x, Observable::PauliX(_)));
418        assert!(matches!(obs_y, Observable::PauliY(_)));
419        assert!(matches!(obs_h, Observable::Hermitian(_)));
420        assert!(matches!(obs_sum, Observable::Sum(_)));
421        assert!(matches!(obs_prod, Observable::Product(_)));
422    }
423    #[test]
424    fn test_performance_estimator_creation() {
425        let config = CuQuantumConfig::default();
426        let estimator = PerformanceEstimator::with_default_device(config);
427        assert!(estimator.is_ok());
428    }
429    #[test]
430    fn test_performance_estimate_small_circuit() {
431        use quantrs2_circuit::prelude::Circuit;
432        let config = CuQuantumConfig::default();
433        let estimator =
434            PerformanceEstimator::with_default_device(config).expect("Should create estimator");
435        let circuit: Circuit<5> = Circuit::new();
436        let estimate = estimator.estimate(&circuit);
437        assert!(estimate.fits_in_memory);
438        assert_eq!(
439            estimate.recommended_backend,
440            RecommendedBackend::StateVector
441        );
442        assert!(estimate.estimated_memory_bytes > 0);
443        assert!(estimate.estimated_gpu_utilization >= 0.0);
444        assert!(estimate.estimated_gpu_utilization <= 1.0);
445    }
446    #[test]
447    fn test_performance_estimate_memory_calculation() {
448        let config = CuQuantumConfig::default();
449        let estimator =
450            PerformanceEstimator::with_default_device(config).expect("Should create estimator");
451        let device_info = estimator.device_info();
452        let _ = device_info;
453        use quantrs2_circuit::prelude::Circuit;
454        let circuit_10: Circuit<10> = Circuit::new();
455        let estimate_10 = estimator.estimate(&circuit_10);
456        assert_eq!(estimate_10.estimated_memory_bytes, 1024 * 16);
457        let circuit_20: Circuit<20> = Circuit::new();
458        let estimate_20 = estimator.estimate(&circuit_20);
459        assert_eq!(estimate_20.estimated_memory_bytes, 1024 * 1024 * 16);
460    }
461    #[test]
462    fn test_performance_estimate_flops_calculation() {
463        use quantrs2_circuit::prelude::Circuit;
464        let config = CuQuantumConfig::default();
465        let estimator =
466            PerformanceEstimator::with_default_device(config).expect("Should create estimator");
467        let circuit_empty: Circuit<5> = Circuit::new();
468        let estimate_empty = estimator.estimate(&circuit_empty);
469        assert_eq!(estimate_empty.estimated_flops, 0.0);
470    }
471    #[test]
472    fn test_circuit_complexity_analysis() {
473        use quantrs2_circuit::prelude::Circuit;
474        let circuit: Circuit<4> = Circuit::new();
475        let complexity = CircuitComplexity::analyze(&circuit);
476        assert_eq!(complexity.num_qubits, 4);
477        assert_eq!(complexity.num_gates, 0);
478        assert_eq!(complexity.depth, 0);
479        assert_eq!(complexity.entanglement_degree, 0.0);
480    }
481    #[test]
482    fn test_gpu_resource_planner() {
483        use quantrs2_circuit::prelude::Circuit;
484        let device = CudaDeviceInfo {
485            device_id: 0,
486            name: "Test GPU".to_string(),
487            total_memory: 16 * 1024 * 1024 * 1024,
488            free_memory: 12 * 1024 * 1024 * 1024,
489            compute_capability: (8, 6),
490            sm_count: 68,
491            max_threads_per_block: 1024,
492            warp_size: 32,
493            has_tensor_cores: true,
494        };
495        let config = CuQuantumConfig::default();
496        let planner = GpuResourcePlanner::new(vec![device], config);
497        let circuits: Vec<Circuit<4>> = vec![Circuit::new(), Circuit::new(), Circuit::new()];
498        let assignments = planner.plan_batch(&circuits);
499        assert_eq!(assignments.len(), 3);
500        for (device_id, _) in &assignments {
501            assert_eq!(*device_id, 0);
502        }
503        let batch_memory = planner.estimate_batch_memory(&circuits);
504        assert_eq!(batch_memory, 3 * 16 * 16);
505    }
506    #[test]
507    fn test_recommended_backend_enum() {
508        assert_ne!(
509            RecommendedBackend::StateVector,
510            RecommendedBackend::TensorNetwork
511        );
512        assert_ne!(RecommendedBackend::Hybrid, RecommendedBackend::NotFeasible);
513        let sv = format!("{:?}", RecommendedBackend::StateVector);
514        assert!(sv.contains("StateVector"));
515    }
516    #[test]
517    fn test_performance_suggestions() {
518        use quantrs2_circuit::prelude::Circuit;
519        let mut config = CuQuantumConfig::default();
520        config.gate_fusion_level = GateFusionLevel::None;
521        let estimator =
522            PerformanceEstimator::with_default_device(config).expect("Should create estimator");
523        let circuit: Circuit<26> = Circuit::new();
524        let estimate = estimator.estimate(&circuit);
525        let has_fusion_suggestion = estimate
526            .suggestions
527            .iter()
528            .any(|s| s.contains("gate fusion"));
529        assert!(
530            has_fusion_suggestion,
531            "Should suggest gate fusion for 26 qubit circuit"
532        );
533    }
534    #[test]
535    fn test_multi_gpu_planner() {
536        use quantrs2_circuit::prelude::Circuit;
537        let devices = vec![
538            CudaDeviceInfo {
539                device_id: 0,
540                name: "GPU 0".to_string(),
541                total_memory: 16 * 1024 * 1024 * 1024,
542                free_memory: 12 * 1024 * 1024 * 1024,
543                compute_capability: (8, 6),
544                sm_count: 68,
545                max_threads_per_block: 1024,
546                warp_size: 32,
547                has_tensor_cores: true,
548            },
549            CudaDeviceInfo {
550                device_id: 1,
551                name: "GPU 1".to_string(),
552                total_memory: 16 * 1024 * 1024 * 1024,
553                free_memory: 12 * 1024 * 1024 * 1024,
554                compute_capability: (8, 6),
555                sm_count: 68,
556                max_threads_per_block: 1024,
557                warp_size: 32,
558                has_tensor_cores: true,
559            },
560        ];
561        let config = CuQuantumConfig::default();
562        let planner = GpuResourcePlanner::new(devices, config);
563        let circuits: Vec<Circuit<4>> = vec![
564            Circuit::new(),
565            Circuit::new(),
566            Circuit::new(),
567            Circuit::new(),
568        ];
569        let assignments = planner.plan_batch(&circuits);
570        assert_eq!(assignments.len(), 4);
571        assert_eq!(assignments[0].0, 0);
572        assert_eq!(assignments[1].0, 1);
573        assert_eq!(assignments[2].0, 0);
574        assert_eq!(assignments[3].0, 1);
575    }
576}