1use super::config::*;
6use super::layers::*;
7use super::types::*;
8use crate::error::{Result, SimulatorError};
9use crate::scirs2_integration::SciRS2Backend;
10use scirs2_core::ndarray::Array1;
11use scirs2_core::Complex64;
12use std::f64::consts::PI;
13
14#[derive(Debug)]
16pub struct QuantumMLFramework {
17 config: QMLConfig,
19 pub layers: Vec<Box<dyn QMLLayer>>,
21 training_state: QMLTrainingState,
23 backend: Option<SciRS2Backend>,
25 stats: QMLStats,
27 training_history: Vec<QMLTrainingResult>,
29}
30
31impl QuantumMLFramework {
32 pub fn new(config: QMLConfig) -> Result<Self> {
34 let mut framework = Self {
35 config,
36 layers: Vec::new(),
37 training_state: QMLTrainingState::new(),
38 backend: None,
39 stats: QMLStats::new(),
40 training_history: Vec::new(),
41 };
42
43 framework.initialize_layers()?;
44
45 let backend = SciRS2Backend::new();
46 if backend.is_available() {
47 framework.backend = Some(backend);
48 }
49
50 Ok(framework)
51 }
52
53 fn initialize_layers(&mut self) -> Result<()> {
54 for layer_config in &self.config.layer_configs {
55 let layer = self.create_layer(layer_config)?;
56 self.layers.push(layer);
57 }
58 Ok(())
59 }
60
61 fn create_layer(&self, config: &QMLLayerConfig) -> Result<Box<dyn QMLLayer>> {
62 match config.layer_type {
63 QMLLayerType::ParameterizedQuantumCircuit => Ok(Box::new(
64 ParameterizedQuantumCircuitLayer::new(self.config.num_qubits, config.clone())?,
65 )),
66 QMLLayerType::QuantumConvolutional => Ok(Box::new(QuantumConvolutionalLayer::new(
67 self.config.num_qubits,
68 config.clone(),
69 )?)),
70 QMLLayerType::QuantumDense => Ok(Box::new(QuantumDenseLayer::new(
71 self.config.num_qubits,
72 config.clone(),
73 )?)),
74 QMLLayerType::QuantumLSTM => Ok(Box::new(QuantumLSTMLayer::new(
75 self.config.num_qubits,
76 config.clone(),
77 )?)),
78 QMLLayerType::QuantumAttention => Ok(Box::new(QuantumAttentionLayer::new(
79 self.config.num_qubits,
80 config.clone(),
81 )?)),
82 _ => Err(SimulatorError::InvalidConfiguration(format!(
83 "Layer type {:?} not yet implemented",
84 config.layer_type
85 ))),
86 }
87 }
88
89 pub fn forward(&mut self, input: &Array1<f64>) -> Result<Array1<f64>> {
91 let mut current_state = self.encode_input(input)?;
92
93 for layer in &mut self.layers {
94 current_state = layer.forward(¤t_state)?;
95 }
96
97 let output = self.decode_output(¤t_state)?;
98 self.stats.forward_passes += 1;
99
100 Ok(output)
101 }
102
103 pub fn backward(&mut self, loss_gradient: &Array1<f64>) -> Result<Array1<f64>> {
105 let mut grad = loss_gradient.clone();
106
107 for layer in self.layers.iter_mut().rev() {
108 grad = layer.backward(&grad)?;
109 }
110
111 self.stats.backward_passes += 1;
112 Ok(grad)
113 }
114
115 pub fn train(
117 &mut self,
118 training_data: &[(Array1<f64>, Array1<f64>)],
119 validation_data: Option<&[(Array1<f64>, Array1<f64>)]>,
120 ) -> Result<QMLTrainingResult> {
121 let mut best_validation_loss = f64::INFINITY;
122 let mut patience_counter = 0;
123 let mut training_metrics = Vec::new();
124
125 let training_start = std::time::Instant::now();
126
127 for epoch in 0..self.config.training_config.epochs {
128 let epoch_start = std::time::Instant::now();
129 let mut epoch_loss = 0.0;
130 let mut num_batches = 0;
131
132 for batch in training_data.chunks(self.config.training_config.batch_size) {
133 let batch_loss = self.train_batch(batch)?;
134 epoch_loss += batch_loss;
135 num_batches += 1;
136 }
137
138 epoch_loss /= f64::from(num_batches);
139
140 let validation_loss = if let Some(val_data) = validation_data {
141 self.evaluate(val_data)?
142 } else {
143 epoch_loss
144 };
145
146 let epoch_time = epoch_start.elapsed();
147
148 let metrics = QMLEpochMetrics {
149 epoch,
150 training_loss: epoch_loss,
151 validation_loss,
152 epoch_time,
153 learning_rate: self.get_current_learning_rate(epoch),
154 };
155
156 training_metrics.push(metrics.clone());
157
158 if self.config.training_config.early_stopping.enabled {
159 if validation_loss
160 < best_validation_loss - self.config.training_config.early_stopping.min_delta
161 {
162 best_validation_loss = validation_loss;
163 patience_counter = 0;
164 } else {
165 patience_counter += 1;
166 if patience_counter >= self.config.training_config.early_stopping.patience {
167 break;
168 }
169 }
170 }
171
172 self.update_learning_rate(epoch, validation_loss);
173 }
174
175 let total_training_time = training_start.elapsed();
176
177 let result = QMLTrainingResult {
178 final_training_loss: training_metrics.last().map_or(0.0, |m| m.training_loss),
179 final_validation_loss: training_metrics.last().map_or(0.0, |m| m.validation_loss),
180 best_validation_loss,
181 epochs_trained: training_metrics.len(),
182 total_training_time,
183 training_metrics,
184 quantum_advantage_metrics: self.compute_quantum_advantage_metrics()?,
185 };
186
187 self.training_history.push(result.clone());
188 Ok(result)
189 }
190
191 fn train_batch(&mut self, batch: &[(Array1<f64>, Array1<f64>)]) -> Result<f64> {
192 let mut total_loss = 0.0;
193 let mut total_gradients: Vec<Array1<f64>> =
194 (0..self.layers.len()).map(|_| Array1::zeros(0)).collect();
195
196 for (input, target) in batch {
197 let prediction = self.forward(input)?;
198 let loss = Self::compute_loss(&prediction, target)?;
199 total_loss += loss;
200
201 let loss_gradient = Self::compute_loss_gradient(&prediction, target)?;
202 let gradients = self.compute_gradients(&loss_gradient)?;
203
204 for (i, grad) in gradients.iter().enumerate() {
205 if total_gradients[i].is_empty() {
206 total_gradients[i] = grad.clone();
207 } else {
208 total_gradients[i] += grad;
209 }
210 }
211 }
212
213 let batch_size = batch.len() as f64;
214 for grad in &mut total_gradients {
215 *grad /= batch_size;
216 }
217
218 self.apply_gradients(&total_gradients)?;
219 Ok(total_loss / batch_size)
220 }
221
222 pub fn evaluate(&mut self, data: &[(Array1<f64>, Array1<f64>)]) -> Result<f64> {
224 let mut total_loss = 0.0;
225
226 for (input, target) in data {
227 let prediction = self.forward(input)?;
228 let loss = Self::compute_loss(&prediction, target)?;
229 total_loss += loss;
230 }
231
232 Ok(total_loss / data.len() as f64)
233 }
234
235 fn encode_input(&self, input: &Array1<f64>) -> Result<Array1<Complex64>> {
236 match self.config.classical_preprocessing.encoding_method {
237 DataEncodingMethod::Amplitude => self.encode_amplitude(input),
238 DataEncodingMethod::Angle => self.encode_angle(input),
239 DataEncodingMethod::Basis => self.encode_basis(input),
240 DataEncodingMethod::QuantumFeatureMap => self.encode_quantum_feature_map(input),
241 _ => Err(SimulatorError::InvalidConfiguration(
242 "Encoding method not implemented".to_string(),
243 )),
244 }
245 }
246
247 fn encode_amplitude(&self, input: &Array1<f64>) -> Result<Array1<Complex64>> {
248 let n_qubits = self.config.num_qubits;
249 let state_size = 1 << n_qubits;
250 let mut state = Array1::zeros(state_size);
251
252 let norm = input.iter().map(|x| x * x).sum::<f64>().sqrt();
253 if norm == 0.0 {
254 return Err(SimulatorError::InvalidState("Zero input norm".to_string()));
255 }
256
257 for (i, &val) in input.iter().enumerate() {
258 if i < state_size {
259 state[i] = Complex64::new(val / norm, 0.0);
260 }
261 }
262
263 Ok(state)
264 }
265
266 fn encode_angle(&self, input: &Array1<f64>) -> Result<Array1<Complex64>> {
267 let n_qubits = self.config.num_qubits;
268 let state_size = 1 << n_qubits;
269 let mut state = Array1::zeros(state_size);
270
271 state[0] = Complex64::new(1.0, 0.0);
272
273 for (i, &angle) in input.iter().enumerate() {
274 if i < n_qubits {
275 state = self.apply_ry_rotation(&state, i, angle)?;
276 }
277 }
278
279 Ok(state)
280 }
281
282 fn encode_basis(&self, input: &Array1<f64>) -> Result<Array1<Complex64>> {
283 let n_qubits = self.config.num_qubits;
284 let state_size = 1 << n_qubits;
285 let mut state = Array1::zeros(state_size);
286
287 let mut binary_index = 0;
288 for (i, &val) in input.iter().enumerate() {
289 if i < n_qubits && val > 0.5 {
290 binary_index |= 1 << i;
291 }
292 }
293
294 state[binary_index] = Complex64::new(1.0, 0.0);
295 Ok(state)
296 }
297
298 fn encode_quantum_feature_map(&self, input: &Array1<f64>) -> Result<Array1<Complex64>> {
299 let n_qubits = self.config.num_qubits;
300 let state_size = 1 << n_qubits;
301 let mut state = Array1::zeros(state_size);
302
303 let hadamard_coeff = 1.0 / (n_qubits as f64 / 2.0).exp2();
304 for i in 0..state_size {
305 state[i] = Complex64::new(hadamard_coeff, 0.0);
306 }
307
308 for (i, &feature) in input.iter().enumerate() {
309 if i < n_qubits {
310 state = self.apply_rz_rotation(&state, i, feature * PI)?;
311 }
312 }
313
314 for i in 0..(n_qubits - 1) {
315 if i + 1 < input.len() {
316 let interaction = input[i] * input[i + 1];
317 state = self.apply_cnot_interaction(&state, i, i + 1, interaction * PI)?;
318 }
319 }
320
321 Ok(state)
322 }
323
324 fn apply_ry_rotation(
325 &self,
326 state: &Array1<Complex64>,
327 qubit: usize,
328 angle: f64,
329 ) -> Result<Array1<Complex64>> {
330 let n_qubits = self.config.num_qubits;
331 let state_size = 1 << n_qubits;
332 let mut new_state = state.clone();
333
334 let cos_half = (angle / 2.0).cos();
335 let sin_half = (angle / 2.0).sin();
336
337 for i in 0..state_size {
338 if i & (1 << qubit) == 0 {
339 let j = i | (1 << qubit);
340 if j < state_size {
341 let state_0 = state[i];
342 let state_1 = state[j];
343
344 new_state[i] = Complex64::new(cos_half, 0.0) * state_0
345 - Complex64::new(sin_half, 0.0) * state_1;
346 new_state[j] = Complex64::new(sin_half, 0.0) * state_0
347 + Complex64::new(cos_half, 0.0) * state_1;
348 }
349 }
350 }
351
352 Ok(new_state)
353 }
354
355 fn apply_rz_rotation(
356 &self,
357 state: &Array1<Complex64>,
358 qubit: usize,
359 angle: f64,
360 ) -> Result<Array1<Complex64>> {
361 let n_qubits = self.config.num_qubits;
362 let state_size = 1 << n_qubits;
363 let mut new_state = state.clone();
364
365 let phase_0 = Complex64::from_polar(1.0, -angle / 2.0);
366 let phase_1 = Complex64::from_polar(1.0, angle / 2.0);
367
368 for i in 0..state_size {
369 if i & (1 << qubit) == 0 {
370 new_state[i] *= phase_0;
371 } else {
372 new_state[i] *= phase_1;
373 }
374 }
375
376 Ok(new_state)
377 }
378
379 fn apply_cnot_interaction(
380 &self,
381 state: &Array1<Complex64>,
382 control: usize,
383 target: usize,
384 interaction: f64,
385 ) -> Result<Array1<Complex64>> {
386 let n_qubits = self.config.num_qubits;
387 let state_size = 1 << n_qubits;
388 let mut new_state = state.clone();
389
390 let phase = Complex64::from_polar(1.0, interaction);
391
392 for i in 0..state_size {
393 if (i & (1 << control)) != 0 && (i & (1 << target)) != 0 {
394 new_state[i] *= phase;
395 }
396 }
397
398 Ok(new_state)
399 }
400
401 fn decode_output(&self, state: &Array1<Complex64>) -> Result<Array1<f64>> {
402 let n_qubits = self.config.num_qubits;
403 let mut output = Array1::zeros(n_qubits);
404
405 for qubit in 0..n_qubits {
406 let expectation = Self::measure_pauli_z_expectation(state, qubit)?;
407 output[qubit] = expectation;
408 }
409
410 Ok(output)
411 }
412
413 fn measure_pauli_z_expectation(state: &Array1<Complex64>, qubit: usize) -> Result<f64> {
414 let state_size = state.len();
415 let mut expectation = 0.0;
416
417 for i in 0..state_size {
418 let probability = state[i].norm_sqr();
419 if i & (1 << qubit) == 0 {
420 expectation += probability;
421 } else {
422 expectation -= probability;
423 }
424 }
425
426 Ok(expectation)
427 }
428
429 fn compute_loss(prediction: &Array1<f64>, target: &Array1<f64>) -> Result<f64> {
430 if prediction.shape() != target.shape() {
431 return Err(SimulatorError::InvalidInput(format!(
432 "Shape mismatch: prediction {:?} != target {:?}",
433 prediction.shape(),
434 target.shape()
435 )));
436 }
437
438 let diff = prediction - target;
439 let mse = diff.iter().map(|x| x * x).sum::<f64>() / diff.len() as f64;
440 Ok(mse)
441 }
442
443 fn compute_loss_gradient(
444 prediction: &Array1<f64>,
445 target: &Array1<f64>,
446 ) -> Result<Array1<f64>> {
447 let diff = prediction - target;
448 let grad = 2.0 * &diff / diff.len() as f64;
449 Ok(grad)
450 }
451
452 fn compute_gradients(&mut self, loss_gradient: &Array1<f64>) -> Result<Vec<Array1<f64>>> {
453 let mut gradients = Vec::new();
454
455 for layer_idx in 0..self.layers.len() {
456 let layer_gradient = match self.config.training_config.gradient_method {
457 GradientMethod::ParameterShift => {
458 self.compute_parameter_shift_gradient(layer_idx, loss_gradient)?
459 }
460 GradientMethod::FiniteDifference => {
461 self.compute_finite_difference_gradient(layer_idx, loss_gradient)?
462 }
463 _ => {
464 return Err(SimulatorError::InvalidConfiguration(
465 "Gradient method not implemented".to_string(),
466 ))
467 }
468 };
469 gradients.push(layer_gradient);
470 }
471
472 Ok(gradients)
473 }
474
475 fn compute_parameter_shift_gradient(
476 &mut self,
477 layer_idx: usize,
478 loss_gradient: &Array1<f64>,
479 ) -> Result<Array1<f64>> {
480 let layer = &self.layers[layer_idx];
481 let parameters = layer.get_parameters();
482 let mut gradient = Array1::zeros(parameters.len());
483
484 let shift = PI / 2.0;
485
486 for (param_idx, ¶m_val) in parameters.iter().enumerate() {
487 let mut params_plus = parameters.clone();
488 params_plus[param_idx] = param_val + shift;
489 self.layers[layer_idx].set_parameters(¶ms_plus);
490 let output_plus = self.forward_layer(layer_idx, loss_gradient)?;
491
492 let mut params_minus = parameters.clone();
493 params_minus[param_idx] = param_val - shift;
494 self.layers[layer_idx].set_parameters(¶ms_minus);
495 let output_minus = self.forward_layer(layer_idx, loss_gradient)?;
496
497 gradient[param_idx] = (output_plus.sum() - output_minus.sum()) / 2.0;
498
499 self.layers[layer_idx].set_parameters(¶meters);
500 }
501
502 Ok(gradient)
503 }
504
505 fn compute_finite_difference_gradient(
506 &mut self,
507 layer_idx: usize,
508 loss_gradient: &Array1<f64>,
509 ) -> Result<Array1<f64>> {
510 let layer = &self.layers[layer_idx];
511 let parameters = layer.get_parameters();
512 let mut gradient = Array1::zeros(parameters.len());
513
514 let eps = 1e-6;
515
516 for (param_idx, ¶m_val) in parameters.iter().enumerate() {
517 let mut params_plus = parameters.clone();
518 params_plus[param_idx] = param_val + eps;
519 self.layers[layer_idx].set_parameters(¶ms_plus);
520 let output_plus = self.forward_layer(layer_idx, loss_gradient)?;
521
522 let mut params_minus = parameters.clone();
523 params_minus[param_idx] = param_val - eps;
524 self.layers[layer_idx].set_parameters(¶ms_minus);
525 let output_minus = self.forward_layer(layer_idx, loss_gradient)?;
526
527 gradient[param_idx] = (output_plus.sum() - output_minus.sum()) / (2.0 * eps);
528
529 self.layers[layer_idx].set_parameters(¶meters);
530 }
531
532 Ok(gradient)
533 }
534
535 fn forward_layer(&mut self, _layer_idx: usize, input: &Array1<f64>) -> Result<Array1<f64>> {
536 self.forward(input)
537 }
538
539 fn apply_gradients(&mut self, gradients: &[Array1<f64>]) -> Result<()> {
540 for (layer_idx, gradient) in gradients.iter().enumerate() {
541 let layer = &mut self.layers[layer_idx];
542 let mut parameters = layer.get_parameters();
543
544 match self.config.training_config.optimizer {
545 OptimizerType::SGD => {
546 for (param, grad) in parameters.iter_mut().zip(gradient.iter()) {
547 *param -= self.config.training_config.learning_rate * grad;
548 }
549 }
550 OptimizerType::Adam => {
551 for (param, grad) in parameters.iter_mut().zip(gradient.iter()) {
552 *param -= self.config.training_config.learning_rate * grad;
553 }
554 }
555 _ => {
556 for (param, grad) in parameters.iter_mut().zip(gradient.iter()) {
557 *param -= self.config.training_config.learning_rate * grad;
558 }
559 }
560 }
561
562 if let Some((min_val, max_val)) =
563 self.config.training_config.regularization.parameter_bounds
564 {
565 for param in &mut parameters {
566 *param = param.clamp(min_val, max_val);
567 }
568 }
569
570 layer.set_parameters(¶meters);
571 }
572
573 Ok(())
574 }
575
576 fn get_current_learning_rate(&self, epoch: usize) -> f64 {
577 let base_lr = self.config.training_config.learning_rate;
578
579 match self.config.training_config.lr_schedule {
580 LearningRateSchedule::Constant => base_lr,
581 LearningRateSchedule::ExponentialDecay => base_lr * 0.95_f64.powi(epoch as i32),
582 LearningRateSchedule::StepDecay => {
583 if epoch % 50 == 0 && epoch > 0 {
584 base_lr * 0.5_f64.powi((epoch / 50) as i32)
585 } else {
586 base_lr
587 }
588 }
589 LearningRateSchedule::CosineAnnealing => {
590 let progress = epoch as f64 / self.config.training_config.epochs as f64;
591 base_lr * 0.5 * (1.0 + (PI * progress).cos())
592 }
593 _ => base_lr,
594 }
595 }
596
597 fn update_learning_rate(&mut self, epoch: usize, _validation_loss: f64) {
598 let current_lr = self.get_current_learning_rate(epoch);
599 self.training_state.current_learning_rate = current_lr;
600 }
601
602 fn compute_quantum_advantage_metrics(&self) -> Result<QuantumAdvantageMetrics> {
603 Ok(QuantumAdvantageMetrics {
604 quantum_volume: 0.0,
605 classical_simulation_cost: 0.0,
606 quantum_speedup_factor: 1.0,
607 circuit_depth: self.layers.iter().map(|l| l.get_depth()).sum(),
608 gate_count: self.layers.iter().map(|l| l.get_gate_count()).sum(),
609 entanglement_measure: 0.0,
610 })
611 }
612
613 #[must_use]
614 pub const fn get_stats(&self) -> &QMLStats {
615 &self.stats
616 }
617
618 #[must_use]
619 pub fn get_training_history(&self) -> &[QMLTrainingResult] {
620 &self.training_history
621 }
622
623 #[must_use]
624 pub fn get_layers(&self) -> &[Box<dyn QMLLayer>] {
625 &self.layers
626 }
627
628 #[must_use]
629 pub const fn get_config(&self) -> &QMLConfig {
630 &self.config
631 }
632
633 pub fn encode_amplitude_public(&self, input: &Array1<f64>) -> Result<Array1<Complex64>> {
634 self.encode_amplitude(input)
635 }
636
637 pub fn encode_angle_public(&self, input: &Array1<f64>) -> Result<Array1<Complex64>> {
638 self.encode_angle(input)
639 }
640
641 pub fn encode_basis_public(&self, input: &Array1<f64>) -> Result<Array1<Complex64>> {
642 self.encode_basis(input)
643 }
644
645 pub fn encode_quantum_feature_map_public(
646 &self,
647 input: &Array1<f64>,
648 ) -> Result<Array1<Complex64>> {
649 self.encode_quantum_feature_map(input)
650 }
651
652 pub fn measure_pauli_z_expectation_public(
653 &self,
654 state: &Array1<Complex64>,
655 qubit: usize,
656 ) -> Result<f64> {
657 Self::measure_pauli_z_expectation(state, qubit)
658 }
659
660 #[must_use]
661 pub fn get_current_learning_rate_public(&self, epoch: usize) -> f64 {
662 self.get_current_learning_rate(epoch)
663 }
664
665 pub fn compute_loss_public(
666 &self,
667 prediction: &Array1<f64>,
668 target: &Array1<f64>,
669 ) -> Result<f64> {
670 Self::compute_loss(prediction, target)
671 }
672
673 pub fn compute_loss_gradient_public(
674 &self,
675 prediction: &Array1<f64>,
676 target: &Array1<f64>,
677 ) -> Result<Array1<f64>> {
678 Self::compute_loss_gradient(prediction, target)
679 }
680}