Skip to main content

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