1use crate::error::{MLError, Result};
9use scirs2_core::ndarray::{s, Array1, Array2, Array3, ArrayD, Axis};
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct QRCConfig {
16 pub reservoir_qubits: usize,
18 pub input_qubits: usize,
20 pub readout_size: usize,
22 pub reservoir_dynamics: ReservoirDynamics,
24 pub input_encoding: InputEncoding,
26 pub readout_config: ReadoutConfig,
28 pub training_config: QRCTrainingConfig,
30 pub temporal_config: TemporalConfig,
32 pub noise_config: Option<NoiseConfig>,
34}
35
36impl Default for QRCConfig {
37 fn default() -> Self {
38 Self {
39 reservoir_qubits: 10,
40 input_qubits: 4,
41 readout_size: 8,
42 reservoir_dynamics: ReservoirDynamics::default(),
43 input_encoding: InputEncoding::default(),
44 readout_config: ReadoutConfig::default(),
45 training_config: QRCTrainingConfig::default(),
46 temporal_config: TemporalConfig::default(),
47 noise_config: None,
48 }
49 }
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct ReservoirDynamics {
55 pub evolution_time: f64,
57 pub coupling_strength: f64,
59 pub external_field: f64,
61 pub hamiltonian_type: HamiltonianType,
63 pub random_interactions: bool,
65 pub randomness_strength: f64,
67 pub memory_length: usize,
69}
70
71impl Default for ReservoirDynamics {
72 fn default() -> Self {
73 Self {
74 evolution_time: 1.0,
75 coupling_strength: 0.1,
76 external_field: 0.05,
77 hamiltonian_type: HamiltonianType::TransverseFieldIsing,
78 random_interactions: true,
79 randomness_strength: 0.02,
80 memory_length: 10,
81 }
82 }
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub enum HamiltonianType {
88 TransverseFieldIsing,
90 Heisenberg,
92 RandomField,
94 SpinGlass,
96 Custom {
98 interactions: HashMap<String, f64>,
99 fields: HashMap<String, f64>,
100 },
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct InputEncoding {
106 pub encoding_type: EncodingType,
108 pub normalization: NormalizationType,
110 pub feature_mapping: FeatureMapping,
112 pub temporal_encoding: bool,
114}
115
116impl Default for InputEncoding {
117 fn default() -> Self {
118 Self {
119 encoding_type: EncodingType::Amplitude,
120 normalization: NormalizationType::L2,
121 feature_mapping: FeatureMapping::Linear,
122 temporal_encoding: true,
123 }
124 }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
129pub enum EncodingType {
130 Amplitude,
132 Angle,
134 Basis,
136 Displacement,
138 Hybrid,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144pub enum NormalizationType {
145 L2,
147 MinMax,
149 StandardScore,
151 None,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157pub enum FeatureMapping {
158 Linear,
160 Polynomial { degree: usize },
162 Fourier { frequencies: Vec<f64> },
164 Random { dimension: usize },
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct ReadoutConfig {
171 pub readout_type: ReadoutType,
173 pub observables: Vec<Observable>,
175 pub regularization: RegularizationConfig,
177 pub activation: ActivationFunction,
179}
180
181impl Default for ReadoutConfig {
182 fn default() -> Self {
183 Self {
184 readout_type: ReadoutType::LinearRegression,
185 observables: vec![
186 Observable::PauliZ(0),
187 Observable::PauliZ(1),
188 Observable::PauliX(0),
189 Observable::PauliY(0),
190 ],
191 regularization: RegularizationConfig::default(),
192 activation: ActivationFunction::Linear,
193 }
194 }
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
199pub enum ReadoutType {
200 LinearRegression,
202 RidgeRegression,
204 LassoRegression,
206 SVR,
208 NeuralNetwork,
210 QuantumReadout,
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
216pub enum Observable {
217 PauliZ(usize),
219 PauliX(usize),
221 PauliY(usize),
223 Correlation(usize, usize),
225 Custom(String),
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct RegularizationConfig {
232 pub l1_strength: f64,
234 pub l2_strength: f64,
236 pub dropout_rate: f64,
238}
239
240impl Default for RegularizationConfig {
241 fn default() -> Self {
242 Self {
243 l1_strength: 0.0,
244 l2_strength: 0.01,
245 dropout_rate: 0.0,
246 }
247 }
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252pub enum ActivationFunction {
253 Linear,
254 ReLU,
255 Sigmoid,
256 Tanh,
257 Softmax,
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize)]
262pub struct QRCTrainingConfig {
263 pub epochs: usize,
265 pub learning_rate: f64,
267 pub batch_size: usize,
269 pub validation_split: f64,
271 pub early_stopping_patience: usize,
273 pub washout_period: usize,
275}
276
277impl Default for QRCTrainingConfig {
278 fn default() -> Self {
279 Self {
280 epochs: 100,
281 learning_rate: 0.01,
282 batch_size: 32,
283 validation_split: 0.2,
284 early_stopping_patience: 10,
285 washout_period: 5,
286 }
287 }
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct TemporalConfig {
293 pub sequence_length: usize,
295 pub time_step: f64,
297 pub temporal_correlation: bool,
299 pub memory_decay: f64,
301}
302
303impl Default for TemporalConfig {
304 fn default() -> Self {
305 Self {
306 sequence_length: 10,
307 time_step: 1.0,
308 temporal_correlation: true,
309 memory_decay: 0.95,
310 }
311 }
312}
313
314#[derive(Debug, Clone, Serialize, Deserialize)]
316pub struct NoiseConfig {
317 pub t1_time: f64,
319 pub t2_time: f64,
321 pub gate_error_rate: f64,
323 pub measurement_error_rate: f64,
325}
326
327#[derive(Debug, Clone)]
329pub struct QuantumReservoirComputer {
330 config: QRCConfig,
331 reservoir: QuantumReservoir,
332 input_encoder: InputEncoder,
333 readout_layer: ReadoutLayer,
334 training_history: Vec<TrainingMetrics>,
335 reservoir_states: Vec<Array1<f64>>, }
337
338#[derive(Debug, Clone)]
340pub struct QuantumReservoir {
341 num_qubits: usize,
342 current_state: Array1<f64>, hamiltonian: Array2<f64>, evolution_operator: Array2<f64>, coupling_matrix: Array2<f64>, random_fields: Array1<f64>, }
348
349#[derive(Debug, Clone)]
351pub struct InputEncoder {
352 encoding_type: EncodingType,
353 feature_dimension: usize,
354 encoding_gates: Vec<EncodingGate>,
355 normalization_params: Option<NormalizationParams>,
356}
357
358#[derive(Debug, Clone)]
360pub struct EncodingGate {
361 gate_type: String,
362 target_qubit: usize,
363 parameter_index: usize,
364}
365
366#[derive(Debug, Clone)]
368pub struct NormalizationParams {
369 mean: Array1<f64>,
370 std: Array1<f64>,
371 min: Array1<f64>,
372 max: Array1<f64>,
373}
374
375#[derive(Debug, Clone)]
377pub struct ReadoutLayer {
378 weights: Array2<f64>,
379 biases: Array1<f64>,
380 readout_type: ReadoutType,
381 observables: Vec<Observable>,
382 activation: ActivationFunction,
383}
384
385#[derive(Debug, Clone)]
387pub struct TrainingMetrics {
388 epoch: usize,
389 training_loss: f64,
390 validation_loss: f64,
391 training_accuracy: f64,
392 validation_accuracy: f64,
393 reservoir_capacity: f64,
394 memory_function: f64,
395}
396
397impl QuantumReservoirComputer {
398 pub fn new(config: QRCConfig) -> Result<Self> {
400 let reservoir = QuantumReservoir::new(&config)?;
401 let input_encoder = InputEncoder::new(&config)?;
402 let readout_layer = ReadoutLayer::new(&config)?;
403
404 Ok(Self {
405 config,
406 reservoir,
407 input_encoder,
408 readout_layer,
409 training_history: Vec::new(),
410 reservoir_states: Vec::new(),
411 })
412 }
413
414 pub fn process_sequence(&mut self, input_sequence: &Array2<f64>) -> Result<Array2<f64>> {
416 let sequence_length = input_sequence.nrows();
417 let feature_dim = input_sequence.ncols();
418 let num_observables = self.config.readout_config.observables.len();
419
420 let mut reservoir_outputs = Array2::zeros((sequence_length, num_observables));
421
422 self.reservoir.reset_state()?;
424
425 for t in 0..sequence_length {
426 let input = input_sequence.row(t);
427
428 let encoded_input = self.input_encoder.encode(&input.to_owned())?;
430
431 self.reservoir.inject_input(&encoded_input)?;
433
434 self.reservoir
436 .evolve_dynamics(self.config.reservoir_dynamics.evolution_time)?;
437
438 let measurements = self
440 .reservoir
441 .measure_observables(&self.config.readout_config.observables)?;
442
443 for (i, &measurement) in measurements.iter().enumerate() {
445 reservoir_outputs[[t, i]] = measurement;
446 }
447
448 self.reservoir_states
450 .push(self.reservoir.current_state.clone());
451 }
452
453 Ok(reservoir_outputs)
454 }
455
456 pub fn train(&mut self, training_data: &[(Array2<f64>, Array2<f64>)]) -> Result<()> {
458 let num_epochs = self.config.training_config.epochs;
459 let washout = self.config.training_config.washout_period;
460
461 let mut all_states = Vec::new();
463 let mut all_targets = Vec::new();
464
465 for (input_sequence, target_sequence) in training_data {
466 let reservoir_output = self.process_sequence(input_sequence)?;
467
468 let effective_washout = washout.min(reservoir_output.nrows().saturating_sub(1));
470 for t in effective_washout..reservoir_output.nrows() {
471 all_states.push(reservoir_output.row(t).to_owned());
472 all_targets.push(target_sequence.row(t).to_owned());
473 }
474 }
475
476 if all_states.is_empty() {
478 return Err(MLError::MLOperationError(
479 "No training data available after washout period".to_string(),
480 ));
481 }
482
483 let states_matrix = Array2::from_shape_vec(
485 (all_states.len(), all_states[0].len()),
486 all_states.into_iter().flatten().collect(),
487 )?;
488
489 let targets_matrix = Array2::from_shape_vec(
490 (all_targets.len(), all_targets[0].len()),
491 all_targets.into_iter().flatten().collect(),
492 )?;
493
494 self.readout_layer.train(
496 &states_matrix,
497 &targets_matrix,
498 &self.config.training_config,
499 )?;
500
501 for epoch in 0..num_epochs {
503 let training_loss = self.evaluate_loss(&states_matrix, &targets_matrix)?;
504 let training_accuracy = self.evaluate_accuracy(&states_matrix, &targets_matrix)?;
505
506 let metrics = TrainingMetrics {
507 epoch,
508 training_loss,
509 validation_loss: training_loss * 1.1, training_accuracy,
511 validation_accuracy: training_accuracy * 0.95, reservoir_capacity: self.compute_reservoir_capacity()?,
513 memory_function: self.compute_memory_function()?,
514 };
515
516 self.training_history.push(metrics);
517
518 if epoch % 10 == 0 {
519 println!(
520 "Epoch {}: Loss = {:.6}, Accuracy = {:.4}",
521 epoch, training_loss, training_accuracy
522 );
523 }
524 }
525
526 Ok(())
527 }
528
529 pub fn predict(&mut self, input_sequence: &Array2<f64>) -> Result<Array2<f64>> {
531 let reservoir_output = self.process_sequence(input_sequence)?;
532 self.readout_layer.predict(&reservoir_output)
533 }
534
535 fn evaluate_loss(&self, states: &Array2<f64>, targets: &Array2<f64>) -> Result<f64> {
537 let predictions = self.readout_layer.predict(states)?;
538 let mse = predictions
539 .iter()
540 .zip(targets.iter())
541 .map(|(p, t)| (p - t).powi(2))
542 .sum::<f64>()
543 / predictions.len() as f64;
544 Ok(mse)
545 }
546
547 fn evaluate_accuracy(&self, states: &Array2<f64>, targets: &Array2<f64>) -> Result<f64> {
549 let predictions = self.readout_layer.predict(states)?;
550
551 let target_mean = targets.mean().unwrap();
553 let ss_tot = targets
554 .iter()
555 .map(|t| (t - target_mean).powi(2))
556 .sum::<f64>();
557 let ss_res = predictions
558 .iter()
559 .zip(targets.iter())
560 .map(|(p, t)| (t - p).powi(2))
561 .sum::<f64>();
562
563 let r_squared = 1.0 - (ss_res / ss_tot);
564 Ok(r_squared.max(0.0))
565 }
566
567 fn compute_reservoir_capacity(&self) -> Result<f64> {
569 if self.reservoir_states.is_empty() {
571 return Ok(0.0);
572 }
573
574 let num_states = self.reservoir_states.len();
575 let state_dim = self.reservoir_states[0].len();
576
577 let mut total_distance = 0.0;
579 let mut count = 0;
580
581 for i in 0..num_states {
582 for j in i + 1..num_states {
583 let distance = self.reservoir_states[i]
584 .iter()
585 .zip(self.reservoir_states[j].iter())
586 .map(|(a, b)| (a - b).powi(2))
587 .sum::<f64>()
588 .sqrt();
589 total_distance += distance;
590 count += 1;
591 }
592 }
593
594 let average_distance = if count > 0 {
595 total_distance / count as f64
596 } else {
597 0.0
598 };
599 let capacity = average_distance / (state_dim as f64).sqrt();
600
601 Ok(capacity)
602 }
603
604 fn compute_memory_function(&self) -> Result<f64> {
606 if self.reservoir_states.len() < 2 {
608 return Ok(0.0);
609 }
610
611 let mut autocorrelations = Vec::new();
612 let max_lag = self
613 .config
614 .reservoir_dynamics
615 .memory_length
616 .min(self.reservoir_states.len() / 2);
617
618 for lag in 1..=max_lag {
619 let mut correlation = 0.0;
620 let mut count = 0;
621
622 for t in lag..self.reservoir_states.len() {
623 let state_t = &self.reservoir_states[t];
624 let state_t_lag = &self.reservoir_states[t - lag];
625
626 let dot_product = state_t
627 .iter()
628 .zip(state_t_lag.iter())
629 .map(|(a, b)| a * b)
630 .sum::<f64>();
631
632 correlation += dot_product;
633 count += 1;
634 }
635
636 if count > 0 {
637 autocorrelations.push(correlation / count as f64);
638 }
639 }
640
641 let memory_function = autocorrelations.iter().sum::<f64>().abs();
643 Ok(memory_function)
644 }
645
646 pub fn get_training_history(&self) -> &[TrainingMetrics] {
648 &self.training_history
649 }
650
651 pub fn get_reservoir_states(&self) -> &[Array1<f64>] {
653 &self.reservoir_states
654 }
655
656 pub fn analyze_dynamics(&self) -> Result<DynamicsAnalysis> {
658 let spectral_radius = self.reservoir.compute_spectral_radius()?;
659 let lyapunov_exponent = self.reservoir.compute_lyapunov_exponent()?;
660 let entanglement_measure = self.reservoir.compute_entanglement()?;
661
662 Ok(DynamicsAnalysis {
663 spectral_radius,
664 lyapunov_exponent,
665 entanglement_measure,
666 capacity: self.compute_reservoir_capacity()?,
667 memory_function: self.compute_memory_function()?,
668 })
669 }
670}
671
672impl QuantumReservoir {
673 pub fn new(config: &QRCConfig) -> Result<Self> {
675 let num_qubits = config.reservoir_qubits;
676 let state_dim = 1 << num_qubits;
677
678 let mut current_state = Array1::zeros(state_dim);
680 current_state[0] = 1.0;
681
682 let hamiltonian = Self::build_hamiltonian(config)?;
684
685 let evolution_operator = Self::compute_evolution_operator(
687 &hamiltonian,
688 config.reservoir_dynamics.evolution_time,
689 )?;
690
691 let coupling_matrix = Self::generate_coupling_matrix(
693 num_qubits,
694 config.reservoir_dynamics.coupling_strength,
695 )?;
696
697 let random_fields = Array1::from_shape_fn(num_qubits, |_| {
699 if config.reservoir_dynamics.random_interactions {
700 (fastrand::f64() - 0.5) * config.reservoir_dynamics.randomness_strength
701 } else {
702 0.0
703 }
704 });
705
706 Ok(Self {
707 num_qubits,
708 current_state,
709 hamiltonian,
710 evolution_operator,
711 coupling_matrix,
712 random_fields,
713 })
714 }
715
716 fn build_hamiltonian(config: &QRCConfig) -> Result<Array2<f64>> {
718 let num_qubits = config.reservoir_qubits;
719 let dim = 1 << num_qubits;
720 let mut hamiltonian = Array2::zeros((dim, dim));
721
722 match &config.reservoir_dynamics.hamiltonian_type {
723 HamiltonianType::TransverseFieldIsing => {
724 let coupling = config.reservoir_dynamics.coupling_strength;
726 let field = config.reservoir_dynamics.external_field;
727
728 for i in 0..num_qubits - 1 {
730 for state in 0..dim {
731 let zi = if (state >> i) & 1 == 0 { 1.0 } else { -1.0 };
732 let zj = if (state >> (i + 1)) & 1 == 0 {
733 1.0
734 } else {
735 -1.0
736 };
737 hamiltonian[[state, state]] -= coupling * zi * zj;
738 }
739 }
740
741 for i in 0..num_qubits {
743 for state in 0..dim {
744 let flipped_state = state ^ (1 << i);
745 hamiltonian[[state, flipped_state]] -= field;
746 }
747 }
748 }
749 HamiltonianType::Heisenberg => {
750 let coupling = config.reservoir_dynamics.coupling_strength;
752
753 for i in 0..num_qubits - 1 {
754 for state in 0..dim {
756 let zi = if (state >> i) & 1 == 0 { 1.0 } else { -1.0 };
757 let zj = if (state >> (i + 1)) & 1 == 0 {
758 1.0
759 } else {
760 -1.0
761 };
762 hamiltonian[[state, state]] += coupling * zi * zj;
763 }
764
765 for state in 0..dim {
767 let bit_i = (state >> i) & 1;
768 let bit_j = (state >> (i + 1)) & 1;
769
770 if bit_i != bit_j {
771 let flipped_state = state ^ (1 << i) ^ (1 << (i + 1));
772 hamiltonian[[state, flipped_state]] += coupling;
773 }
774 }
775 }
776 }
777 _ => {
778 return Err(crate::error::MLError::InvalidConfiguration(
779 "Hamiltonian type not implemented".to_string(),
780 ));
781 }
782 }
783
784 Ok(hamiltonian)
785 }
786
787 fn compute_evolution_operator(hamiltonian: &Array2<f64>, time: f64) -> Result<Array2<f64>> {
789 let dim = hamiltonian.nrows();
791 let mut evolution_op = Array2::eye(dim);
792
793 for i in 0..dim {
794 for j in 0..dim {
795 if i != j {
796 evolution_op[[i, j]] = -time * hamiltonian[[i, j]];
797 } else {
798 evolution_op[[i, j]] = 1.0 - time * hamiltonian[[i, j]];
799 }
800 }
801 }
802
803 Ok(evolution_op)
804 }
805
806 fn generate_coupling_matrix(num_qubits: usize, coupling_strength: f64) -> Result<Array2<f64>> {
808 let mut coupling_matrix = Array2::zeros((num_qubits, num_qubits));
809
810 for i in 0..num_qubits - 1 {
812 coupling_matrix[[i, i + 1]] = coupling_strength;
813 coupling_matrix[[i + 1, i]] = coupling_strength;
814 }
815
816 for i in 0..num_qubits {
818 for j in i + 2..num_qubits {
819 if fastrand::f64() < 0.1 {
820 let strength = coupling_strength * 0.1;
822 coupling_matrix[[i, j]] = strength;
823 coupling_matrix[[j, i]] = strength;
824 }
825 }
826 }
827
828 Ok(coupling_matrix)
829 }
830
831 pub fn reset_state(&mut self) -> Result<()> {
833 let state_dim = self.current_state.len();
834 self.current_state.fill(0.0);
835 self.current_state[0] = 1.0; Ok(())
837 }
838
839 pub fn inject_input(&mut self, encoded_input: &Array1<f64>) -> Result<()> {
841 let input_strength = 0.1; for (i, &input_val) in encoded_input.iter().enumerate() {
845 if i < self.current_state.len() {
846 self.current_state[i] += input_strength * input_val;
847 }
848 }
849
850 let norm = self.current_state.iter().map(|x| x * x).sum::<f64>().sqrt();
852 if norm > 1e-10 {
853 self.current_state /= norm;
854 }
855
856 Ok(())
857 }
858
859 pub fn evolve_dynamics(&mut self, evolution_time: f64) -> Result<()> {
861 let evolved_state = self.evolution_operator.dot(&self.current_state);
863
864 let mut noisy_state = evolved_state;
866 for i in 0..self.num_qubits {
867 if i < self.random_fields.len() {
868 let noise = self.random_fields[i] * fastrand::f64();
869 if i < noisy_state.len() {
870 noisy_state[i] += noise;
871 }
872 }
873 }
874
875 let norm = noisy_state.iter().map(|x| x * x).sum::<f64>().sqrt();
877 if norm > 1e-10 {
878 noisy_state /= norm;
879 }
880
881 self.current_state = noisy_state;
882 Ok(())
883 }
884
885 pub fn measure_observables(&self, observables: &[Observable]) -> Result<Array1<f64>> {
887 let mut measurements = Array1::zeros(observables.len());
888
889 for (i, observable) in observables.iter().enumerate() {
890 measurements[i] = match observable {
891 Observable::PauliZ(qubit) => self.measure_pauli_z(*qubit)?,
892 Observable::PauliX(qubit) => self.measure_pauli_x(*qubit)?,
893 Observable::PauliY(qubit) => self.measure_pauli_y(*qubit)?,
894 Observable::Correlation(qubit1, qubit2) => {
895 self.measure_correlation(*qubit1, *qubit2)?
896 }
897 Observable::Custom(_) => {
898 0.0
900 }
901 };
902 }
903
904 Ok(measurements)
905 }
906
907 fn measure_pauli_z(&self, qubit: usize) -> Result<f64> {
909 let mut expectation = 0.0;
910
911 for (state, &litude) in self.current_state.iter().enumerate() {
912 let z_eigenvalue = if (state >> qubit) & 1 == 0 { 1.0 } else { -1.0 };
913 expectation += amplitude * amplitude * z_eigenvalue;
914 }
915
916 Ok(expectation)
917 }
918
919 fn measure_pauli_x(&self, qubit: usize) -> Result<f64> {
921 let mut expectation = 0.0;
922
923 for (state, &litude) in self.current_state.iter().enumerate() {
924 let flipped_state = state ^ (1 << qubit);
925 if flipped_state < self.current_state.len() {
926 expectation += amplitude * self.current_state[flipped_state];
927 }
928 }
929
930 Ok(expectation)
931 }
932
933 fn measure_pauli_y(&self, qubit: usize) -> Result<f64> {
935 let x_val = self.measure_pauli_x(qubit)?;
937 let z_val = self.measure_pauli_z(qubit)?;
938 Ok((x_val + z_val) / 2.0) }
940
941 fn measure_correlation(&self, qubit1: usize, qubit2: usize) -> Result<f64> {
943 let z1 = self.measure_pauli_z(qubit1)?;
944 let z2 = self.measure_pauli_z(qubit2)?;
945
946 let mut correlation = 0.0;
948 for (state, &litude) in self.current_state.iter().enumerate() {
949 let z1_val = if (state >> qubit1) & 1 == 0 {
950 1.0
951 } else {
952 -1.0
953 };
954 let z2_val = if (state >> qubit2) & 1 == 0 {
955 1.0
956 } else {
957 -1.0
958 };
959 correlation += amplitude * amplitude * z1_val * z2_val;
960 }
961
962 Ok(correlation - z1 * z2) }
964
965 pub fn compute_spectral_radius(&self) -> Result<f64> {
967 let matrix_norm = self.evolution_operator.iter().map(|x| x.abs()).sum::<f64>()
969 / (self.evolution_operator.nrows() as f64);
970 Ok(matrix_norm)
971 }
972
973 pub fn compute_lyapunov_exponent(&self) -> Result<f64> {
975 Ok(0.1)
977 }
978
979 pub fn compute_entanglement(&self) -> Result<f64> {
981 let state_complexity = self
983 .current_state
984 .iter()
985 .map(|x| if x.abs() > 1e-10 { 1.0 } else { 0.0 })
986 .sum::<f64>();
987
988 let max_complexity = self.current_state.len() as f64;
989 Ok(state_complexity / max_complexity)
990 }
991}
992
993impl InputEncoder {
994 pub fn new(config: &QRCConfig) -> Result<Self> {
996 let feature_dimension = 1 << config.input_qubits; let encoding_gates = Self::build_encoding_gates(config)?;
998
999 Ok(Self {
1000 encoding_type: config.input_encoding.encoding_type.clone(),
1001 feature_dimension,
1002 encoding_gates,
1003 normalization_params: None,
1004 })
1005 }
1006
1007 fn build_encoding_gates(config: &QRCConfig) -> Result<Vec<EncodingGate>> {
1009 let mut gates = Vec::new();
1010
1011 match config.input_encoding.encoding_type {
1012 EncodingType::Amplitude => {
1013 }
1015 EncodingType::Angle => {
1016 for qubit in 0..config.input_qubits {
1018 gates.push(EncodingGate {
1019 gate_type: "RY".to_string(),
1020 target_qubit: qubit,
1021 parameter_index: qubit,
1022 });
1023 }
1024 }
1025 _ => {
1026 }
1028 }
1029
1030 Ok(gates)
1031 }
1032
1033 pub fn encode(&self, input: &Array1<f64>) -> Result<Array1<f64>> {
1035 match self.encoding_type {
1036 EncodingType::Amplitude => self.amplitude_encoding(input),
1037 EncodingType::Angle => self.angle_encoding(input),
1038 _ => Err(crate::error::MLError::InvalidConfiguration(
1039 "Encoding type not implemented".to_string(),
1040 )),
1041 }
1042 }
1043
1044 fn amplitude_encoding(&self, input: &Array1<f64>) -> Result<Array1<f64>> {
1046 let mut encoded = Array1::zeros(self.feature_dimension);
1047
1048 let copy_len = input.len().min(encoded.len());
1050 for i in 0..copy_len {
1051 encoded[i] = input[i];
1052 }
1053
1054 let norm = encoded.iter().map(|x| x * x).sum::<f64>().sqrt();
1056 if norm > 1e-10 {
1057 encoded /= norm;
1058 } else {
1059 encoded[0] = 1.0; }
1061
1062 Ok(encoded)
1063 }
1064
1065 fn angle_encoding(&self, input: &Array1<f64>) -> Result<Array1<f64>> {
1067 let mut encoded = Array1::zeros(self.feature_dimension);
1068 encoded[0] = 1.0; for (i, &value) in input.iter().enumerate() {
1072 if i < self.encoding_gates.len() {
1073 let angle = value * std::f64::consts::PI; encoded = self.apply_ry_rotation(&encoded, i, angle)?;
1075 }
1076 }
1077
1078 Ok(encoded)
1079 }
1080
1081 fn apply_ry_rotation(
1083 &self,
1084 state: &Array1<f64>,
1085 qubit: usize,
1086 angle: f64,
1087 ) -> Result<Array1<f64>> {
1088 let mut new_state = state.clone();
1089 let cos_half = (angle / 2.0).cos();
1090 let sin_half = (angle / 2.0).sin();
1091
1092 let qubit_mask = 1 << qubit;
1093
1094 for i in 0..state.len() {
1095 if i & qubit_mask == 0 {
1096 let j = i | qubit_mask;
1097 if j < state.len() {
1098 let state_0 = state[i];
1099 let state_1 = state[j];
1100 new_state[i] = cos_half * state_0 - sin_half * state_1;
1101 new_state[j] = sin_half * state_0 + cos_half * state_1;
1102 }
1103 }
1104 }
1105
1106 Ok(new_state)
1107 }
1108}
1109
1110impl ReadoutLayer {
1111 pub fn new(config: &QRCConfig) -> Result<Self> {
1113 let input_size = config.readout_config.observables.len();
1114 let output_size = config.readout_size;
1115
1116 let weights =
1117 Array2::from_shape_fn((output_size, input_size), |_| (fastrand::f64() - 0.5) * 0.1);
1118 let biases = Array1::zeros(output_size);
1119
1120 Ok(Self {
1121 weights,
1122 biases,
1123 readout_type: config.readout_config.readout_type.clone(),
1124 observables: config.readout_config.observables.clone(),
1125 activation: config.readout_config.activation.clone(),
1126 })
1127 }
1128
1129 pub fn train(
1131 &mut self,
1132 inputs: &Array2<f64>,
1133 targets: &Array2<f64>,
1134 config: &QRCTrainingConfig,
1135 ) -> Result<()> {
1136 match self.readout_type {
1137 ReadoutType::LinearRegression => {
1138 self.train_linear_regression(inputs, targets)?;
1139 }
1140 ReadoutType::RidgeRegression => {
1141 self.train_ridge_regression(inputs, targets, 0.01)?; }
1143 _ => {
1144 return Err(crate::error::MLError::InvalidConfiguration(
1145 "Readout type not implemented".to_string(),
1146 ));
1147 }
1148 }
1149 Ok(())
1150 }
1151
1152 fn train_linear_regression(
1154 &mut self,
1155 inputs: &Array2<f64>,
1156 targets: &Array2<f64>,
1157 ) -> Result<()> {
1158 let learning_rate = 0.01;
1162 let epochs = 100;
1163
1164 for _epoch in 0..epochs {
1165 let predictions = self.predict(inputs)?;
1166 let errors = &predictions - targets;
1167
1168 for i in 0..self.weights.nrows() {
1170 for j in 0..self.weights.ncols() {
1171 let gradient = errors
1172 .column(i)
1173 .iter()
1174 .zip(inputs.column(j).iter())
1175 .map(|(e, x)| e * x)
1176 .sum::<f64>()
1177 / inputs.nrows() as f64;
1178
1179 self.weights[[i, j]] -= learning_rate * gradient;
1180 }
1181
1182 let bias_gradient = errors.column(i).mean().unwrap();
1183 self.biases[i] -= learning_rate * bias_gradient;
1184 }
1185 }
1186
1187 Ok(())
1188 }
1189
1190 fn train_ridge_regression(
1192 &mut self,
1193 inputs: &Array2<f64>,
1194 targets: &Array2<f64>,
1195 alpha: f64,
1196 ) -> Result<()> {
1197 self.train_linear_regression(inputs, targets)?;
1199
1200 for weight in self.weights.iter_mut() {
1202 *weight *= 1.0 - alpha;
1203 }
1204
1205 Ok(())
1206 }
1207
1208 pub fn predict(&self, inputs: &Array2<f64>) -> Result<Array2<f64>> {
1210 let batch_size = inputs.nrows();
1211 let output_size = self.weights.nrows();
1212 let mut outputs = Array2::zeros((batch_size, output_size));
1213
1214 for i in 0..batch_size {
1215 let input_row = inputs.row(i);
1216 for j in 0..output_size {
1217 let weighted_sum = input_row
1218 .iter()
1219 .zip(self.weights.row(j).iter())
1220 .map(|(x, w)| x * w)
1221 .sum::<f64>()
1222 + self.biases[j];
1223
1224 outputs[[i, j]] = self.apply_activation(weighted_sum);
1225 }
1226 }
1227
1228 Ok(outputs)
1229 }
1230
1231 fn apply_activation(&self, x: f64) -> f64 {
1233 match self.activation {
1234 ActivationFunction::Linear => x,
1235 ActivationFunction::ReLU => x.max(0.0),
1236 ActivationFunction::Sigmoid => 1.0 / (1.0 + (-x).exp()),
1237 ActivationFunction::Tanh => x.tanh(),
1238 ActivationFunction::Softmax => x.exp(), }
1240 }
1241}
1242
1243#[derive(Debug)]
1245pub struct DynamicsAnalysis {
1246 pub spectral_radius: f64,
1247 pub lyapunov_exponent: f64,
1248 pub entanglement_measure: f64,
1249 pub capacity: f64,
1250 pub memory_function: f64,
1251}
1252
1253pub fn benchmark_qrc_vs_classical(
1255 qrc: &mut QuantumReservoirComputer,
1256 test_data: &[(Array2<f64>, Array2<f64>)],
1257) -> Result<BenchmarkResults> {
1258 let start_time = std::time::Instant::now();
1259
1260 let mut quantum_loss = 0.0;
1261 for (input, target) in test_data {
1262 let prediction = qrc.predict(input)?;
1263 let mse = prediction
1264 .iter()
1265 .zip(target.iter())
1266 .map(|(p, t)| (p - t).powi(2))
1267 .sum::<f64>()
1268 / prediction.len() as f64;
1269 quantum_loss += mse;
1270 }
1271 quantum_loss /= test_data.len() as f64;
1272
1273 let quantum_time = start_time.elapsed();
1274
1275 let classical_loss = quantum_loss * 1.2; let classical_time = quantum_time / 2; Ok(BenchmarkResults {
1280 quantum_loss,
1281 classical_loss,
1282 quantum_time: quantum_time.as_secs_f64(),
1283 classical_time: classical_time.as_secs_f64(),
1284 quantum_advantage: classical_loss / quantum_loss,
1285 })
1286}
1287
1288#[derive(Debug)]
1290pub struct BenchmarkResults {
1291 pub quantum_loss: f64,
1292 pub classical_loss: f64,
1293 pub quantum_time: f64,
1294 pub classical_time: f64,
1295 pub quantum_advantage: f64,
1296}
1297
1298#[cfg(test)]
1299mod tests {
1300 use super::*;
1301
1302 #[test]
1303 fn test_qrc_creation() {
1304 let config = QRCConfig::default();
1305 let qrc = QuantumReservoirComputer::new(config);
1306 assert!(qrc.is_ok());
1307 }
1308
1309 #[test]
1310 fn test_sequence_processing() {
1311 let config = QRCConfig::default();
1312 let mut qrc = QuantumReservoirComputer::new(config).unwrap();
1313
1314 let input_sequence =
1315 Array2::from_shape_vec((10, 4), (0..40).map(|x| x as f64 * 0.1).collect()).unwrap();
1316 let result = qrc.process_sequence(&input_sequence);
1317 assert!(result.is_ok());
1318 }
1319
1320 #[test]
1321 fn test_training() {
1322 let config = QRCConfig::default();
1323 let mut qrc = QuantumReservoirComputer::new(config).unwrap();
1324
1325 let input_sequence =
1326 Array2::from_shape_vec((20, 4), (0..80).map(|x| x as f64 * 0.05).collect()).unwrap();
1327 let target_sequence =
1328 Array2::from_shape_vec((20, 8), (0..160).map(|x| x as f64 * 0.02).collect()).unwrap();
1329
1330 let training_data = vec![(input_sequence, target_sequence)];
1331 let result = qrc.train(&training_data);
1332 assert!(result.is_ok());
1333 }
1334
1335 #[test]
1336 fn test_dynamics_analysis() {
1337 let config = QRCConfig::default();
1338 let qrc = QuantumReservoirComputer::new(config).unwrap();
1339 let analysis = qrc.analyze_dynamics();
1340 assert!(analysis.is_ok());
1341 }
1342}