quantrs2_sim/quantum_machine_learning_layers/
utils.rs1use super::config::{QMLArchitectureType, QMLConfig};
6use super::framework::QuantumMLFramework;
7use super::types::{QMLBenchmarkResults, QuantumAdvantageMetrics};
8use crate::error::Result;
9use scirs2_core::ndarray::Array1;
10use scirs2_core::random::prelude::*;
11use std::collections::HashMap;
12
13pub struct QMLUtils;
15
16impl QMLUtils {
17 #[must_use]
19 pub fn generate_synthetic_data(
20 num_samples: usize,
21 input_dim: usize,
22 output_dim: usize,
23 ) -> (Vec<Array1<f64>>, Vec<Array1<f64>>) {
24 let mut rng = thread_rng();
25 let mut inputs = Vec::new();
26 let mut outputs = Vec::new();
27
28 for _ in 0..num_samples {
29 let input: Array1<f64> = Array1::from_vec(
30 (0..input_dim)
31 .map(|_| rng.random_range(-1.0_f64..1.0_f64))
32 .collect(),
33 );
34
35 let output = Array1::from_vec(
37 (0..output_dim)
38 .map(|i| {
39 if i < input_dim {
40 input[i].sin() } else {
42 rng.random_range(-1.0_f64..1.0_f64)
43 }
44 })
45 .collect(),
46 );
47
48 inputs.push(input);
49 outputs.push(output);
50 }
51
52 (inputs, outputs)
53 }
54
55 #[must_use]
57 pub fn train_test_split(
58 inputs: Vec<Array1<f64>>,
59 outputs: Vec<Array1<f64>>,
60 test_ratio: f64,
61 ) -> (
62 Vec<(Array1<f64>, Array1<f64>)>,
63 Vec<(Array1<f64>, Array1<f64>)>,
64 ) {
65 let total_samples = inputs.len();
66 let test_samples = ((total_samples as f64) * test_ratio) as usize;
67 let train_samples = total_samples - test_samples;
68
69 let mut combined: Vec<(Array1<f64>, Array1<f64>)> =
70 inputs.into_iter().zip(outputs).collect();
71
72 let mut rng = thread_rng();
74 for i in (1..combined.len()).rev() {
75 let j = rng.random_range(0..=i);
76 combined.swap(i, j);
77 }
78
79 let (train_data, test_data) = combined.split_at(train_samples);
80 (train_data.to_vec(), test_data.to_vec())
81 }
82
83 #[must_use]
85 pub fn evaluate_accuracy(
86 predictions: &[Array1<f64>],
87 targets: &[Array1<f64>],
88 threshold: f64,
89 ) -> f64 {
90 let mut correct = 0;
91 let total = predictions.len();
92
93 for (pred, target) in predictions.iter().zip(targets.iter()) {
94 let diff = pred - target;
95 let mse = diff.iter().map(|x| x * x).sum::<f64>() / diff.len() as f64;
96 if mse < threshold {
97 correct += 1;
98 }
99 }
100
101 f64::from(correct) / total as f64
102 }
103
104 #[must_use]
106 pub fn compute_circuit_complexity(
107 num_qubits: usize,
108 depth: usize,
109 gate_count: usize,
110 ) -> HashMap<String, f64> {
111 let mut metrics = HashMap::new();
112
113 let state_space_size = 2.0_f64.powi(num_qubits as i32);
115 metrics.insert("state_space_size".to_string(), state_space_size);
116
117 let circuit_complexity = (depth * gate_count) as f64;
119 metrics.insert("circuit_complexity".to_string(), circuit_complexity);
120
121 let classical_cost = state_space_size * gate_count as f64;
123 metrics.insert("classical_simulation_cost".to_string(), classical_cost);
124
125 let quantum_advantage = classical_cost.log(circuit_complexity);
127 metrics.insert("quantum_advantage_estimate".to_string(), quantum_advantage);
128
129 metrics
130 }
131}
132
133pub fn benchmark_quantum_ml_layers(config: &QMLConfig) -> Result<QMLBenchmarkResults> {
135 let mut results = QMLBenchmarkResults {
136 training_times: HashMap::new(),
137 final_accuracies: HashMap::new(),
138 convergence_rates: HashMap::new(),
139 memory_usage: HashMap::new(),
140 quantum_advantage: HashMap::new(),
141 parameter_counts: HashMap::new(),
142 circuit_depths: HashMap::new(),
143 gate_counts: HashMap::new(),
144 };
145
146 let (inputs, outputs) =
148 QMLUtils::generate_synthetic_data(100, config.num_qubits, config.num_qubits);
149 let (train_data, val_data) = QMLUtils::train_test_split(inputs, outputs, 0.2);
150
151 let architectures = vec![
153 QMLArchitectureType::VariationalQuantumCircuit,
154 QMLArchitectureType::QuantumConvolutionalNN,
155 ];
157
158 for architecture in architectures {
159 let arch_name = format!("{architecture:?}");
160
161 let mut arch_config = config.clone();
163 arch_config.architecture_type = architecture;
164
165 let start_time = std::time::Instant::now();
167 let mut framework = QuantumMLFramework::new(arch_config)?;
168
169 let training_result = framework.train(&train_data, Some(&val_data))?;
170 let training_time = start_time.elapsed();
171
172 let final_accuracy = framework.evaluate(&val_data)?;
174
175 results
177 .training_times
178 .insert(arch_name.clone(), training_time);
179 results
180 .final_accuracies
181 .insert(arch_name.clone(), 1.0 / (1.0 + final_accuracy)); results.convergence_rates.insert(
183 arch_name.clone(),
184 training_result.epochs_trained as f64 / config.training_config.epochs as f64,
185 );
186 results
187 .memory_usage
188 .insert(arch_name.clone(), framework.get_stats().peak_memory_usage);
189 results
190 .quantum_advantage
191 .insert(arch_name.clone(), training_result.quantum_advantage_metrics);
192 results.parameter_counts.insert(
193 arch_name.clone(),
194 framework
195 .layers
196 .iter()
197 .map(|l| l.get_num_parameters())
198 .sum(),
199 );
200 results.circuit_depths.insert(
201 arch_name.clone(),
202 framework.layers.iter().map(|l| l.get_depth()).sum(),
203 );
204 results.gate_counts.insert(
205 arch_name.clone(),
206 framework.layers.iter().map(|l| l.get_gate_count()).sum(),
207 );
208 }
209
210 Ok(results)
211}