1#![allow(dead_code)]
7
8#[cfg(feature = "dwave")]
9use crate::compile::CompiledModel;
10use scirs2_core::ndarray::{Array1, Array2, Array3};
11use scirs2_core::random::prelude::*;
12use scirs2_core::random::{Distribution, RandNormal};
13use std::f64::consts::PI;
14
15type Normal<T> = RandNormal<T>;
16
17pub struct QuantumBoltzmannMachine {
19 n_visible: usize,
21 n_hidden: usize,
23 transverse_field: f64,
25 temperature: f64,
27 learning_rate: f64,
29 weights: Array2<f64>,
31 visible_bias: Array1<f64>,
33 hidden_bias: Array1<f64>,
35 use_quantum_sampling: bool,
37}
38
39impl QuantumBoltzmannMachine {
40 pub fn new(n_visible: usize, n_hidden: usize) -> Self {
42 use scirs2_core::random::prelude::*;
43 let mut rng = StdRng::seed_from_u64(42);
44
45 let weights = Array2::from_shape_fn((n_visible, n_hidden), |_| rng.gen_range(-0.1..0.1));
47 let visible_bias = Array1::from_shape_fn(n_visible, |_| rng.gen_range(-0.1..0.1));
48 let hidden_bias = Array1::from_shape_fn(n_hidden, |_| rng.gen_range(-0.1..0.1));
49
50 Self {
51 n_visible,
52 n_hidden,
53 transverse_field: 1.0,
54 temperature: 1.0,
55 learning_rate: 0.01,
56 weights,
57 visible_bias,
58 hidden_bias,
59 use_quantum_sampling: true,
60 }
61 }
62
63 pub const fn with_transverse_field(mut self, field: f64) -> Self {
65 self.transverse_field = field;
66 self
67 }
68
69 pub const fn with_temperature(mut self, temp: f64) -> Self {
71 self.temperature = temp;
72 self
73 }
74
75 pub const fn with_learning_rate(mut self, rate: f64) -> Self {
77 self.learning_rate = rate;
78 self
79 }
80
81 pub fn train(&mut self, data: &Array2<bool>, epochs: usize) -> Result<TrainingResult, String> {
83 let mut loss_history = Vec::new();
84 let batch_size = data.shape()[0];
85
86 for epoch in 0..epochs {
87 let mut epoch_loss = 0.0;
88
89 for batch_idx in 0..batch_size {
91 let visible_view = data.row(batch_idx);
92 let visible: Array1<bool> = visible_view.to_owned();
93
94 let hidden_probs = self.hidden_given_visible(&visible);
96 let hidden_sample = self.sample_units(&hidden_probs);
97
98 let (v_neg, h_neg) = if self.use_quantum_sampling {
100 self.quantum_gibbs_sampling(&visible)?
101 } else {
102 self.classical_gibbs_sampling(&visible, 1)
103 };
104
105 self.update_parameters(&visible, &hidden_sample, &v_neg, &h_neg);
107
108 let reconstruction_error = self.compute_reconstruction_error(&visible, &v_neg);
110 epoch_loss += reconstruction_error;
111 }
112
113 loss_history.push(epoch_loss / batch_size as f64);
114
115 if epoch > 0 && loss_history[epoch] > loss_history[epoch - 1] {
117 self.learning_rate *= 0.95;
118 }
119 }
120
121 let final_loss = *loss_history
122 .last()
123 .ok_or("Training failed: epochs must be > 0")?;
124 let converged = final_loss < 0.01;
125
126 Ok(TrainingResult {
127 final_loss,
128 loss_history,
129 converged,
130 })
131 }
132
133 fn hidden_given_visible(&self, visible: &Array1<bool>) -> Array1<f64> {
135 let visible_float: Array1<f64> = visible.mapv(|v| if v { 1.0 } else { -1.0 });
136 let activation = self.hidden_bias.clone() + visible_float.dot(&self.weights);
137 activation.mapv(|a| 1.0 / (1.0 + (-a / self.temperature).exp()))
138 }
139
140 fn visible_given_hidden(&self, hidden: &Array1<bool>) -> Array1<f64> {
142 let hidden_float: Array1<f64> = hidden.mapv(|h| if h { 1.0 } else { -1.0 });
143 let activation = self.visible_bias.clone() + self.weights.dot(&hidden_float);
144 activation.mapv(|a| 1.0 / (1.0 + (-a / self.temperature).exp()))
145 }
146
147 fn sample_units(&self, probs: &Array1<f64>) -> Array1<bool> {
149 let mut rng = thread_rng();
150 probs.mapv(|p| rng.gen_bool(p))
151 }
152
153 fn classical_gibbs_sampling(
155 &self,
156 initial_visible: &Array1<bool>,
157 steps: usize,
158 ) -> (Array1<bool>, Array1<bool>) {
159 let mut visible = initial_visible.clone();
160 let mut hidden = Array1::from_elem(self.n_hidden, false);
161
162 for _ in 0..steps {
163 let hidden_probs = self.hidden_given_visible(&visible);
164 hidden = self.sample_units(&hidden_probs);
165
166 let visible_probs = self.visible_given_hidden(&hidden);
167 visible = self.sample_units(&visible_probs);
168 }
169
170 (visible, hidden)
171 }
172
173 fn quantum_gibbs_sampling(
175 &self,
176 initial_visible: &Array1<bool>,
177 ) -> Result<(Array1<bool>, Array1<bool>), String> {
178 let mut rng = thread_rng();
180 let tunneling_prob = (-2.0 / self.transverse_field).exp();
181
182 let mut visible = initial_visible.clone();
183 let mut hidden = Array1::from_elem(self.n_hidden, false);
184
185 for _ in 0..10 {
187 let hidden_probs = self.hidden_given_visible(&visible);
189 hidden = self.sample_units(&hidden_probs);
190
191 if rng.gen_bool(tunneling_prob) {
193 let flip_idx = rng.gen_range(0..self.n_visible);
195 visible[flip_idx] = !visible[flip_idx];
196 }
197
198 let visible_probs = self.visible_given_hidden(&hidden);
199 visible = self.sample_units(&visible_probs);
200
201 if rng.gen_bool(tunneling_prob) {
203 let flip_idx = rng.gen_range(0..self.n_hidden);
204 hidden[flip_idx] = !hidden[flip_idx];
205 }
206 }
207
208 Ok((visible, hidden))
209 }
210
211 fn update_parameters(
213 &mut self,
214 v_pos: &Array1<bool>,
215 h_pos: &Array1<bool>,
216 v_neg: &Array1<bool>,
217 h_neg: &Array1<bool>,
218 ) {
219 let v_pos_float: Array1<f64> = v_pos.mapv(|v| if v { 1.0 } else { -1.0 });
220 let h_pos_float: Array1<f64> = h_pos.mapv(|h| if h { 1.0 } else { -1.0 });
221 let v_neg_float: Array1<f64> = v_neg.mapv(|v| if v { 1.0 } else { -1.0 });
222 let h_neg_float: Array1<f64> = h_neg.mapv(|h| if h { 1.0 } else { -1.0 });
223
224 for i in 0..self.n_visible {
226 for j in 0..self.n_hidden {
227 let positive = v_pos_float[i] * h_pos_float[j];
228 let negative = v_neg_float[i] * h_neg_float[j];
229 self.weights[[i, j]] += self.learning_rate * (positive - negative);
230 }
231 }
232
233 self.visible_bias += &(self.learning_rate * (v_pos_float - v_neg_float));
235 self.hidden_bias += &(self.learning_rate * (h_pos_float - h_neg_float));
236 }
237
238 fn compute_reconstruction_error(
240 &self,
241 original: &Array1<bool>,
242 reconstructed: &Array1<bool>,
243 ) -> f64 {
244 original
245 .iter()
246 .zip(reconstructed.iter())
247 .filter(|(&o, &r)| o != r)
248 .count() as f64
249 / original.len() as f64
250 }
251
252 pub fn generate_samples(&self, num_samples: usize) -> Vec<Array1<bool>> {
254 let mut samples = Vec::new();
255 let mut rng = thread_rng();
256
257 for _ in 0..num_samples {
258 let initial_visible = Array1::from_shape_fn(self.n_visible, |_| rng.gen_bool(0.5));
260
261 let (sample, _) = self.classical_gibbs_sampling(&initial_visible, 100);
262 samples.push(sample);
263 }
264
265 samples
266 }
267}
268
269#[derive(Debug, Clone)]
270pub struct TrainingResult {
271 pub final_loss: f64,
272 pub loss_history: Vec<f64>,
273 pub converged: bool,
274}
275
276pub struct QuantumVAE {
278 input_dim: usize,
280 latent_dim: usize,
282 n_layers: usize,
284 encoder_params: Array2<f64>,
286 decoder_params: Array2<f64>,
288 use_quantum_encoder: bool,
290 noise_strength: f64,
292}
293
294impl QuantumVAE {
295 pub fn new(input_dim: usize, latent_dim: usize, n_layers: usize) -> Self {
297 use scirs2_core::random::prelude::*;
298 let mut rng = thread_rng();
299
300 let encoder_params =
301 Array2::from_shape_fn((n_layers, input_dim), |_| rng.gen_range(-0.1..0.1));
302
303 let decoder_params =
304 Array2::from_shape_fn((n_layers, latent_dim), |_| rng.gen_range(-0.1..0.1));
305
306 Self {
307 input_dim,
308 latent_dim,
309 n_layers,
310 encoder_params,
311 decoder_params,
312 use_quantum_encoder: true,
313 noise_strength: 0.01,
314 }
315 }
316
317 pub fn encode(&self, input: &Array1<bool>) -> (Array1<f64>, Array1<f64>) {
319 if self.use_quantum_encoder {
320 self.quantum_encode(input)
321 } else {
322 self.classical_encode(input)
323 }
324 }
325
326 fn quantum_encode(&self, input: &Array1<bool>) -> (Array1<f64>, Array1<f64>) {
328 let input_float: Array1<f64> = input.mapv(|x| if x { 1.0 } else { 0.0 });
329 let mut state = input_float;
330
331 for layer in 0..self.n_layers {
333 for i in 0..self.input_dim {
335 let angle = self.encoder_params[[layer, i]];
336 state[i] = state[i].mul_add(angle.cos(), (1.0 - state[i]) * angle.sin());
337 }
338
339 for i in 0..self.input_dim - 1 {
341 let temp = state[i];
342 state[i] = state[i].mul_add(0.9, state[i + 1] * 0.1);
343 state[i + 1] = state[i + 1].mul_add(0.9, temp * 0.1);
344 }
345 }
346
347 let mean = state
349 .slice(scirs2_core::ndarray::s![..self.latent_dim])
350 .to_owned();
351 let log_var = state
352 .slice(scirs2_core::ndarray::s![
353 self.latent_dim..2 * self.latent_dim.min(self.input_dim)
354 ])
355 .to_owned();
356
357 (mean, log_var)
358 }
359
360 fn classical_encode(&self, input: &Array1<bool>) -> (Array1<f64>, Array1<f64>) {
362 let input_float: Array1<f64> = input.mapv(|x| if x { 1.0 } else { 0.0 });
363
364 let encoded = self.encoder_params.dot(&input_float);
366
367 let mean = Array1::from_vec(encoded.iter().take(self.latent_dim).copied().collect());
368 let log_var = Array1::from_vec(
369 encoded
370 .iter()
371 .skip(self.latent_dim)
372 .take(self.latent_dim)
373 .copied()
374 .collect(),
375 );
376
377 (mean, log_var)
378 }
379
380 pub fn decode(&self, latent: &Array1<f64>) -> Array1<f64> {
382 let mut output = latent.clone();
383
384 for layer in 0..self.n_layers {
386 let mut new_output = Array1::zeros(self.input_dim);
387
388 for i in 0..self.latent_dim.min(output.len()) {
389 for j in 0..self.input_dim {
390 new_output[j] += output[i] * self.decoder_params[[layer, i]].sin();
391 }
392 }
393
394 output = new_output.mapv(|x: f64| 1.0 / (1.0 + (-x).exp()));
395 }
396
397 output
398 }
399
400 fn reparameterize(&self, mean: &Array1<f64>, log_var: &Array1<f64>) -> Array1<f64> {
402 use scirs2_core::random::prelude::*;
403 let mut rng = thread_rng();
404 let std = log_var.mapv(|x| (x / 2.0).exp());
405 let eps = Array1::from_shape_fn(mean.len(), |_| rng.gen_range(-1.0..1.0));
406
407 mean + eps * std
408 }
409
410 pub fn generate(&self, num_samples: usize) -> Vec<Array1<bool>> {
412 use scirs2_core::random::prelude::*;
413 let mut rng = thread_rng();
414 let mut samples = Vec::new();
415
416 for _ in 0..num_samples {
417 let z = Array1::from_shape_fn(self.latent_dim, |_| rng.gen_range(-1.0..1.0));
419
420 let decoded = self.decode(&z);
422
423 let binary = decoded.mapv(|x| x > 0.5);
425 samples.push(binary);
426 }
427
428 samples
429 }
430}
431
432pub struct QuantumGAN {
434 generator: QuantumGenerator,
436 discriminator: QuantumDiscriminator,
438 config: QGANConfig,
440}
441
442#[derive(Clone)]
443pub struct QuantumGenerator {
444 latent_dim: usize,
446 output_dim: usize,
448 depth: usize,
450 params: Array2<f64>,
452}
453
454#[derive(Clone)]
455pub struct QuantumDiscriminator {
456 input_dim: usize,
458 depth: usize,
460 params: Array2<f64>,
462}
463
464#[derive(Debug, Clone)]
465pub struct QGANConfig {
466 gen_lr: f64,
468 disc_lr: f64,
470 disc_steps: usize,
472 gradient_penalty: f64,
474 use_wasserstein: bool,
476}
477
478impl QuantumGAN {
479 pub fn new(latent_dim: usize, output_dim: usize, depth: usize) -> Self {
481 let generator = QuantumGenerator::new(latent_dim, output_dim, depth);
482 let discriminator = QuantumDiscriminator::new(output_dim, depth);
483
484 let config = QGANConfig {
485 gen_lr: 0.0002,
486 disc_lr: 0.0002,
487 disc_steps: 5,
488 gradient_penalty: 10.0,
489 use_wasserstein: true,
490 };
491
492 Self {
493 generator,
494 discriminator,
495 config,
496 }
497 }
498
499 pub fn train(
501 &mut self,
502 real_data: &[Array1<bool>],
503 epochs: usize,
504 ) -> Result<QGANTrainingResult, String> {
505 let mut gen_losses = Vec::new();
506 let mut disc_losses = Vec::new();
507 let mut rng = thread_rng();
508
509 for _epoch in 0..epochs {
510 let mut epoch_gen_loss = 0.0;
511 let mut epoch_disc_loss = 0.0;
512
513 for _ in 0..self.config.disc_steps {
515 let real_idx = rng.gen_range(0..real_data.len());
517 let real_sample = &real_data[real_idx];
518
519 let fake_sample = self.generator.generate(&mut rng);
521
522 let disc_loss = self.discriminator.train_step(
524 real_sample,
525 &fake_sample,
526 self.config.disc_lr,
527 self.config.use_wasserstein,
528 )?;
529
530 epoch_disc_loss += disc_loss;
531 }
532
533 let gen_loss = self.train_generator_step(&mut rng)?;
535 epoch_gen_loss += gen_loss;
536
537 gen_losses.push(epoch_gen_loss);
538 disc_losses.push(epoch_disc_loss / self.config.disc_steps as f64);
539 }
540
541 let final_gen_loss = *gen_losses
542 .last()
543 .ok_or("Training failed: epochs must be > 0")?;
544 let final_disc_loss = *disc_losses
545 .last()
546 .ok_or("Training failed: epochs must be > 0")?;
547
548 Ok(QGANTrainingResult {
549 generator_losses: gen_losses,
550 discriminator_losses: disc_losses,
551 final_gen_loss,
552 final_disc_loss,
553 })
554 }
555
556 fn train_generator_step<R: Rng>(&mut self, rng: &mut R) -> Result<f64, String> {
558 let fake_sample = self.generator.generate(rng);
560
561 let disc_score = self.discriminator.forward(&fake_sample)?;
563
564 let loss = if self.config.use_wasserstein {
566 -disc_score } else {
568 -(disc_score + 1e-8).ln() };
570
571 self.generator.update_parameters(loss, self.config.gen_lr);
573
574 Ok(loss)
575 }
576
577 pub fn generate_optimized(&self, num_samples: usize) -> Vec<Array1<bool>> {
579 let mut rng = thread_rng();
580 let mut samples = Vec::new();
581
582 for _ in 0..num_samples {
583 let sample = self.generator.generate(&mut rng);
584 samples.push(sample);
585 }
586
587 samples
588 }
589}
590
591impl QuantumGenerator {
592 fn new(latent_dim: usize, output_dim: usize, depth: usize) -> Self {
593 let mut rng = thread_rng();
594
595 let params = Array2::from_shape_fn(
596 (depth, latent_dim + output_dim),
597 |_| rng.gen::<f64>() * PI / 2.0 - PI / 4.0, );
599
600 Self {
601 latent_dim,
602 output_dim,
603 depth,
604 params,
605 }
606 }
607
608 fn generate<R: Rng>(&self, rng: &mut R) -> Array1<bool> {
609 let latent = Array1::from_shape_fn(
611 self.latent_dim,
612 |_| rng.gen::<f64>().mul_add(2.0, -1.0), );
614
615 let mut state = Array1::zeros(self.output_dim);
617
618 for layer in 0..self.depth {
620 for i in 0..self.output_dim.min(self.latent_dim) {
622 let angle = latent[i] * self.params[[layer, i]];
623 state[i] = angle.sin();
624 }
625
626 for i in 0..self.output_dim - 1 {
628 let coupling = self.params[[layer, self.latent_dim + i]];
629 let temp = state[i];
630 state[i] = state[i].mul_add(coupling.cos(), state[i + 1] * coupling.sin());
631 state[i + 1] = state[i + 1].mul_add(coupling.cos(), -(temp * coupling.sin()));
632 }
633 }
634
635 state.mapv(|x| x > 0.0)
637 }
638
639 fn update_parameters(&mut self, loss: f64, lr: f64) {
640 let gradient_estimate = loss * 0.1;
642 self.params -= lr * gradient_estimate;
643 }
644}
645
646impl QuantumDiscriminator {
647 fn new(input_dim: usize, depth: usize) -> Self {
648 let mut rng = thread_rng();
649 let normal =
650 Normal::new(0.0, PI / 4.0).expect("Normal distribution with std=PI/4 is always valid");
651
652 let params = Array2::from_shape_fn((depth, input_dim), |_| normal.sample(&mut rng));
653
654 Self {
655 input_dim,
656 depth,
657 params,
658 }
659 }
660
661 fn forward(&self, input: &Array1<bool>) -> Result<f64, String> {
662 let input_float: Array1<f64> = input.mapv(|x| if x { 1.0 } else { -1.0 });
663 let mut state = input_float;
664
665 for layer in 0..self.depth {
667 for i in 0..self.input_dim {
669 let angle = self.params[[layer, i]];
670 state[i] = state[i].mul_add(angle.cos(), (1.0 - state[i].abs()) * angle.sin());
671 }
672
673 if layer == self.depth - 1 {
675 return state
677 .mean()
678 .ok_or_else(|| "Cannot compute mean of empty state".to_string());
679 }
680 }
681
682 Ok(state[0])
683 }
684
685 fn train_step(
686 &mut self,
687 real: &Array1<bool>,
688 fake: &Array1<bool>,
689 lr: f64,
690 wasserstein: bool,
691 ) -> Result<f64, String> {
692 let real_score = self.forward(real)?;
693 let fake_score = self.forward(fake)?;
694
695 let loss = if wasserstein {
696 fake_score - real_score } else {
698 -(real_score + 1e-8).ln() - (1.0 - fake_score + 1e-8).ln() };
700
701 let gradient_estimate = loss * 0.1;
703 self.params -= lr * gradient_estimate;
704
705 Ok(loss)
706 }
707}
708
709#[derive(Debug, Clone)]
710pub struct QGANTrainingResult {
711 pub generator_losses: Vec<f64>,
712 pub discriminator_losses: Vec<f64>,
713 pub final_gen_loss: f64,
714 pub final_disc_loss: f64,
715}
716
717pub struct QuantumRL {
719 state_dim: usize,
721 action_dim: usize,
723 q_network: QuantumQNetwork,
725 replay_buffer: Vec<Experience>,
727 epsilon: f64,
729 gamma: f64,
731 learning_rate: f64,
733}
734
735#[derive(Clone)]
736struct QuantumQNetwork {
737 input_dim: usize,
739 output_dim: usize,
741 n_layers: usize,
743 params: Array3<f64>,
745}
746
747#[derive(Debug, Clone)]
748pub struct Experience {
749 pub state: Array1<f64>,
750 pub action: usize,
751 pub reward: f64,
752 pub next_state: Array1<f64>,
753 pub done: bool,
754}
755
756impl QuantumRL {
757 pub fn new(state_dim: usize, action_dim: usize) -> Self {
759 let q_network = QuantumQNetwork::new(state_dim, action_dim, 4);
760
761 Self {
762 state_dim,
763 action_dim,
764 q_network,
765 replay_buffer: Vec::new(),
766 epsilon: 1.0,
767 gamma: 0.99,
768 learning_rate: 0.001,
769 }
770 }
771
772 pub fn select_action(&self, state: &Array1<f64>, rng: &mut StdRng) -> usize {
774 if rng.gen_bool(self.epsilon) {
775 rng.gen_range(0..self.action_dim)
777 } else {
778 let q_values = self.q_network.forward(state);
780 q_values
781 .iter()
782 .enumerate()
783 .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
784 .map(|(idx, _)| idx)
785 .unwrap_or(0)
786 }
787 }
788
789 pub fn store_experience(&mut self, experience: Experience) {
791 self.replay_buffer.push(experience);
792
793 if self.replay_buffer.len() > 10000 {
795 self.replay_buffer.remove(0);
796 }
797 }
798
799 pub fn train(&mut self, batch_size: usize) -> Result<f64, String> {
801 if self.replay_buffer.len() < batch_size {
802 return Ok(0.0);
803 }
804
805 let mut rng = thread_rng();
806 let mut total_loss = 0.0;
807
808 for _ in 0..batch_size {
810 let idx = rng.gen_range(0..self.replay_buffer.len());
811 let experience = &self.replay_buffer[idx];
812
813 let target = if experience.done {
815 experience.reward
816 } else {
817 let next_q_values = self.q_network.forward(&experience.next_state);
818 self.gamma.mul_add(
819 next_q_values
820 .iter()
821 .copied()
822 .fold(f64::NEG_INFINITY, f64::max),
823 experience.reward,
824 )
825 };
826
827 let loss = self.q_network.update(
829 &experience.state,
830 experience.action,
831 target,
832 self.learning_rate,
833 )?;
834
835 total_loss += loss;
836 }
837
838 self.epsilon *= 0.995;
840 self.epsilon = self.epsilon.max(0.01);
841
842 Ok(total_loss / batch_size as f64)
843 }
844}
845
846impl QuantumQNetwork {
847 fn new(input_dim: usize, output_dim: usize, n_layers: usize) -> Self {
848 let mut rng = thread_rng();
849 let normal =
850 Normal::new(0.0, 0.1).expect("Normal distribution with std=0.1 is always valid");
851
852 let params = Array3::from_shape_fn(
853 (n_layers, input_dim + output_dim, input_dim + output_dim),
854 |_| normal.sample(&mut rng),
855 );
856
857 Self {
858 input_dim,
859 output_dim,
860 n_layers,
861 params,
862 }
863 }
864
865 fn forward(&self, state: &Array1<f64>) -> Array1<f64> {
866 let mut quantum_state = Array1::zeros(self.input_dim + self.output_dim);
868 quantum_state
869 .slice_mut(scirs2_core::ndarray::s![..self.input_dim])
870 .assign(state);
871
872 for layer in 0..self.n_layers {
874 let mut new_state = Array1::zeros(quantum_state.len());
875
876 for i in 0..quantum_state.len() {
878 for j in 0..quantum_state.len() {
879 new_state[i] += quantum_state[j] * self.params[[layer, i, j]];
880 }
881 }
882
883 quantum_state = new_state.mapv(|x: f64| x.tanh());
885 }
886
887 quantum_state
889 .slice(scirs2_core::ndarray::s![self.input_dim..])
890 .to_owned()
891 }
892
893 fn update(
894 &mut self,
895 state: &Array1<f64>,
896 action: usize,
897 target: f64,
898 lr: f64,
899 ) -> Result<f64, String> {
900 let q_values = self.forward(state);
901 let prediction = q_values[action];
902 let loss = (target - prediction).powi(2);
903
904 let gradient = 2.0 * (prediction - target);
906
907 for layer in 0..self.n_layers {
909 for i in 0..self.params.shape()[1] {
910 for j in 0..self.params.shape()[2] {
911 self.params[[layer, i, j]] -= lr * gradient * 0.01;
912 }
913 }
914 }
915
916 Ok(loss)
917 }
918}
919
920#[cfg(test)]
921mod tests {
922 use super::*;
923
924 #[test]
925 fn test_quantum_boltzmann_machine() {
926 let mut qbm = QuantumBoltzmannMachine::new(4, 2);
927
928 let data = Array2::from_shape_fn((10, 4), |(i, j)| (i + j) % 2 == 0);
930
931 let mut result = qbm.train(&data, 10);
932 assert!(result.is_ok());
933
934 let samples = qbm.generate_samples(5);
935 assert_eq!(samples.len(), 5);
936 }
937
938 #[test]
939 fn test_quantum_vae() {
940 let qvae = QuantumVAE::new(8, 2, 3);
941
942 let input = Array1::from_vec(vec![true, false, true, false, true, false, true, false]);
943 let (mean, log_var) = qvae.encode(&input);
944
945 assert_eq!(mean.len(), 2);
946 assert_eq!(log_var.len(), 2);
947
948 let samples = qvae.generate(3);
949 assert_eq!(samples.len(), 3);
950 }
951
952 #[test]
953 fn test_quantum_gan() {
954 let mut qgan = QuantumGAN::new(2, 4, 2);
955
956 let mut real_data = vec![
958 Array1::from_vec(vec![true, false, true, false]),
959 Array1::from_vec(vec![false, true, false, true]),
960 ];
961
962 let mut result = qgan.train(&real_data, 5);
963 assert!(result.is_ok());
964
965 let samples = qgan.generate_optimized(3);
966 assert_eq!(samples.len(), 3);
967 }
968
969 #[test]
970 fn test_quantum_rl() {
971 let mut qrl = QuantumRL::new(4, 2);
972 let mut rng = StdRng::seed_from_u64(42);
973
974 let mut state = Array1::from_vec(vec![0.1, 0.2, 0.3, 0.4]);
975 let action = qrl.select_action(&state, &mut rng);
976 assert!(action < 2);
977
978 let experience = Experience {
979 state: state.clone(),
980 action,
981 reward: 1.0,
982 next_state: Array1::from_vec(vec![0.2, 0.3, 0.4, 0.5]),
983 done: false,
984 };
985
986 qrl.store_experience(experience);
987 assert_eq!(qrl.replay_buffer.len(), 1);
988 }
989}