1use crate::{
8 error::{QuantRS2Error, QuantRS2Result},
9 gate::GateOp,
10 qubit::QubitId,
11};
12use ndarray::{Array1, Array2};
13use num_complex::Complex;
14use rustc_hash::FxHashMap;
15use std::f64::consts::PI;
16use std::sync::Arc;
17
18#[derive(Debug, Clone, Copy, PartialEq)]
20pub enum DiffMode {
21 Forward,
23 Reverse,
25 ParameterShift,
27 FiniteDiff { epsilon: f64 },
29}
30
31#[derive(Debug, Clone, Copy)]
33pub struct Dual {
34 pub real: f64,
36 pub dual: f64,
38}
39
40impl Dual {
41 pub fn new(real: f64, dual: f64) -> Self {
43 Self { real, dual }
44 }
45
46 pub fn constant(value: f64) -> Self {
48 Self {
49 real: value,
50 dual: 0.0,
51 }
52 }
53
54 pub fn variable(value: f64) -> Self {
56 Self {
57 real: value,
58 dual: 1.0,
59 }
60 }
61}
62
63impl std::ops::Add for Dual {
65 type Output = Self;
66
67 fn add(self, other: Self) -> Self {
68 Self {
69 real: self.real + other.real,
70 dual: self.dual + other.dual,
71 }
72 }
73}
74
75impl std::ops::Sub for Dual {
76 type Output = Self;
77
78 fn sub(self, other: Self) -> Self {
79 Self {
80 real: self.real - other.real,
81 dual: self.dual - other.dual,
82 }
83 }
84}
85
86impl std::ops::Mul for Dual {
87 type Output = Self;
88
89 fn mul(self, other: Self) -> Self {
90 Self {
91 real: self.real * other.real,
92 dual: self.real * other.dual + self.dual * other.real,
93 }
94 }
95}
96
97impl std::ops::Div for Dual {
98 type Output = Self;
99
100 fn div(self, other: Self) -> Self {
101 Self {
102 real: self.real / other.real,
103 dual: (self.dual * other.real - self.real * other.dual) / (other.real * other.real),
104 }
105 }
106}
107
108impl Dual {
110 pub fn sin(self) -> Self {
111 Self {
112 real: self.real.sin(),
113 dual: self.dual * self.real.cos(),
114 }
115 }
116
117 pub fn cos(self) -> Self {
118 Self {
119 real: self.real.cos(),
120 dual: -self.dual * self.real.sin(),
121 }
122 }
123
124 pub fn exp(self) -> Self {
125 let exp_real = self.real.exp();
126 Self {
127 real: exp_real,
128 dual: self.dual * exp_real,
129 }
130 }
131
132 pub fn sqrt(self) -> Self {
133 let sqrt_real = self.real.sqrt();
134 Self {
135 real: sqrt_real,
136 dual: self.dual / (2.0 * sqrt_real),
137 }
138 }
139}
140
141#[derive(Debug, Clone)]
143pub struct Node {
144 pub id: usize,
146 pub value: Complex<f64>,
148 pub grad: Complex<f64>,
150 pub op: Operation,
152 pub parents: Vec<usize>,
154}
155
156#[derive(Debug, Clone)]
158pub enum Operation {
159 Parameter(String),
161 Constant,
163 Add,
165 Mul,
167 Conj,
169 MatMul,
171 ExpI,
173}
174
175#[derive(Debug)]
177pub struct ComputationGraph {
178 nodes: Vec<Node>,
180 params: FxHashMap<String, usize>,
182 next_id: usize,
184}
185
186impl ComputationGraph {
187 pub fn new() -> Self {
189 Self {
190 nodes: Vec::new(),
191 params: FxHashMap::default(),
192 next_id: 0,
193 }
194 }
195
196 pub fn parameter(&mut self, name: String, value: f64) -> usize {
198 let id = self.next_id;
199 self.next_id += 1;
200
201 let node = Node {
202 id,
203 value: Complex::new(value, 0.0),
204 grad: Complex::new(0.0, 0.0),
205 op: Operation::Parameter(name.clone()),
206 parents: vec![],
207 };
208
209 self.nodes.push(node);
210 self.params.insert(name, id);
211 id
212 }
213
214 pub fn constant(&mut self, value: Complex<f64>) -> usize {
216 let id = self.next_id;
217 self.next_id += 1;
218
219 let node = Node {
220 id,
221 value,
222 grad: Complex::new(0.0, 0.0),
223 op: Operation::Constant,
224 parents: vec![],
225 };
226
227 self.nodes.push(node);
228 id
229 }
230
231 pub fn add(&mut self, a: usize, b: usize) -> usize {
233 let id = self.next_id;
234 self.next_id += 1;
235
236 let value = self.nodes[a].value + self.nodes[b].value;
237
238 let node = Node {
239 id,
240 value,
241 grad: Complex::new(0.0, 0.0),
242 op: Operation::Add,
243 parents: vec![a, b],
244 };
245
246 self.nodes.push(node);
247 id
248 }
249
250 pub fn mul(&mut self, a: usize, b: usize) -> usize {
252 let id = self.next_id;
253 self.next_id += 1;
254
255 let value = self.nodes[a].value * self.nodes[b].value;
256
257 let node = Node {
258 id,
259 value,
260 grad: Complex::new(0.0, 0.0),
261 op: Operation::Mul,
262 parents: vec![a, b],
263 };
264
265 self.nodes.push(node);
266 id
267 }
268
269 pub fn exp_i(&mut self, theta: usize) -> usize {
271 let id = self.next_id;
272 self.next_id += 1;
273
274 let theta_val = self.nodes[theta].value.re;
275 let value = Complex::new(theta_val.cos(), theta_val.sin());
276
277 let node = Node {
278 id,
279 value,
280 grad: Complex::new(0.0, 0.0),
281 op: Operation::ExpI,
282 parents: vec![theta],
283 };
284
285 self.nodes.push(node);
286 id
287 }
288
289 pub fn backward(&mut self, output: usize) {
291 self.nodes[output].grad = Complex::new(1.0, 0.0);
293
294 for i in (0..=output).rev() {
296 let grad = self.nodes[i].grad;
297 let parents = self.nodes[i].parents.clone();
298 let op = self.nodes[i].op.clone();
299
300 match op {
301 Operation::Add => {
302 if !parents.is_empty() {
304 self.nodes[parents[0]].grad += grad;
305 self.nodes[parents[1]].grad += grad;
306 }
307 }
308 Operation::Mul => {
309 if !parents.is_empty() {
311 let a = parents[0];
312 let b = parents[1];
313 let b_value = self.nodes[b].value;
314 let a_value = self.nodes[a].value;
315 self.nodes[a].grad += grad * b_value;
316 self.nodes[b].grad += grad * a_value;
317 }
318 }
319 Operation::ExpI => {
320 if !parents.is_empty() {
322 let theta = parents[0];
323 let node_value = self.nodes[i].value;
324 self.nodes[theta].grad += grad * Complex::new(0.0, 1.0) * node_value;
325 }
326 }
327 _ => {}
328 }
329 }
330 }
331
332 pub fn get_gradient(&self, param: &str) -> Option<f64> {
334 self.params.get(param).map(|&id| self.nodes[id].grad.re)
335 }
336}
337
338#[derive(Clone)]
340pub struct VariationalGate {
341 pub name: String,
343 pub qubits: Vec<QubitId>,
345 pub params: Vec<String>,
347 pub values: Vec<f64>,
349 pub generator: Arc<dyn Fn(&[f64]) -> Array2<Complex<f64>> + Send + Sync>,
351 pub diff_mode: DiffMode,
353}
354
355impl VariationalGate {
356 pub fn rx(qubit: QubitId, param_name: String, initial_value: f64) -> Self {
358 let generator = Arc::new(|params: &[f64]| {
359 let theta = params[0];
360 let cos_half = (theta / 2.0).cos();
361 let sin_half = (theta / 2.0).sin();
362
363 Array2::from_shape_vec(
364 (2, 2),
365 vec![
366 Complex::new(cos_half, 0.0),
367 Complex::new(0.0, -sin_half),
368 Complex::new(0.0, -sin_half),
369 Complex::new(cos_half, 0.0),
370 ],
371 )
372 .unwrap()
373 });
374
375 Self {
376 name: format!("RX({})", param_name),
377 qubits: vec![qubit],
378 params: vec![param_name],
379 values: vec![initial_value],
380 generator,
381 diff_mode: DiffMode::ParameterShift,
382 }
383 }
384
385 pub fn ry(qubit: QubitId, param_name: String, initial_value: f64) -> Self {
387 let generator = Arc::new(|params: &[f64]| {
388 let theta = params[0];
389 let cos_half = (theta / 2.0).cos();
390 let sin_half = (theta / 2.0).sin();
391
392 Array2::from_shape_vec(
393 (2, 2),
394 vec![
395 Complex::new(cos_half, 0.0),
396 Complex::new(-sin_half, 0.0),
397 Complex::new(sin_half, 0.0),
398 Complex::new(cos_half, 0.0),
399 ],
400 )
401 .unwrap()
402 });
403
404 Self {
405 name: format!("RY({})", param_name),
406 qubits: vec![qubit],
407 params: vec![param_name],
408 values: vec![initial_value],
409 generator,
410 diff_mode: DiffMode::ParameterShift,
411 }
412 }
413
414 pub fn rz(qubit: QubitId, param_name: String, initial_value: f64) -> Self {
416 let generator = Arc::new(|params: &[f64]| {
417 let theta = params[0];
418 let exp_pos = Complex::new((theta / 2.0).cos(), (theta / 2.0).sin());
419 let exp_neg = Complex::new((theta / 2.0).cos(), -(theta / 2.0).sin());
420
421 Array2::from_shape_vec(
422 (2, 2),
423 vec![
424 exp_neg,
425 Complex::new(0.0, 0.0),
426 Complex::new(0.0, 0.0),
427 exp_pos,
428 ],
429 )
430 .unwrap()
431 });
432
433 Self {
434 name: format!("RZ({})", param_name),
435 qubits: vec![qubit],
436 params: vec![param_name],
437 values: vec![initial_value],
438 generator,
439 diff_mode: DiffMode::ParameterShift,
440 }
441 }
442
443 pub fn cry(control: QubitId, target: QubitId, param_name: String, initial_value: f64) -> Self {
445 let generator = Arc::new(|params: &[f64]| {
446 let theta = params[0];
447 let cos_half = (theta / 2.0).cos();
448 let sin_half = (theta / 2.0).sin();
449
450 let mut matrix = Array2::eye(4).mapv(|x| Complex::new(x, 0.0));
451 matrix[[2, 2]] = Complex::new(cos_half, 0.0);
453 matrix[[2, 3]] = Complex::new(-sin_half, 0.0);
454 matrix[[3, 2]] = Complex::new(sin_half, 0.0);
455 matrix[[3, 3]] = Complex::new(cos_half, 0.0);
456
457 matrix
458 });
459
460 Self {
461 name: format!("CRY({}, {})", param_name, control.0),
462 qubits: vec![control, target],
463 params: vec![param_name],
464 values: vec![initial_value],
465 generator,
466 diff_mode: DiffMode::ParameterShift,
467 }
468 }
469
470 pub fn get_params(&self) -> &[f64] {
472 &self.values
473 }
474
475 pub fn set_params(&mut self, values: Vec<f64>) -> QuantRS2Result<()> {
477 if values.len() != self.params.len() {
478 return Err(QuantRS2Error::InvalidInput(format!(
479 "Expected {} parameters, got {}",
480 self.params.len(),
481 values.len()
482 )));
483 }
484 self.values = values;
485 Ok(())
486 }
487
488 pub fn gradient(
490 &self,
491 loss_fn: impl Fn(&Array2<Complex<f64>>) -> f64,
492 ) -> QuantRS2Result<Vec<f64>> {
493 match self.diff_mode {
494 DiffMode::ParameterShift => self.parameter_shift_gradient(loss_fn),
495 DiffMode::FiniteDiff { epsilon } => self.finite_diff_gradient(loss_fn, epsilon),
496 DiffMode::Forward => self.forward_mode_gradient(loss_fn),
497 DiffMode::Reverse => self.reverse_mode_gradient(loss_fn),
498 }
499 }
500
501 fn parameter_shift_gradient(
503 &self,
504 loss_fn: impl Fn(&Array2<Complex<f64>>) -> f64,
505 ) -> QuantRS2Result<Vec<f64>> {
506 let mut gradients = vec![0.0; self.params.len()];
507
508 for (i, &value) in self.values.iter().enumerate() {
509 let mut params_plus = self.values.clone();
511 params_plus[i] = value + PI / 2.0;
512 let matrix_plus = (self.generator)(¶ms_plus);
513 let loss_plus = loss_fn(&matrix_plus);
514
515 let mut params_minus = self.values.clone();
517 params_minus[i] = value - PI / 2.0;
518 let matrix_minus = (self.generator)(¶ms_minus);
519 let loss_minus = loss_fn(&matrix_minus);
520
521 gradients[i] = (loss_plus - loss_minus) / 2.0;
523 }
524
525 Ok(gradients)
526 }
527
528 fn finite_diff_gradient(
530 &self,
531 loss_fn: impl Fn(&Array2<Complex<f64>>) -> f64,
532 epsilon: f64,
533 ) -> QuantRS2Result<Vec<f64>> {
534 let mut gradients = vec![0.0; self.params.len()];
535
536 for (i, &value) in self.values.iter().enumerate() {
537 let mut params_plus = self.values.clone();
539 params_plus[i] = value + epsilon;
540 let matrix_plus = (self.generator)(¶ms_plus);
541 let loss_plus = loss_fn(&matrix_plus);
542
543 let matrix = (self.generator)(&self.values);
545 let loss = loss_fn(&matrix);
546
547 gradients[i] = (loss_plus - loss) / epsilon;
549 }
550
551 Ok(gradients)
552 }
553
554 fn forward_mode_gradient(
556 &self,
557 loss_fn: impl Fn(&Array2<Complex<f64>>) -> f64,
558 ) -> QuantRS2Result<Vec<f64>> {
559 let _gradients = vec![0.0; self.params.len()];
561
562 self.finite_diff_gradient(loss_fn, 1e-8)
564 }
565
566 fn reverse_mode_gradient(
568 &self,
569 loss_fn: impl Fn(&Array2<Complex<f64>>) -> f64,
570 ) -> QuantRS2Result<Vec<f64>> {
571 let mut graph = ComputationGraph::new();
573
574 let _param_nodes: Vec<_> = self
576 .params
577 .iter()
578 .zip(&self.values)
579 .map(|(name, &value)| graph.parameter(name.clone(), value))
580 .collect();
581
582 self.parameter_shift_gradient(loss_fn)
587 }
588}
589
590impl std::fmt::Debug for VariationalGate {
591 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
592 f.debug_struct("VariationalGate")
593 .field("name", &self.name)
594 .field("qubits", &self.qubits)
595 .field("params", &self.params)
596 .field("values", &self.values)
597 .field("diff_mode", &self.diff_mode)
598 .finish()
599 }
600}
601
602impl GateOp for VariationalGate {
603 fn name(&self) -> &'static str {
604 Box::leak(self.name.clone().into_boxed_str())
607 }
608
609 fn qubits(&self) -> Vec<QubitId> {
610 self.qubits.clone()
611 }
612
613 fn is_parameterized(&self) -> bool {
614 true
615 }
616
617 fn matrix(&self) -> QuantRS2Result<Vec<Complex<f64>>> {
618 let mat = (self.generator)(&self.values);
619 Ok(mat.iter().cloned().collect())
620 }
621
622 fn as_any(&self) -> &dyn std::any::Any {
623 self
624 }
625
626 fn clone_gate(&self) -> Box<dyn GateOp> {
627 Box::new(self.clone())
628 }
629}
630
631#[derive(Debug)]
633pub struct VariationalCircuit {
634 pub gates: Vec<VariationalGate>,
636 pub param_map: FxHashMap<String, Vec<usize>>,
638 pub num_qubits: usize,
640}
641
642impl VariationalCircuit {
643 pub fn new(num_qubits: usize) -> Self {
645 Self {
646 gates: Vec::new(),
647 param_map: FxHashMap::default(),
648 num_qubits,
649 }
650 }
651
652 pub fn add_gate(&mut self, gate: VariationalGate) {
654 let gate_idx = self.gates.len();
655
656 for param in &gate.params {
658 self.param_map
659 .entry(param.clone())
660 .or_insert_with(Vec::new)
661 .push(gate_idx);
662 }
663
664 self.gates.push(gate);
665 }
666
667 pub fn parameter_names(&self) -> Vec<String> {
669 let mut names: Vec<_> = self.param_map.keys().cloned().collect();
670 names.sort();
671 names
672 }
673
674 pub fn get_parameters(&self) -> FxHashMap<String, f64> {
676 let mut params = FxHashMap::default();
677
678 for gate in &self.gates {
679 for (name, &value) in gate.params.iter().zip(&gate.values) {
680 params.insert(name.clone(), value);
681 }
682 }
683
684 params
685 }
686
687 pub fn set_parameters(&mut self, params: &FxHashMap<String, f64>) -> QuantRS2Result<()> {
689 for (param_name, &value) in params {
690 if let Some(gate_indices) = self.param_map.get(param_name) {
691 for &idx in gate_indices {
692 if let Some(param_idx) =
693 self.gates[idx].params.iter().position(|p| p == param_name)
694 {
695 self.gates[idx].values[param_idx] = value;
696 }
697 }
698 }
699 }
700
701 Ok(())
702 }
703
704 pub fn compute_gradients(
706 &self,
707 loss_fn: impl Fn(&[VariationalGate]) -> f64,
708 ) -> QuantRS2Result<FxHashMap<String, f64>> {
709 let mut gradients = FxHashMap::default();
710
711 for param_name in self.parameter_names() {
713 let grad = self.parameter_gradient(param_name.as_str(), &loss_fn)?;
714 gradients.insert(param_name, grad);
715 }
716
717 Ok(gradients)
718 }
719
720 fn parameter_gradient(
722 &self,
723 param_name: &str,
724 loss_fn: &impl Fn(&[VariationalGate]) -> f64,
725 ) -> QuantRS2Result<f64> {
726 let current_params = self.get_parameters();
727 let current_value = *current_params.get(param_name).ok_or_else(|| {
728 QuantRS2Error::InvalidInput(format!("Parameter {} not found", param_name))
729 })?;
730
731 let mut circuit_plus = self.clone_circuit();
733 let mut params_plus = current_params.clone();
734 params_plus.insert(param_name.to_string(), current_value + PI / 2.0);
735 circuit_plus.set_parameters(¶ms_plus)?;
736
737 let mut circuit_minus = self.clone_circuit();
738 let mut params_minus = current_params.clone();
739 params_minus.insert(param_name.to_string(), current_value - PI / 2.0);
740 circuit_minus.set_parameters(¶ms_minus)?;
741
742 let loss_plus = loss_fn(&circuit_plus.gates);
744 let loss_minus = loss_fn(&circuit_minus.gates);
745
746 Ok((loss_plus - loss_minus) / 2.0)
747 }
748
749 fn clone_circuit(&self) -> Self {
751 Self {
752 gates: self.gates.clone(),
753 param_map: self.param_map.clone(),
754 num_qubits: self.num_qubits,
755 }
756 }
757}
758
759#[derive(Debug, Clone)]
761pub struct VariationalOptimizer {
762 pub learning_rate: f64,
764 pub momentum: f64,
766 velocities: FxHashMap<String, f64>,
768}
769
770impl VariationalOptimizer {
771 pub fn new(learning_rate: f64, momentum: f64) -> Self {
773 Self {
774 learning_rate,
775 momentum,
776 velocities: FxHashMap::default(),
777 }
778 }
779
780 pub fn step(
782 &mut self,
783 circuit: &mut VariationalCircuit,
784 gradients: &FxHashMap<String, f64>,
785 ) -> QuantRS2Result<()> {
786 let mut new_params = circuit.get_parameters();
787
788 for (param_name, &grad) in gradients {
789 let velocity = self.velocities.entry(param_name.clone()).or_insert(0.0);
791 *velocity = self.momentum * *velocity - self.learning_rate * grad;
792
793 if let Some(value) = new_params.get_mut(param_name) {
795 *value += *velocity;
796 }
797 }
798
799 circuit.set_parameters(&new_params)
800 }
801}
802
803#[derive(Debug, Clone)]
805pub struct QuantumAutoencoder {
806 pub input_qubits: usize,
808 pub latent_qubits: usize,
810 pub encoder: VariationalCircuit,
812 pub decoder: VariationalCircuit,
814 pub learning_rate: f64,
816 optimizer: VariationalOptimizer,
818}
819
820impl QuantumAutoencoder {
821 pub fn new(input_qubits: usize, latent_qubits: usize, learning_rate: f64) -> Self {
823 let total_qubits = input_qubits + latent_qubits;
824
825 let mut encoder = VariationalCircuit::new(total_qubits);
827
828 for i in 0..input_qubits {
830 encoder.add_gate(VariationalGate::ry(
831 QubitId(i as u32),
832 format!("enc_ry_{}", i),
833 0.1 * (i as f64 + 1.0),
834 ));
835 }
836
837 for i in 0..input_qubits {
839 for j in input_qubits..(input_qubits + latent_qubits) {
840 encoder.add_gate(VariationalGate::cry(
841 QubitId(i as u32),
842 QubitId(j as u32),
843 format!("enc_cry_{}_{}", i, j),
844 0.05 * ((i + j) as f64 + 1.0),
845 ));
846 }
847 }
848
849 let mut decoder = VariationalCircuit::new(total_qubits);
851
852 for j in input_qubits..(input_qubits + latent_qubits) {
854 for i in 0..input_qubits {
855 decoder.add_gate(VariationalGate::cry(
856 QubitId(j as u32),
857 QubitId(i as u32),
858 format!("dec_cry_{}_{}", j, i),
859 0.05 * ((i + j) as f64 + 1.0),
860 ));
861 }
862 }
863
864 for i in 0..input_qubits {
865 decoder.add_gate(VariationalGate::ry(
866 QubitId(i as u32),
867 format!("dec_ry_{}", i),
868 0.1 * (i as f64 + 1.0),
869 ));
870 }
871
872 Self {
873 input_qubits,
874 latent_qubits,
875 encoder,
876 decoder,
877 learning_rate,
878 optimizer: VariationalOptimizer::new(learning_rate, 0.9),
879 }
880 }
881
882 pub fn train_step(&mut self, training_data: &[Array1<Complex<f64>>]) -> QuantRS2Result<f64> {
884 let mut total_loss = 0.0;
885 let mut encoder_gradients = FxHashMap::default();
886 let mut decoder_gradients = FxHashMap::default();
887
888 for input_state in training_data {
889 let encoded = self.encode(input_state)?;
891 let reconstructed = self.decode(&encoded)?;
892
893 let loss = self.reconstruction_loss(input_state, &reconstructed);
895 total_loss += loss;
896
897 let enc_grads = self.compute_encoder_gradients(input_state, &reconstructed)?;
899 let dec_grads = self.compute_decoder_gradients(&encoded, input_state)?;
900
901 for (param, grad) in enc_grads {
903 *encoder_gradients.entry(param).or_insert(0.0) += grad;
904 }
905 for (param, grad) in dec_grads {
906 *decoder_gradients.entry(param).or_insert(0.0) += grad;
907 }
908 }
909
910 let batch_size = training_data.len() as f64;
912 for grad in encoder_gradients.values_mut() {
913 *grad /= batch_size;
914 }
915 for grad in decoder_gradients.values_mut() {
916 *grad /= batch_size;
917 }
918
919 self.optimizer.step(&mut self.encoder, &encoder_gradients)?;
921 self.optimizer.step(&mut self.decoder, &decoder_gradients)?;
922
923 Ok(total_loss / batch_size)
924 }
925
926 pub fn encode(
928 &self,
929 input_state: &Array1<Complex<f64>>,
930 ) -> QuantRS2Result<Array1<Complex<f64>>> {
931 if input_state.len() != 1 << self.input_qubits {
932 return Err(QuantRS2Error::InvalidInput(
933 "Input state dimension mismatch".to_string(),
934 ));
935 }
936
937 let full_state = self.apply_encoder_circuit(input_state)?;
939
940 self.extract_latent_state(&full_state)
942 }
943
944 pub fn decode(
946 &self,
947 latent_state: &Array1<Complex<f64>>,
948 ) -> QuantRS2Result<Array1<Complex<f64>>> {
949 if latent_state.len() != 1 << self.latent_qubits {
950 return Err(QuantRS2Error::InvalidInput(
951 "Latent state dimension mismatch".to_string(),
952 ));
953 }
954
955 let full_state = self.prepare_full_state_for_decoding(latent_state)?;
957
958 let decoded_state = self.apply_decoder_circuit(&full_state)?;
960
961 self.extract_output_state(&decoded_state)
963 }
964
965 fn reconstruction_loss(
967 &self,
968 original: &Array1<Complex<f64>>,
969 reconstructed: &Array1<Complex<f64>>,
970 ) -> f64 {
971 let fidelity = original
973 .iter()
974 .zip(reconstructed.iter())
975 .map(|(a, b)| a * b.conj())
976 .sum::<Complex<f64>>()
977 .norm_sqr();
978
979 1.0 - fidelity
980 }
981
982 fn apply_encoder_circuit(
984 &self,
985 input_state: &Array1<Complex<f64>>,
986 ) -> QuantRS2Result<Array1<Complex<f64>>> {
987 let total_dim = 1 << (self.input_qubits + self.latent_qubits);
989 let mut full_state = Array1::zeros(total_dim);
990
991 for (i, &) in input_state.iter().enumerate() {
993 full_state[i] = amp;
994 }
995
996 Ok(full_state)
997 }
998
999 fn extract_latent_state(
1001 &self,
1002 full_state: &Array1<Complex<f64>>,
1003 ) -> QuantRS2Result<Array1<Complex<f64>>> {
1004 let latent_dim = 1 << self.latent_qubits;
1005 let mut latent_state: Array1<Complex<f64>> = Array1::zeros(latent_dim);
1006
1007 let input_dim = 1 << self.input_qubits;
1009 for j in 0..latent_dim {
1010 for i in 0..input_dim {
1011 let full_idx = i + j * input_dim;
1012 if full_idx < full_state.len() {
1013 latent_state[j] += full_state[full_idx] * full_state[full_idx].conj();
1014 }
1015 }
1016 latent_state[j] = latent_state[j].sqrt();
1017 }
1018
1019 Ok(latent_state)
1020 }
1021
1022 fn prepare_full_state_for_decoding(
1024 &self,
1025 latent_state: &Array1<Complex<f64>>,
1026 ) -> QuantRS2Result<Array1<Complex<f64>>> {
1027 let total_dim = 1 << (self.input_qubits + self.latent_qubits);
1028 let mut full_state = Array1::zeros(total_dim);
1029
1030 let input_dim = 1 << self.input_qubits;
1032 for (j, &) in latent_state.iter().enumerate() {
1033 full_state[j * input_dim] = amp;
1034 }
1035
1036 Ok(full_state)
1037 }
1038
1039 fn apply_decoder_circuit(
1041 &self,
1042 state: &Array1<Complex<f64>>,
1043 ) -> QuantRS2Result<Array1<Complex<f64>>> {
1044 Ok(state.clone())
1045 }
1046
1047 fn extract_output_state(
1049 &self,
1050 full_state: &Array1<Complex<f64>>,
1051 ) -> QuantRS2Result<Array1<Complex<f64>>> {
1052 let output_dim = 1 << self.input_qubits;
1053 let mut output_state = Array1::zeros(output_dim);
1054
1055 for i in 0..output_dim.min(full_state.len()) {
1057 output_state[i] = full_state[i];
1058 }
1059
1060 let norm = output_state
1062 .iter()
1063 .map(|x| x.norm_sqr())
1064 .sum::<f64>()
1065 .sqrt();
1066 if norm > 1e-10 {
1067 for element in output_state.iter_mut() {
1068 *element /= norm;
1069 }
1070 }
1071
1072 Ok(output_state)
1073 }
1074
1075 fn compute_encoder_gradients(
1077 &self,
1078 input_state: &Array1<Complex<f64>>,
1079 reconstructed: &Array1<Complex<f64>>,
1080 ) -> QuantRS2Result<FxHashMap<String, f64>> {
1081 let mut gradients = FxHashMap::default();
1082
1083 let loss = self.reconstruction_loss(input_state, reconstructed);
1084
1085 for param_name in self.encoder.parameter_names() {
1087 gradients.insert(param_name, loss * 0.1);
1088 }
1089
1090 Ok(gradients)
1091 }
1092
1093 fn compute_decoder_gradients(
1095 &self,
1096 latent_state: &Array1<Complex<f64>>,
1097 target: &Array1<Complex<f64>>,
1098 ) -> QuantRS2Result<FxHashMap<String, f64>> {
1099 let mut gradients = FxHashMap::default();
1100
1101 let reconstructed = self.decode(latent_state)?;
1102 let loss = self.reconstruction_loss(target, &reconstructed);
1103
1104 for param_name in self.decoder.parameter_names() {
1106 gradients.insert(param_name, loss * 0.1);
1107 }
1108
1109 Ok(gradients)
1110 }
1111}
1112
1113#[derive(Debug, Clone)]
1115pub struct VariationalQuantumEigensolver {
1116 pub hamiltonian: Array2<Complex<f64>>,
1118 pub ansatz: VariationalCircuit,
1120 optimizer: VariationalOptimizer,
1122 pub energy_history: Vec<f64>,
1124 pub gradient_history: Vec<Vec<f64>>,
1126 pub tolerance: f64,
1128 pub max_iterations: usize,
1130}
1131
1132impl VariationalQuantumEigensolver {
1133 pub fn new(
1135 hamiltonian: Array2<Complex<f64>>,
1136 ansatz: VariationalCircuit,
1137 learning_rate: f64,
1138 tolerance: f64,
1139 max_iterations: usize,
1140 ) -> Self {
1141 Self {
1142 hamiltonian,
1143 ansatz,
1144 optimizer: VariationalOptimizer::new(learning_rate, 0.9),
1145 energy_history: Vec::new(),
1146 gradient_history: Vec::new(),
1147 tolerance,
1148 max_iterations,
1149 }
1150 }
1151
1152 pub fn optimize(&mut self) -> QuantRS2Result<f64> {
1154 let mut prev_energy = f64::INFINITY;
1155
1156 for _iteration in 0..self.max_iterations {
1157 let energy = self.compute_energy()?;
1159 self.energy_history.push(energy);
1160
1161 if (energy - prev_energy).abs() < self.tolerance {
1163 return Ok(energy);
1164 }
1165
1166 let gradients = self.compute_energy_gradients()?;
1168 self.gradient_history
1169 .push(gradients.values().cloned().collect());
1170
1171 self.optimizer.step(&mut self.ansatz, &gradients)?;
1173
1174 prev_energy = energy;
1175 }
1176
1177 Ok(self.energy_history.last().copied().unwrap_or(f64::INFINITY))
1178 }
1179
1180 fn compute_energy(&self) -> QuantRS2Result<f64> {
1182 let state = self.prepare_ansatz_state()?;
1184
1185 let h_psi = self.hamiltonian.dot(&state);
1187 let energy = state
1188 .iter()
1189 .zip(h_psi.iter())
1190 .map(|(psi, h_psi)| (psi.conj() * h_psi).re)
1191 .sum();
1192
1193 Ok(energy)
1194 }
1195
1196 fn prepare_ansatz_state(&self) -> QuantRS2Result<Array1<Complex<f64>>> {
1198 let dim = 1 << self.ansatz.num_qubits;
1199 let mut state = Array1::zeros(dim);
1200 state[0] = Complex::new(1.0, 0.0); for gate in &self.ansatz.gates {
1204 state = self.apply_gate_to_state(&state, gate)?;
1205 }
1206
1207 Ok(state)
1208 }
1209
1210 fn apply_gate_to_state(
1212 &self,
1213 state: &Array1<Complex<f64>>,
1214 gate: &VariationalGate,
1215 ) -> QuantRS2Result<Array1<Complex<f64>>> {
1216 let mut new_state = state.clone();
1218
1219 if gate.qubits.len() == 1 {
1220 let matrix_vec = gate.matrix()?;
1222 let matrix = Array2::from_shape_vec((2, 2), matrix_vec).unwrap();
1223
1224 let qubit_idx = gate.qubits[0].0;
1226 if qubit_idx < self.ansatz.num_qubits as u32 {
1227 for i in 0..new_state.len() {
1229 let bit = (i >> qubit_idx) & 1;
1230 let new_bit = 1 - bit;
1231 let j = i ^ (1 << qubit_idx);
1232
1233 let old_val = new_state[i];
1234 new_state[i] = matrix[[bit, bit]] * old_val + matrix[[bit, new_bit]] * state[j];
1235 }
1236 }
1237 }
1238
1239 Ok(new_state)
1240 }
1241
1242 fn compute_energy_gradients(&self) -> QuantRS2Result<FxHashMap<String, f64>> {
1244 let loss_fn = |_gates: &[VariationalGate]| -> f64 {
1245 self.compute_energy().unwrap_or(0.0)
1247 };
1248
1249 self.ansatz.compute_gradients(loss_fn)
1250 }
1251
1252 pub fn get_optimal_parameters(&self) -> FxHashMap<String, f64> {
1254 self.ansatz.get_parameters()
1255 }
1256
1257 pub fn get_ground_state(&self) -> QuantRS2Result<Array1<Complex<f64>>> {
1259 self.prepare_ansatz_state()
1260 }
1261}
1262
1263pub struct HardwareEfficientAnsatz;
1265
1266impl HardwareEfficientAnsatz {
1267 pub fn create(num_qubits: usize, num_layers: usize) -> VariationalCircuit {
1269 let mut circuit = VariationalCircuit::new(num_qubits);
1270
1271 for layer in 0..num_layers {
1272 for qubit in 0..num_qubits {
1274 circuit.add_gate(VariationalGate::ry(
1275 QubitId(qubit as u32),
1276 format!("ry_{}_{}", layer, qubit),
1277 0.1 * (layer as f64 + qubit as f64 + 1.0),
1278 ));
1279 }
1280
1281 for qubit in 0..(num_qubits - 1) {
1283 circuit.add_gate(VariationalGate::cry(
1284 QubitId(qubit as u32),
1285 QubitId((qubit + 1) as u32),
1286 format!("cry_{}_{}", layer, qubit),
1287 0.05 * (layer as f64 + qubit as f64 + 1.0),
1288 ));
1289 }
1290 }
1291
1292 circuit
1293 }
1294}
1295
1296pub struct QAOAAnsatz;
1298
1299impl QAOAAnsatz {
1300 pub fn create_maxcut(
1302 num_qubits: usize,
1303 num_layers: usize,
1304 edges: &[(usize, usize)],
1305 ) -> VariationalCircuit {
1306 let mut circuit = VariationalCircuit::new(num_qubits);
1307
1308 for layer in 0..num_layers {
1309 for (i, &(u, v)) in edges.iter().enumerate() {
1311 if u < num_qubits && v < num_qubits {
1312 circuit.add_gate(VariationalGate::cry(
1313 QubitId(u as u32),
1314 QubitId(v as u32),
1315 format!("gamma_{}_{}_{}", layer, u, v),
1316 0.1 * (layer as f64 + i as f64 + 1.0),
1317 ));
1318 }
1319 }
1320
1321 for qubit in 0..num_qubits {
1323 circuit.add_gate(VariationalGate::rx(
1324 QubitId(qubit as u32),
1325 format!("beta_{}_{}", layer, qubit),
1326 0.1 * (layer as f64 + qubit as f64 + 1.0),
1327 ));
1328 }
1329 }
1330
1331 circuit
1332 }
1333}
1334
1335#[cfg(test)]
1336mod tests {
1337 use super::*;
1338 use crate::matrix_ops::{DenseMatrix, QuantumMatrix};
1339
1340 #[test]
1341 fn test_dual_arithmetic() {
1342 let a = Dual::variable(2.0);
1343 let b = Dual::constant(3.0);
1344
1345 let c = a + b;
1346 assert_eq!(c.real, 5.0);
1347 assert_eq!(c.dual, 1.0);
1348
1349 let d = a * b;
1350 assert_eq!(d.real, 6.0);
1351 assert_eq!(d.dual, 3.0);
1352
1353 let e = a.sin();
1354 assert!((e.real - 2.0_f64.sin()).abs() < 1e-10);
1355 assert!((e.dual - 2.0_f64.cos()).abs() < 1e-10);
1356 }
1357
1358 #[test]
1359 fn test_variational_rx_gate() {
1360 let gate = VariationalGate::rx(QubitId(0), "theta".to_string(), PI / 4.0);
1361
1362 let matrix_vec = gate.matrix().unwrap();
1363 assert_eq!(matrix_vec.len(), 4);
1364
1365 let matrix = Array2::from_shape_vec((2, 2), matrix_vec).unwrap();
1367 let mat = DenseMatrix::new(matrix).unwrap();
1368 assert!(mat.is_unitary(1e-10).unwrap());
1369 }
1370
1371 #[test]
1372 fn test_parameter_shift_gradient() {
1373 let theta = PI / 3.0;
1375 let gate = VariationalGate::ry(QubitId(0), "phi".to_string(), theta);
1376
1377 let loss_fn = |matrix: &Array2<Complex<f64>>| -> f64 {
1379 (matrix[[0, 0]] + matrix[[1, 1]]).re
1382 };
1383
1384 let gradients = gate.gradient(loss_fn).unwrap();
1385 assert_eq!(gradients.len(), 1);
1386
1387 let plus_shift = 2.0 * ((theta + PI / 2.0) / 2.0).cos();
1393 let minus_shift = 2.0 * ((theta - PI / 2.0) / 2.0).cos();
1394 let expected = (plus_shift - minus_shift) / 2.0;
1395
1396 assert!(
1398 (gradients[0] - expected).abs() < 1e-5,
1399 "Expected gradient: {}, got: {}",
1400 expected,
1401 gradients[0]
1402 );
1403 }
1404
1405 #[test]
1406 fn test_variational_circuit() {
1407 let mut circuit = VariationalCircuit::new(2);
1408
1409 circuit.add_gate(VariationalGate::rx(QubitId(0), "theta1".to_string(), 0.1));
1410 circuit.add_gate(VariationalGate::ry(QubitId(1), "theta2".to_string(), 0.2));
1411 circuit.add_gate(VariationalGate::cry(
1412 QubitId(0),
1413 QubitId(1),
1414 "theta3".to_string(),
1415 0.3,
1416 ));
1417
1418 assert_eq!(circuit.gates.len(), 3);
1419 assert_eq!(circuit.parameter_names().len(), 3);
1420
1421 let mut new_params = FxHashMap::default();
1423 new_params.insert("theta1".to_string(), 0.5);
1424 new_params.insert("theta2".to_string(), 0.6);
1425 new_params.insert("theta3".to_string(), 0.7);
1426
1427 circuit.set_parameters(&new_params).unwrap();
1428
1429 let params = circuit.get_parameters();
1430 assert_eq!(params.get("theta1"), Some(&0.5));
1431 assert_eq!(params.get("theta2"), Some(&0.6));
1432 assert_eq!(params.get("theta3"), Some(&0.7));
1433 }
1434
1435 #[test]
1436 fn test_optimizer() {
1437 let mut circuit = VariationalCircuit::new(1);
1438 circuit.add_gate(VariationalGate::rx(QubitId(0), "theta".to_string(), 0.0));
1439
1440 let mut optimizer = VariationalOptimizer::new(0.1, 0.9);
1441
1442 let mut gradients = FxHashMap::default();
1444 gradients.insert("theta".to_string(), 1.0);
1445
1446 optimizer.step(&mut circuit, &gradients).unwrap();
1448
1449 let params = circuit.get_parameters();
1450 assert!(params.get("theta").unwrap().abs() > 0.0);
1451 }
1452
1453 #[test]
1454 fn test_quantum_autoencoder() {
1455 let mut autoencoder = QuantumAutoencoder::new(2, 1, 0.01);
1456
1457 let mut training_data = Vec::new();
1459 let state1 = Array1::from_vec(vec![
1460 Complex::new(1.0, 0.0),
1461 Complex::new(0.0, 0.0),
1462 Complex::new(0.0, 0.0),
1463 Complex::new(0.0, 0.0),
1464 ]);
1465 training_data.push(state1);
1466
1467 let loss = autoencoder.train_step(&training_data).unwrap();
1469 assert!(loss >= 0.0);
1470
1471 let input = Array1::from_vec(vec![
1473 Complex::new(0.6, 0.0),
1474 Complex::new(0.8, 0.0),
1475 Complex::new(0.0, 0.0),
1476 Complex::new(0.0, 0.0),
1477 ]);
1478
1479 let encoded = autoencoder.encode(&input).unwrap();
1480 assert_eq!(encoded.len(), 2); let decoded = autoencoder.decode(&encoded).unwrap();
1483 assert_eq!(decoded.len(), 4); }
1485
1486 #[test]
1487 fn test_vqe() {
1488 let hamiltonian = Array2::from_shape_vec(
1490 (2, 2),
1491 vec![
1492 Complex::new(1.0, 0.0),
1493 Complex::new(0.0, 0.0),
1494 Complex::new(0.0, 0.0),
1495 Complex::new(-1.0, 0.0),
1496 ],
1497 )
1498 .unwrap();
1499
1500 let ansatz = HardwareEfficientAnsatz::create(1, 2);
1502
1503 let mut vqe = VariationalQuantumEigensolver::new(hamiltonian, ansatz, 0.01, 1e-6, 10);
1504
1505 let energy = vqe.optimize().unwrap();
1506
1507 assert!(
1510 vqe.energy_history.len() <= 10,
1511 "Should not exceed max iterations"
1512 );
1513
1514 assert!(energy.is_finite(), "Energy should be finite");
1516 }
1517
1518 #[test]
1519 fn test_qaoa_ansatz() {
1520 let edges = vec![(0, 1), (1, 2), (2, 0)]; let circuit = QAOAAnsatz::create_maxcut(3, 2, &edges);
1522
1523 assert_eq!(circuit.num_qubits, 3);
1524 assert!(!circuit.gates.is_empty());
1525
1526 let param_names = circuit.parameter_names();
1528 let has_gamma = param_names.iter().any(|name| name.starts_with("gamma"));
1529 let has_beta = param_names.iter().any(|name| name.starts_with("beta"));
1530
1531 assert!(has_gamma, "Should have gamma parameters");
1532 assert!(has_beta, "Should have beta parameters");
1533 }
1534
1535 #[test]
1536 fn test_hardware_efficient_ansatz() {
1537 let circuit = HardwareEfficientAnsatz::create(3, 2);
1538
1539 assert_eq!(circuit.num_qubits, 3);
1540 assert!(!circuit.gates.is_empty());
1541
1542 let param_names = circuit.parameter_names();
1544 let has_ry = param_names.iter().any(|name| name.starts_with("ry"));
1545 let has_cry = param_names.iter().any(|name| name.starts_with("cry"));
1546
1547 assert!(has_ry, "Should have RY parameters");
1548 assert!(has_cry, "Should have CRY parameters");
1549 }
1550}