1use crate::{
28 error::{QuantRS2Error, QuantRS2Result},
29 gate::GateOp,
30 qubit::QubitId,
31};
32use scirs2_core::ndarray::{Array1, Array2, Array3, Axis};
33use scirs2_core::random::prelude::*;
34use scirs2_core::Complex64;
35use std::f64::consts::PI;
36
37#[derive(Debug, Clone)]
39pub struct QuantumReservoirConfig {
40 pub num_qubits: usize,
42 pub depth: usize,
44 pub spectral_radius: f64,
46 pub input_scaling: f64,
48 pub leak_rate: f64,
50 pub use_entanglement: bool,
52 pub seed: Option<u64>,
54}
55
56impl Default for QuantumReservoirConfig {
57 fn default() -> Self {
58 Self {
59 num_qubits: 8,
60 depth: 10,
61 spectral_radius: 0.9,
62 input_scaling: 1.0,
63 leak_rate: 0.3,
64 use_entanglement: true,
65 seed: None,
66 }
67 }
68}
69
70#[derive(Debug, Clone)]
72pub struct QuantumReservoir {
73 config: QuantumReservoirConfig,
75 reservoir_gates: Vec<Vec<ReservoirGate>>,
77 input_params: Array2<f64>,
79 state: Option<Array1<Complex64>>,
81}
82
83#[derive(Debug, Clone)]
85struct ReservoirGate {
86 gate_type: GateType,
88 qubits: Vec<usize>,
90 params: Vec<f64>,
92}
93
94#[derive(Debug, Clone, Copy)]
95enum GateType {
96 RotationX,
97 RotationY,
98 RotationZ,
99 CNOT,
100 CZ,
101 SWAP,
102}
103
104impl QuantumReservoir {
105 pub fn new(config: QuantumReservoirConfig) -> QuantRS2Result<Self> {
107 if config.num_qubits < 2 {
108 return Err(QuantRS2Error::InvalidInput(
109 "Quantum reservoir requires at least 2 qubits".to_string(),
110 ));
111 }
112
113 let mut rng = if let Some(seed) = config.seed {
114 thread_rng() } else {
116 thread_rng()
117 };
118
119 let input_params = Array2::from_shape_fn((config.num_qubits, config.num_qubits), |_| {
121 rng.gen_range(-PI..PI) * config.input_scaling
122 });
123
124 let reservoir_gates = Self::generate_reservoir_gates(
126 config.num_qubits,
127 config.depth,
128 config.use_entanglement,
129 &mut rng,
130 );
131
132 Ok(Self {
133 config,
134 reservoir_gates,
135 input_params,
136 state: None,
137 })
138 }
139
140 fn generate_reservoir_gates(
142 num_qubits: usize,
143 depth: usize,
144 use_entanglement: bool,
145 rng: &mut impl Rng,
146 ) -> Vec<Vec<ReservoirGate>> {
147 let mut layers = Vec::with_capacity(depth);
148
149 for _ in 0..depth {
150 let mut layer = Vec::new();
151
152 for q in 0..num_qubits {
154 let gate_type = match rng.gen_range(0..3) {
155 0 => GateType::RotationX,
156 1 => GateType::RotationY,
157 _ => GateType::RotationZ,
158 };
159
160 layer.push(ReservoirGate {
161 gate_type,
162 qubits: vec![q],
163 params: vec![rng.gen_range(-PI..PI)],
164 });
165 }
166
167 if use_entanglement {
169 for q in 0..num_qubits - 1 {
170 let gate_type = match rng.gen_range(0..3) {
171 0 => GateType::CNOT,
172 1 => GateType::CZ,
173 _ => GateType::SWAP,
174 };
175
176 layer.push(ReservoirGate {
177 gate_type,
178 qubits: vec![q, q + 1],
179 params: vec![],
180 });
181 }
182 }
183
184 layers.push(layer);
185 }
186
187 layers
188 }
189
190 pub fn encode_input(&self, input: &Array1<f64>) -> QuantRS2Result<Array1<Complex64>> {
192 if input.len() != self.config.num_qubits {
193 return Err(QuantRS2Error::InvalidInput(format!(
194 "Input dimension {} does not match num_qubits {}",
195 input.len(),
196 self.config.num_qubits
197 )));
198 }
199
200 let dim = 1 << self.config.num_qubits;
201 let mut state = Array1::zeros(dim);
202
203 state[0] = Complex64::new(1.0, 0.0);
205
206 for i in 0..self.config.num_qubits {
208 let angle = input[i] * self.input_params[[i, i]];
209 state = self.apply_rotation_y(&state, i, angle)?;
210 }
211
212 Ok(state)
213 }
214
215 fn apply_rotation_y(
217 &self,
218 state: &Array1<Complex64>,
219 qubit: usize,
220 angle: f64,
221 ) -> QuantRS2Result<Array1<Complex64>> {
222 let dim = state.len();
223 let mut new_state = Array1::zeros(dim);
224
225 let cos_half = (angle / 2.0).cos();
226 let sin_half = (angle / 2.0).sin();
227
228 for i in 0..dim {
229 let bit = (i >> qubit) & 1;
230 if bit == 0 {
231 let j = i | (1 << qubit);
232 new_state[i] = state[i] * cos_half - state[j] * sin_half;
233 new_state[j] = state[i] * sin_half + state[j] * cos_half;
234 }
235 }
236
237 Ok(new_state)
238 }
239
240 pub fn step(&mut self, input: &Array1<f64>) -> QuantRS2Result<Array1<f64>> {
242 let input_state = self.encode_input(input)?;
244
245 let current_state = if let Some(prev_state) = &self.state {
247 let alpha = self.config.leak_rate;
248 &input_state * (1.0 - alpha) + prev_state * alpha
249 } else {
250 input_state
251 };
252
253 let mut state = current_state;
255 for layer in &self.reservoir_gates {
256 state = self.apply_gate_layer(&state, layer)?;
257 }
258
259 self.state = Some(state.clone());
261
262 self.extract_features(&state)
264 }
265
266 fn apply_gate_layer(
268 &self,
269 state: &Array1<Complex64>,
270 layer: &[ReservoirGate],
271 ) -> QuantRS2Result<Array1<Complex64>> {
272 let mut current_state = state.clone();
273
274 for gate in layer {
275 current_state = match gate.gate_type {
276 GateType::RotationX => {
277 self.apply_rotation_x(¤t_state, gate.qubits[0], gate.params[0])?
278 }
279 GateType::RotationY => {
280 self.apply_rotation_y(¤t_state, gate.qubits[0], gate.params[0])?
281 }
282 GateType::RotationZ => {
283 self.apply_rotation_z(¤t_state, gate.qubits[0], gate.params[0])?
284 }
285 GateType::CNOT => {
286 self.apply_cnot(¤t_state, gate.qubits[0], gate.qubits[1])?
287 }
288 GateType::CZ => self.apply_cz(¤t_state, gate.qubits[0], gate.qubits[1])?,
289 GateType::SWAP => {
290 self.apply_swap(¤t_state, gate.qubits[0], gate.qubits[1])?
291 }
292 };
293 }
294
295 Ok(current_state)
296 }
297
298 fn apply_rotation_x(
300 &self,
301 state: &Array1<Complex64>,
302 qubit: usize,
303 angle: f64,
304 ) -> QuantRS2Result<Array1<Complex64>> {
305 let dim = state.len();
306 let mut new_state = Array1::zeros(dim);
307
308 let cos_half = Complex64::new((angle / 2.0).cos(), 0.0);
309 let sin_half = Complex64::new(0.0, -(angle / 2.0).sin());
310
311 for i in 0..dim {
312 let bit = (i >> qubit) & 1;
313 if bit == 0 {
314 let j = i | (1 << qubit);
315 new_state[i] = state[i] * cos_half + state[j] * sin_half;
316 new_state[j] = state[i] * sin_half + state[j] * cos_half;
317 }
318 }
319
320 Ok(new_state)
321 }
322
323 fn apply_rotation_z(
325 &self,
326 state: &Array1<Complex64>,
327 qubit: usize,
328 angle: f64,
329 ) -> QuantRS2Result<Array1<Complex64>> {
330 let dim = state.len();
331 let mut new_state = state.clone();
332
333 let phase = Complex64::new((angle / 2.0).cos(), -(angle / 2.0).sin());
334
335 for i in 0..dim {
336 let bit = (i >> qubit) & 1;
337 if bit == 1 {
338 new_state[i] = new_state[i] * phase;
339 } else {
340 new_state[i] = new_state[i] * phase.conj();
341 }
342 }
343
344 Ok(new_state)
345 }
346
347 fn apply_cnot(
349 &self,
350 state: &Array1<Complex64>,
351 control: usize,
352 target: usize,
353 ) -> QuantRS2Result<Array1<Complex64>> {
354 let dim = state.len();
355 let mut new_state = state.clone();
356
357 for i in 0..dim {
358 let control_bit = (i >> control) & 1;
359 if control_bit == 1 {
360 let j = i ^ (1 << target);
361 let temp = new_state[i];
362 new_state[i] = new_state[j];
363 new_state[j] = temp;
364 }
365 }
366
367 Ok(new_state)
368 }
369
370 fn apply_cz(
372 &self,
373 state: &Array1<Complex64>,
374 qubit1: usize,
375 qubit2: usize,
376 ) -> QuantRS2Result<Array1<Complex64>> {
377 let dim = state.len();
378 let mut new_state = state.clone();
379
380 for i in 0..dim {
381 let bit1 = (i >> qubit1) & 1;
382 let bit2 = (i >> qubit2) & 1;
383 if bit1 == 1 && bit2 == 1 {
384 new_state[i] = -new_state[i];
385 }
386 }
387
388 Ok(new_state)
389 }
390
391 fn apply_swap(
393 &self,
394 state: &Array1<Complex64>,
395 qubit1: usize,
396 qubit2: usize,
397 ) -> QuantRS2Result<Array1<Complex64>> {
398 let dim = state.len();
399 let mut new_state = state.clone();
400
401 for i in 0..dim {
402 let bit1 = (i >> qubit1) & 1;
403 let bit2 = (i >> qubit2) & 1;
404
405 if bit1 != bit2 {
406 let j = i ^ ((1 << qubit1) | (1 << qubit2));
407 if i < j {
408 let temp = new_state[i];
409 new_state[i] = new_state[j];
410 new_state[j] = temp;
411 }
412 }
413 }
414
415 Ok(new_state)
416 }
417
418 fn extract_features(&self, state: &Array1<Complex64>) -> QuantRS2Result<Array1<f64>> {
420 let num_features = self.config.num_qubits * 3; let mut features = Array1::zeros(num_features);
422
423 for q in 0..self.config.num_qubits {
425 features[q * 3] = self.pauli_x_expectation(state, q)?;
426 features[q * 3 + 1] = self.pauli_y_expectation(state, q)?;
427 features[q * 3 + 2] = self.pauli_z_expectation(state, q)?;
428 }
429
430 Ok(features)
431 }
432
433 fn pauli_x_expectation(&self, state: &Array1<Complex64>, qubit: usize) -> QuantRS2Result<f64> {
435 let dim = state.len();
436 let mut expectation = 0.0;
437
438 for i in 0..dim {
439 let j = i ^ (1 << qubit);
440 let contrib = (state[i].conj() * state[j]).re;
441 expectation += contrib;
442 }
443
444 Ok(expectation * 2.0)
445 }
446
447 fn pauli_y_expectation(&self, state: &Array1<Complex64>, qubit: usize) -> QuantRS2Result<f64> {
449 let dim = state.len();
450 let mut expectation = 0.0;
451
452 for i in 0..dim {
453 let bit = (i >> qubit) & 1;
454 let j = i ^ (1 << qubit);
455
456 let contrib = if bit == 0 {
457 (state[i].conj() * state[j] * Complex64::new(0.0, -1.0)).re
458 } else {
459 (state[i].conj() * state[j] * Complex64::new(0.0, 1.0)).re
460 };
461
462 expectation += contrib;
463 }
464
465 Ok(expectation * 2.0)
466 }
467
468 fn pauli_z_expectation(&self, state: &Array1<Complex64>, qubit: usize) -> QuantRS2Result<f64> {
470 let dim = state.len();
471 let mut expectation = 0.0;
472
473 for i in 0..dim {
474 let bit = (i >> qubit) & 1;
475 let sign = if bit == 0 { 1.0 } else { -1.0 };
476 expectation += sign * state[i].norm_sqr();
477 }
478
479 Ok(expectation)
480 }
481
482 pub fn reset(&mut self) {
484 self.state = None;
485 }
486
487 pub const fn config(&self) -> &QuantumReservoirConfig {
489 &self.config
490 }
491}
492
493#[derive(Debug, Clone)]
495pub struct QuantumReadout {
496 input_dim: usize,
498 output_dim: usize,
500 weights: Array2<f64>,
502 bias: Array1<f64>,
504}
505
506impl QuantumReadout {
507 pub fn new(input_dim: usize, output_dim: usize) -> Self {
509 let mut rng = thread_rng();
510 let scale = (2.0 / input_dim as f64).sqrt();
511
512 let weights =
513 Array2::from_shape_fn((output_dim, input_dim), |_| rng.gen_range(-scale..scale));
514
515 let bias = Array1::zeros(output_dim);
516
517 Self {
518 input_dim,
519 output_dim,
520 weights,
521 bias,
522 }
523 }
524
525 pub fn forward(&self, features: &Array1<f64>) -> QuantRS2Result<Array1<f64>> {
527 if features.len() != self.input_dim {
528 return Err(QuantRS2Error::InvalidInput(format!(
529 "Input dimension {} does not match expected {}",
530 features.len(),
531 self.input_dim
532 )));
533 }
534
535 let mut output = self.bias.clone();
536 for i in 0..self.output_dim {
537 for j in 0..self.input_dim {
538 output[i] += self.weights[[i, j]] * features[j];
539 }
540 }
541
542 Ok(output)
543 }
544
545 pub fn train(
547 &mut self,
548 features: &Array2<f64>,
549 targets: &Array2<f64>,
550 reg_param: f64,
551 ) -> QuantRS2Result<()> {
552 let n_samples = features.shape()[0];
553 let n_features = features.shape()[1];
554 let n_outputs = targets.shape()[1];
555
556 if n_features != self.input_dim {
557 return Err(QuantRS2Error::InvalidInput(
558 "Feature dimension mismatch".to_string(),
559 ));
560 }
561
562 if n_outputs != self.output_dim {
563 return Err(QuantRS2Error::InvalidInput(
564 "Output dimension mismatch".to_string(),
565 ));
566 }
567
568 let mut xtx = Array2::zeros((n_features, n_features));
572 for i in 0..n_features {
573 for j in 0..n_features {
574 let mut sum = 0.0;
575 for k in 0..n_samples {
576 sum += features[[k, i]] * features[[k, j]];
577 }
578 xtx[[i, j]] = sum;
579 if i == j {
580 xtx[[i, j]] += reg_param; }
582 }
583 }
584
585 let mut xty = Array2::zeros((n_features, n_outputs));
587 for i in 0..n_features {
588 for j in 0..n_outputs {
589 let mut sum = 0.0;
590 for k in 0..n_samples {
591 sum += features[[k, i]] * targets[[k, j]];
592 }
593 xty[[i, j]] = sum;
594 }
595 }
596
597 self.weights = xty.t().to_owned();
600
601 Ok(())
602 }
603}
604
605#[derive(Debug, Clone)]
607pub struct QuantumReservoirComputer {
608 reservoir: QuantumReservoir,
610 readout: QuantumReadout,
612}
613
614impl QuantumReservoirComputer {
615 pub fn new(
617 reservoir_config: QuantumReservoirConfig,
618 output_dim: usize,
619 ) -> QuantRS2Result<Self> {
620 let num_features = reservoir_config.num_qubits * 3;
621 let reservoir = QuantumReservoir::new(reservoir_config)?;
622 let readout = QuantumReadout::new(num_features, output_dim);
623
624 Ok(Self { reservoir, readout })
625 }
626
627 pub fn process_sequence(&mut self, inputs: &Array2<f64>) -> QuantRS2Result<Array2<f64>> {
629 let seq_len = inputs.shape()[0];
630 let output_dim = self.readout.output_dim;
631
632 self.reservoir.reset();
633
634 let mut outputs = Array2::zeros((seq_len, output_dim));
635
636 for t in 0..seq_len {
637 let input = inputs.row(t).to_owned();
638 let features = self.reservoir.step(&input)?;
639 let output = self.readout.forward(&features)?;
640 outputs.row_mut(t).assign(&output);
641 }
642
643 Ok(outputs)
644 }
645
646 pub fn train(
648 &mut self,
649 input_sequences: &[Array2<f64>],
650 target_sequences: &[Array2<f64>],
651 reg_param: f64,
652 ) -> QuantRS2Result<()> {
653 let mut all_features = Vec::new();
655 let mut all_targets = Vec::new();
656
657 for (inputs, targets) in input_sequences.iter().zip(target_sequences.iter()) {
658 self.reservoir.reset();
659 let seq_len = inputs.shape()[0];
660
661 for t in 0..seq_len {
662 let input = inputs.row(t).to_owned();
663 let features = self.reservoir.step(&input)?;
664 all_features.push(features);
665 all_targets.push(targets.row(t).to_owned());
666 }
667 }
668
669 let n_samples = all_features.len();
671 let n_features = all_features[0].len();
672 let n_outputs = all_targets[0].len();
673
674 let mut feature_matrix = Array2::zeros((n_samples, n_features));
675 let mut target_matrix = Array2::zeros((n_samples, n_outputs));
676
677 for (i, (feat, targ)) in all_features.iter().zip(all_targets.iter()).enumerate() {
678 feature_matrix.row_mut(i).assign(feat);
679 target_matrix.row_mut(i).assign(targ);
680 }
681
682 self.readout
684 .train(&feature_matrix, &target_matrix, reg_param)?;
685
686 Ok(())
687 }
688}
689
690#[cfg(test)]
691mod tests {
692 use super::*;
693
694 #[test]
695 fn test_quantum_reservoir() {
696 let config = QuantumReservoirConfig::default();
697 let mut reservoir =
698 QuantumReservoir::new(config).expect("Failed to create quantum reservoir");
699
700 let input = Array1::from_vec(vec![0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]);
701 let features = reservoir
702 .step(&input)
703 .expect("Failed to step quantum reservoir");
704
705 assert_eq!(features.len(), 8 * 3); }
707
708 #[test]
709 fn test_quantum_reservoir_computer() {
710 let config = QuantumReservoirConfig {
711 num_qubits: 4,
712 depth: 5,
713 spectral_radius: 0.9,
714 input_scaling: 1.0,
715 leak_rate: 0.3,
716 use_entanglement: true,
717 seed: Some(42),
718 };
719
720 let mut qrc = QuantumReservoirComputer::new(config, 2)
721 .expect("Failed to create quantum reservoir computer");
722
723 let inputs = Array2::from_shape_fn((10, 4), |(i, j)| (i + j) as f64 * 0.1);
725 let outputs = qrc
726 .process_sequence(&inputs)
727 .expect("Failed to process sequence");
728
729 assert_eq!(outputs.shape(), &[10, 2]);
730 }
731}