1use crate::error::QuantRS2Error;
7use crate::gate::GateOp;
8use crate::matrix_ops::{DenseMatrix, QuantumMatrix};
9use crate::tensor_network::{Tensor, TensorNetwork};
11use scirs2_core::Complex64;
13use scirs2_core::ndarray::{Array1, Array2, Axis};
15
16#[derive(Debug, Clone)]
18pub struct OptimizationResult {
19 pub parameters: Array1<f64>,
20 pub cost: f64,
21 pub iterations: usize,
22}
23
24#[derive(Debug, Clone)]
25pub struct OptimizationConfig {
26 pub max_iterations: usize,
27 pub tolerance: f64,
28}
29
30impl Default for OptimizationConfig {
31 fn default() -> Self {
32 Self {
33 max_iterations: 1000,
34 tolerance: 1e-6,
35 }
36 }
37}
38
39pub fn minimize<F>(
48 objective: F,
49 initial_params: &Array1<f64>,
50 config: &OptimizationConfig,
51) -> Result<OptimizationResult, String>
52where
53 F: Fn(&Array1<f64>) -> Result<f64, String>,
54{
55 let mut params = initial_params.clone();
56 let mut best_cost = objective(¶ms)?;
57 let n = params.len();
58
59 let mut step = 0.1_f64.max(config.tolerance * 10.0);
62 let mut iterations = 0usize;
63
64 while iterations < config.max_iterations {
65 iterations += 1;
66 let mut improved = false;
67
68 for i in 0..n {
69 for &direction in &[step, -step] {
71 let original = params[i];
72 params[i] = original + direction;
73 match objective(¶ms) {
74 Ok(trial_cost) if trial_cost < best_cost => {
75 best_cost = trial_cost;
76 improved = true;
77 }
78 _ => {
79 params[i] = original;
81 }
82 }
83 }
84 }
85
86 if !improved {
87 step *= 0.5;
89 if step < config.tolerance {
90 break;
91 }
92 }
93 }
94
95 Ok(OptimizationResult {
96 parameters: params,
97 cost: best_cost,
98 iterations,
99 })
100}
101use std::f64::consts::PI;
103
104fn decompose_svd(
118 matrix: &Array2<Complex64>,
119) -> Result<(Array2<Complex64>, Array1<f64>, Array2<Complex64>), QuantRS2Error> {
120 let (nrows, ncols) = matrix.dim();
121 let min_dim = nrows.min(ncols);
122
123 if nrows == 0 || ncols == 0 {
124 return Ok((
125 Array2::zeros((nrows, min_dim)),
126 Array1::zeros(min_dim),
127 Array2::zeros((min_dim, ncols)),
128 ));
129 }
130
131 let m_dag = matrix.t().mapv(|z| z.conj());
133 let gram = m_dag.dot(matrix);
134
135 let eig = crate::eigensolve::eigen_decompose_unitary(&gram, 1e-12)?;
139
140 let mut order: Vec<usize> = (0..eig.eigenvalues.len()).collect();
142 order.sort_by(|&i, &j| {
143 let si = eig.eigenvalues[j].re.max(0.0).sqrt();
144 let sj = eig.eigenvalues[i].re.max(0.0).sqrt();
145 si.total_cmp(&sj)
146 });
147
148 let mut singular_values = Array1::<f64>::zeros(min_dim);
149 let mut v_right = Array2::<Complex64>::zeros((ncols, min_dim));
150 let mut u_left = Array2::<Complex64>::zeros((nrows, min_dim));
151
152 for (out_idx, &col) in order.iter().take(min_dim).enumerate() {
153 let sigma = eig.eigenvalues[col].re.max(0.0).sqrt();
154 singular_values[out_idx] = sigma;
155
156 let v_k = eig.eigenvectors.column(col).to_owned();
158 for r in 0..ncols {
159 v_right[[r, out_idx]] = v_k[r];
160 }
161
162 if sigma > 1e-12 {
164 let mv = matrix.dot(&v_k);
165 for r in 0..nrows {
166 u_left[[r, out_idx]] = mv[r] / Complex64::new(sigma, 0.0);
167 }
168 }
169 }
170
171 complete_orthonormal_columns(&mut u_left, &singular_values);
174
175 let vt = v_right.t().mapv(|z| z.conj());
177
178 Ok((u_left, singular_values, vt))
179}
180
181fn complete_orthonormal_columns(u: &mut Array2<Complex64>, singular_values: &Array1<f64>) {
184 let nrows = u.nrows();
185 let ncols = u.ncols();
186 let mut next_basis = 0usize;
187
188 for col in 0..ncols {
189 if singular_values[col] > 1e-12 {
190 continue;
191 }
192 while next_basis < nrows {
194 let mut candidate = Array1::<Complex64>::zeros(nrows);
195 candidate[next_basis] = Complex64::new(1.0, 0.0);
196 next_basis += 1;
197
198 for prev in 0..ncols {
200 if prev == col {
201 continue;
202 }
203 let prev_col = u.column(prev).to_owned();
204 let norm_sq: f64 = prev_col.iter().map(|z| z.norm_sqr()).sum();
205 if norm_sq < 1e-24 {
206 continue;
207 }
208 let proj: Complex64 = prev_col
209 .iter()
210 .zip(candidate.iter())
211 .map(|(p, c)| p.conj() * c)
212 .sum();
213 for r in 0..nrows {
214 candidate[r] -= proj * prev_col[r];
215 }
216 }
217
218 let norm: f64 = candidate.iter().map(|z| z.norm_sqr()).sum::<f64>().sqrt();
219 if norm > 1e-10 {
220 for r in 0..nrows {
221 u[[r, col]] = candidate[r] / Complex64::new(norm, 0.0);
222 }
223 break;
224 }
225 }
226 }
227}
228
229#[derive(Debug, Clone)]
231pub struct QuantumNaturalGradient {
232 pub fisher_information: Array2<f64>,
233 pub gradient: Array1<f64>,
234 pub regularization: f64,
235}
236
237impl QuantumNaturalGradient {
238 pub fn new(parameter_count: usize, regularization: f64) -> Self {
240 Self {
241 fisher_information: Array2::eye(parameter_count),
242 gradient: Array1::zeros(parameter_count),
243 regularization,
244 }
245 }
246
247 pub fn compute_fisher_information(
249 &mut self,
250 circuit_generator: impl Fn(&Array1<f64>) -> Result<Array2<Complex64>, QuantRS2Error>,
251 parameters: &Array1<f64>,
252 state: &Array1<Complex64>,
253 ) -> Result<(), QuantRS2Error> {
254 let n_params = parameters.len();
255 let eps = 1e-8;
256
257 for i in 0..n_params {
259 for j in i..n_params {
260 let mut params_plus = parameters.clone();
261 let mut params_minus = parameters.clone();
262 params_plus[i] += eps;
263 params_minus[i] -= eps;
264
265 let circuit_plus = circuit_generator(¶ms_plus)?;
266 let circuit_minus = circuit_generator(¶ms_minus)?;
267
268 let state_plus = circuit_plus.dot(state);
269 let state_minus = circuit_minus.dot(state);
270
271 let overlap = state_plus.dot(&state_minus.mapv(|x| x.conj()));
273 let fisher_element = 4.0 * (1.0 - overlap.norm_sqr());
274
275 self.fisher_information[[i, j]] = fisher_element;
276 if i != j {
277 self.fisher_information[[j, i]] = fisher_element;
278 }
279 }
280 }
281
282 for i in 0..n_params {
284 self.fisher_information[[i, i]] += self.regularization;
285 }
286
287 Ok(())
288 }
289
290 pub fn natural_gradient(&self) -> Result<Array1<f64>, QuantRS2Error> {
292 let n = self.fisher_information.nrows();
295 let mut fisher_inv = Array2::eye(n);
296 for i in 0..n {
297 let diag_val = self.fisher_information[[i, i]];
298 if diag_val.abs() > 1e-10 {
299 fisher_inv[[i, i]] = 1.0 / diag_val;
300 }
301 }
302 Ok(fisher_inv.dot(&self.gradient))
303 }
304
305 pub fn update_parameters(
307 &self,
308 parameters: &Array1<f64>,
309 learning_rate: f64,
310 ) -> Result<Array1<f64>, QuantRS2Error> {
311 let nat_grad = self.natural_gradient()?;
312 Ok(parameters - learning_rate * &nat_grad)
313 }
314}
315
316#[derive(Debug, Clone)]
318pub struct ParameterShiftOptimizer {
319 pub shift_value: f64,
320 pub higher_order_shifts: Vec<f64>,
321 pub use_finite_differences: bool,
322}
323
324impl Default for ParameterShiftOptimizer {
325 fn default() -> Self {
326 Self {
327 shift_value: PI / 2.0,
328 higher_order_shifts: vec![PI / 2.0, PI, 3.0 * PI / 2.0],
329 use_finite_differences: false,
330 }
331 }
332}
333
334impl ParameterShiftOptimizer {
335 pub fn compute_gradient(
337 &self,
338 expectation_fn: impl Fn(&Array1<f64>) -> Result<f64, QuantRS2Error>,
339 parameters: &Array1<f64>,
340 ) -> Result<Array1<f64>, QuantRS2Error> {
341 let n_params = parameters.len();
342 let mut gradient = Array1::zeros(n_params);
343
344 for i in 0..n_params {
345 if self.use_finite_differences {
346 gradient[i] = self.finite_difference_gradient(&expectation_fn, parameters, i)?;
347 } else {
348 gradient[i] = self.parameter_shift_gradient(&expectation_fn, parameters, i)?;
349 }
350 }
351
352 Ok(gradient)
353 }
354
355 fn parameter_shift_gradient(
357 &self,
358 expectation_fn: &impl Fn(&Array1<f64>) -> Result<f64, QuantRS2Error>,
359 parameters: &Array1<f64>,
360 param_idx: usize,
361 ) -> Result<f64, QuantRS2Error> {
362 let mut params_plus = parameters.clone();
363 let mut params_minus = parameters.clone();
364
365 params_plus[param_idx] += self.shift_value;
366 params_minus[param_idx] -= self.shift_value;
367
368 let exp_plus = expectation_fn(¶ms_plus)?;
369 let exp_minus = expectation_fn(¶ms_minus)?;
370
371 Ok((exp_plus - exp_minus) / 2.0)
372 }
373
374 fn finite_difference_gradient(
376 &self,
377 expectation_fn: &impl Fn(&Array1<f64>) -> Result<f64, QuantRS2Error>,
378 parameters: &Array1<f64>,
379 param_idx: usize,
380 ) -> Result<f64, QuantRS2Error> {
381 let eps = 1e-7;
382 let mut params_plus = parameters.clone();
383 let mut params_minus = parameters.clone();
384
385 params_plus[param_idx] += eps;
386 params_minus[param_idx] -= eps;
387
388 let exp_plus = expectation_fn(¶ms_plus)?;
389 let exp_minus = expectation_fn(¶ms_minus)?;
390
391 Ok((exp_plus - exp_minus) / (2.0 * eps))
392 }
393
394 pub fn higher_order_gradient(
396 &self,
397 expectation_fn: impl Fn(&Array1<f64>) -> Result<f64, QuantRS2Error>,
398 parameters: &Array1<f64>,
399 param_idx: usize,
400 ) -> Result<f64, QuantRS2Error> {
401 let mut gradient = 0.0;
402 let weights = [0.5, -0.5, 0.0, 0.0]; for (i, &shift) in self.higher_order_shifts.iter().enumerate() {
405 if i < weights.len() {
406 let mut params = parameters.clone();
407 params[param_idx] += shift;
408 let expectation = expectation_fn(¶ms)?;
409 gradient += weights[i] * expectation;
410 }
411 }
412
413 Ok(gradient)
414 }
415}
416
417#[derive(Debug, Clone)]
419pub struct QuantumKernelOptimizer {
420 pub feature_map: QuantumFeatureMap,
421 pub kernel_matrix: Array2<f64>,
422 pub optimization_history: Vec<f64>,
423 pub feature_map_parameters: Array1<f64>,
424}
425
426#[derive(Debug, Clone)]
427pub enum QuantumFeatureMap {
428 ZZFeatureMap { num_qubits: usize, depth: usize },
429 PauliFeatureMap { paulis: Vec<String>, depth: usize },
430 CustomFeatureMap { gates: Vec<Box<dyn GateOp>> },
431}
432
433impl QuantumKernelOptimizer {
434 pub fn new(feature_map: QuantumFeatureMap) -> Self {
436 Self {
437 feature_map,
438 kernel_matrix: Array2::zeros((1, 1)),
439 optimization_history: Vec::new(),
440 feature_map_parameters: Array1::zeros(4), }
442 }
443
444 pub fn compute_kernel_matrix(
446 &mut self,
447 data_points: &Array2<f64>,
448 ) -> Result<Array2<f64>, QuantRS2Error> {
449 let n_samples = data_points.nrows();
450 let mut kernel_matrix = Array2::zeros((n_samples, n_samples));
451
452 for i in 0..n_samples {
453 for j in i..n_samples {
454 let x_i = data_points.row(i);
455 let x_j = data_points.row(j);
456 let kernel_value = self.compute_kernel_element(&x_i, &x_j)?;
457
458 kernel_matrix[[i, j]] = kernel_value;
459 kernel_matrix[[j, i]] = kernel_value;
460 }
461 }
462
463 self.kernel_matrix.clone_from(&kernel_matrix);
464 Ok(kernel_matrix)
465 }
466
467 fn compute_kernel_element(
469 &self,
470 x_i: &scirs2_core::ndarray::ArrayView1<f64>,
471 x_j: &scirs2_core::ndarray::ArrayView1<f64>,
472 ) -> Result<f64, QuantRS2Error> {
473 let circuit_i = self.create_feature_circuit(&x_i.to_owned())?;
474 let circuit_j = self.create_feature_circuit(&x_j.to_owned())?;
475
476 let overlap = circuit_i.t().dot(&circuit_j);
478 Ok(overlap.diag().map(|x| x.norm_sqr()).sum())
479 }
480
481 fn create_feature_circuit(
483 &self,
484 data_point: &Array1<f64>,
485 ) -> Result<Array2<Complex64>, QuantRS2Error> {
486 match &self.feature_map {
487 QuantumFeatureMap::ZZFeatureMap { num_qubits, depth } => {
488 self.create_zz_feature_map(data_point, *num_qubits, *depth)
489 }
490 QuantumFeatureMap::PauliFeatureMap { paulis, depth } => {
491 self.create_pauli_feature_map(data_point, paulis, *depth)
492 }
493 QuantumFeatureMap::CustomFeatureMap { gates: _ } => {
494 Ok(Array2::eye(2_usize.pow(data_point.len() as u32)))
496 }
497 }
498 }
499
500 fn create_zz_feature_map(
502 &self,
503 data_point: &Array1<f64>,
504 num_qubits: usize,
505 depth: usize,
506 ) -> Result<Array2<Complex64>, QuantRS2Error> {
507 let dim = 2_usize.pow(num_qubits as u32);
508 let mut circuit = Array2::eye(dim);
509
510 for layer in 0..depth {
511 for qubit in 0..num_qubits {
513 let angle = data_point[qubit % data_point.len()] * (layer + 1) as f64;
514 let rotation = self.ry_gate(angle);
515 circuit = self.apply_single_qubit_gate(&circuit, &rotation, qubit, num_qubits)?;
516 }
517
518 for qubit in 0..num_qubits - 1 {
520 let angle = data_point[qubit % data_point.len()]
521 * data_point[(qubit + 1) % data_point.len()];
522 let zz_gate = self.zz_gate(angle);
523 circuit =
524 self.apply_two_qubit_gate(&circuit, &zz_gate, qubit, qubit + 1, num_qubits)?;
525 }
526 }
527
528 Ok(circuit)
529 }
530
531 fn create_pauli_feature_map(
533 &self,
534 data_point: &Array1<f64>,
535 paulis: &[String],
536 depth: usize,
537 ) -> Result<Array2<Complex64>, QuantRS2Error> {
538 let num_qubits = paulis.len();
539 let dim = 2_usize.pow(num_qubits as u32);
540 let mut circuit = Array2::eye(dim);
541
542 for _layer in 0..depth {
543 for (i, pauli_string) in paulis.iter().enumerate() {
544 let angle = data_point[i % data_point.len()];
545 let pauli_rotation = self.pauli_rotation(pauli_string, angle)?;
546 circuit = circuit.dot(&pauli_rotation);
547 }
548 }
549
550 Ok(circuit)
551 }
552
553 fn ry_gate(&self, angle: f64) -> Array2<Complex64> {
555 let cos_half = (angle / 2.0).cos();
556 let sin_half = (angle / 2.0).sin();
557
558 scirs2_core::ndarray::array![
559 [
560 Complex64::new(cos_half, 0.0),
561 Complex64::new(-sin_half, 0.0)
562 ],
563 [Complex64::new(sin_half, 0.0), Complex64::new(cos_half, 0.0)]
564 ]
565 }
566
567 fn zz_gate(&self, angle: f64) -> Array2<Complex64> {
569 let exp_factor = Complex64::from_polar(1.0, angle / 2.0);
570
571 scirs2_core::ndarray::array![
572 [
573 exp_factor.conj(),
574 Complex64::new(0.0, 0.0),
575 Complex64::new(0.0, 0.0),
576 Complex64::new(0.0, 0.0)
577 ],
578 [
579 Complex64::new(0.0, 0.0),
580 exp_factor,
581 Complex64::new(0.0, 0.0),
582 Complex64::new(0.0, 0.0)
583 ],
584 [
585 Complex64::new(0.0, 0.0),
586 Complex64::new(0.0, 0.0),
587 exp_factor,
588 Complex64::new(0.0, 0.0)
589 ],
590 [
591 Complex64::new(0.0, 0.0),
592 Complex64::new(0.0, 0.0),
593 Complex64::new(0.0, 0.0),
594 exp_factor.conj()
595 ]
596 ]
597 }
598
599 fn pauli_rotation(
601 &self,
602 pauli_string: &str,
603 angle: f64,
604 ) -> Result<Array2<Complex64>, QuantRS2Error> {
605 let cos_half = (angle / 2.0).cos();
607 let sin_half = (angle / 2.0).sin();
608 match pauli_string {
609 "X" => Ok(scirs2_core::ndarray::array![
610 [
611 Complex64::new(cos_half, 0.0),
612 Complex64::new(0.0, -sin_half)
613 ],
614 [
615 Complex64::new(0.0, -sin_half),
616 Complex64::new(cos_half, 0.0)
617 ]
618 ]),
619 "Y" => Ok(scirs2_core::ndarray::array![
620 [
621 Complex64::new(cos_half, 0.0),
622 Complex64::new(-sin_half, 0.0)
623 ],
624 [Complex64::new(sin_half, 0.0), Complex64::new(cos_half, 0.0)]
625 ]),
626 "Z" => Ok(scirs2_core::ndarray::array![
627 [
628 Complex64::new(cos_half, -sin_half),
629 Complex64::new(0.0, 0.0)
630 ],
631 [Complex64::new(0.0, 0.0), Complex64::new(cos_half, sin_half)]
632 ]),
633 _ => Err(QuantRS2Error::InvalidGateOp(format!(
634 "Unknown Pauli string: {pauli_string}"
635 ))),
636 }
637 }
638
639 fn pauli_x(&self) -> Array2<Complex64> {
641 scirs2_core::ndarray::array![
642 [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)],
643 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]
644 ]
645 }
646
647 fn pauli_y(&self) -> Array2<Complex64> {
648 scirs2_core::ndarray::array![
649 [Complex64::new(0.0, 0.0), Complex64::new(0.0, -1.0)],
650 [Complex64::new(0.0, 1.0), Complex64::new(0.0, 0.0)]
651 ]
652 }
653
654 fn pauli_z(&self) -> Array2<Complex64> {
655 scirs2_core::ndarray::array![
656 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
657 [Complex64::new(0.0, 0.0), Complex64::new(-1.0, 0.0)]
658 ]
659 }
660
661 fn apply_single_qubit_gate(
663 &self,
664 circuit: &Array2<Complex64>,
665 gate: &Array2<Complex64>,
666 target_qubit: usize,
667 num_qubits: usize,
668 ) -> Result<Array2<Complex64>, QuantRS2Error> {
669 let mut full_gate = Array2::eye(2_usize.pow(num_qubits as u32));
670
671 for i in 0..num_qubits {
673 let local_gate = if i == target_qubit {
674 gate.clone()
675 } else {
676 Array2::eye(2)
677 };
678
679 if i == 0 {
680 full_gate = local_gate;
681 } else {
682 let gate_matrix = DenseMatrix::new(local_gate)?;
683 let full_gate_matrix = DenseMatrix::new(full_gate)?;
684 full_gate = full_gate_matrix.tensor_product(&gate_matrix)?;
685 }
686 }
687
688 Ok(circuit.dot(&full_gate))
689 }
690
691 fn apply_two_qubit_gate(
693 &self,
694 circuit: &Array2<Complex64>,
695 gate: &Array2<Complex64>,
696 _control: usize,
697 _target: usize,
698 _num_qubits: usize,
699 ) -> Result<Array2<Complex64>, QuantRS2Error> {
700 Ok(circuit.dot(gate))
703 }
704
705 pub fn optimize_kernel_parameters(
707 &mut self,
708 training_data: &Array2<f64>,
709 training_labels: &Array1<f64>,
710 ) -> Result<OptimizationResult, QuantRS2Error> {
711 let _training_data_clone = training_data.clone();
713 let _training_labels_clone = training_labels.clone();
714
715 let objective = |params: &Array1<f64>| -> Result<f64, String> {
716 let loss = params.iter().map(|x| x * x).sum::<f64>();
718 Ok(loss)
719 };
720
721 let initial_params = Array1::ones(4); let config = OptimizationConfig::default();
723
724 let result = minimize(objective, &initial_params, &config).map_err(|e| {
725 QuantRS2Error::OptimizationFailed(format!("Kernel optimization failed: {e:?}"))
726 })?;
727
728 self.feature_map_parameters.clone_from(&result.parameters);
730
731 Ok(result)
732 }
733
734 const fn update_feature_map_parameters(&self, _params: &Array1<f64>) {
736 }
739
740 fn compute_classification_loss(&self, kernel: &Array2<f64>, labels: &Array1<f64>) -> f64 {
742 let n = labels.len();
744 let mut loss = 0.0;
745
746 for i in 0..n {
747 for j in 0..n {
748 loss += labels[i] * labels[j] * kernel[[i, j]];
749 }
750 }
751
752 -loss / (n as f64)
753 }
754}
755
756#[derive(Debug, Clone)]
758pub struct HardwareEfficientMLLayer {
759 pub num_qubits: usize,
760 pub num_layers: usize,
761 pub parameters: Array1<f64>,
762 pub entanglement_pattern: EntanglementPattern,
763}
764
765#[derive(Debug, Clone)]
766pub enum EntanglementPattern {
767 Linear,
768 Circular,
769 AllToAll,
770 Custom(Vec<(usize, usize)>),
771}
772
773impl HardwareEfficientMLLayer {
774 pub fn new(
776 num_qubits: usize,
777 num_layers: usize,
778 entanglement_pattern: EntanglementPattern,
779 ) -> Self {
780 let num_params = num_qubits * num_layers * 3; Self {
782 num_qubits,
783 num_layers,
784 parameters: Array1::zeros(num_params),
785 entanglement_pattern,
786 }
787 }
788
789 pub fn initialize_parameters(&mut self, rng: &mut impl scirs2_core::random::Rng) {
791 use scirs2_core::random::prelude::*;
792 for param in &mut self.parameters {
793 *param = rng.random_range(-PI..PI);
794 }
795 }
796
797 pub fn build_circuit(&self) -> Result<Array2<Complex64>, QuantRS2Error> {
799 let dim = 2_usize.pow(self.num_qubits as u32);
800 let mut circuit = Array2::eye(dim);
801
802 let mut param_idx = 0;
803 for layer in 0..self.num_layers {
804 for qubit in 0..self.num_qubits {
806 let rx_angle = self.parameters[param_idx];
807 let ry_angle = self.parameters[param_idx + 1];
808 let rz_angle = self.parameters[param_idx + 2];
809 param_idx += 3;
810
811 let rotation_gates = self.create_rotation_sequence(rx_angle, ry_angle, rz_angle);
813 circuit = self.apply_rotation_to_circuit(&circuit, &rotation_gates, qubit)?;
814 }
815
816 if layer < self.num_layers - 1 {
818 circuit = self.apply_entanglement_layer(&circuit)?;
819 }
820 }
821
822 Ok(circuit)
823 }
824
825 fn create_rotation_sequence(&self, rx: f64, ry: f64, rz: f64) -> Vec<Array2<Complex64>> {
827 vec![self.rx_gate(rx), self.ry_gate(ry), self.rz_gate(rz)]
828 }
829
830 fn rx_gate(&self, angle: f64) -> Array2<Complex64> {
832 let cos_half = (angle / 2.0).cos();
833 let sin_half = (angle / 2.0).sin();
834
835 scirs2_core::ndarray::array![
836 [
837 Complex64::new(cos_half, 0.0),
838 Complex64::new(0.0, -sin_half)
839 ],
840 [
841 Complex64::new(0.0, -sin_half),
842 Complex64::new(cos_half, 0.0)
843 ]
844 ]
845 }
846
847 fn ry_gate(&self, angle: f64) -> Array2<Complex64> {
849 let cos_half = (angle / 2.0).cos();
850 let sin_half = (angle / 2.0).sin();
851
852 scirs2_core::ndarray::array![
853 [
854 Complex64::new(cos_half, 0.0),
855 Complex64::new(-sin_half, 0.0)
856 ],
857 [Complex64::new(sin_half, 0.0), Complex64::new(cos_half, 0.0)]
858 ]
859 }
860
861 fn rz_gate(&self, angle: f64) -> Array2<Complex64> {
863 let exp_factor = Complex64::from_polar(1.0, angle / 2.0);
864
865 scirs2_core::ndarray::array![
866 [exp_factor.conj(), Complex64::new(0.0, 0.0)],
867 [Complex64::new(0.0, 0.0), exp_factor]
868 ]
869 }
870
871 fn apply_rotation_to_circuit(
873 &self,
874 circuit: &Array2<Complex64>,
875 rotations: &[Array2<Complex64>],
876 qubit: usize,
877 ) -> Result<Array2<Complex64>, QuantRS2Error> {
878 let mut result = circuit.clone();
879 for rotation in rotations {
880 let full_gate = self.create_single_qubit_gate(rotation, qubit)?;
882 result = result.dot(&full_gate);
883 }
884 Ok(result)
885 }
886
887 fn create_single_qubit_gate(
889 &self,
890 gate: &Array2<Complex64>,
891 target_qubit: usize,
892 ) -> Result<Array2<Complex64>, QuantRS2Error> {
893 let dim = 2_usize.pow(self.num_qubits as u32);
894 let mut full_gate = Array2::eye(dim);
895
896 for i in 0..dim {
898 let target_bit = (i >> target_qubit) & 1;
899 if target_bit == 0 {
900 let j = i | (1 << target_qubit);
901 if j < dim {
902 full_gate[[i, i]] = gate[[0, 0]];
903 full_gate[[j, i]] = gate[[1, 0]];
904 }
905 } else {
906 let j = i & !(1 << target_qubit);
907 if j < dim {
908 full_gate[[j, i]] = gate[[0, 1]];
909 full_gate[[i, i]] = gate[[1, 1]];
910 }
911 }
912 }
913
914 Ok(full_gate)
915 }
916
917 fn apply_entanglement_layer(
919 &self,
920 circuit: &Array2<Complex64>,
921 ) -> Result<Array2<Complex64>, QuantRS2Error> {
922 let mut result = circuit.clone();
923
924 let entangling_pairs = match &self.entanglement_pattern {
925 EntanglementPattern::Linear => (0..self.num_qubits - 1).map(|i| (i, i + 1)).collect(),
926 EntanglementPattern::Circular => {
927 let mut pairs: Vec<(usize, usize)> =
928 (0..self.num_qubits - 1).map(|i| (i, i + 1)).collect();
929 if self.num_qubits > 2 {
930 pairs.push((self.num_qubits - 1, 0));
931 }
932 pairs
933 }
934 EntanglementPattern::AllToAll => {
935 let mut pairs = Vec::new();
936 for i in 0..self.num_qubits {
937 for j in i + 1..self.num_qubits {
938 pairs.push((i, j));
939 }
940 }
941 pairs
942 }
943 EntanglementPattern::Custom(pairs) => pairs.clone(),
944 };
945
946 for (control, target) in entangling_pairs {
947 let cnot = self.cnot_gate();
948 result = self.apply_cnot_to_circuit(&result, &cnot, control, target)?;
949 }
950
951 Ok(result)
952 }
953
954 fn cnot_gate(&self) -> Array2<Complex64> {
956 scirs2_core::ndarray::array![
957 [
958 Complex64::new(1.0, 0.0),
959 Complex64::new(0.0, 0.0),
960 Complex64::new(0.0, 0.0),
961 Complex64::new(0.0, 0.0)
962 ],
963 [
964 Complex64::new(0.0, 0.0),
965 Complex64::new(1.0, 0.0),
966 Complex64::new(0.0, 0.0),
967 Complex64::new(0.0, 0.0)
968 ],
969 [
970 Complex64::new(0.0, 0.0),
971 Complex64::new(0.0, 0.0),
972 Complex64::new(0.0, 0.0),
973 Complex64::new(1.0, 0.0)
974 ],
975 [
976 Complex64::new(0.0, 0.0),
977 Complex64::new(0.0, 0.0),
978 Complex64::new(1.0, 0.0),
979 Complex64::new(0.0, 0.0)
980 ]
981 ]
982 }
983
984 fn apply_cnot_to_circuit(
986 &self,
987 circuit: &Array2<Complex64>,
988 cnot: &Array2<Complex64>,
989 _control: usize,
990 _target: usize,
991 ) -> Result<Array2<Complex64>, QuantRS2Error> {
992 Ok(circuit.dot(cnot))
994 }
995
996 pub fn expectation_value(
998 &self,
999 observable: &Array2<Complex64>,
1000 input_state: &Array1<Complex64>,
1001 ) -> Result<f64, QuantRS2Error> {
1002 let circuit = self.build_circuit()?;
1003 let output_state = circuit.dot(input_state);
1004 let expectation = output_state.t().dot(&observable.dot(&output_state));
1005 Ok(expectation.re)
1006 }
1007
1008 pub fn update_parameters(&mut self, gradient: &Array1<f64>, learning_rate: f64) {
1010 self.parameters = &self.parameters - learning_rate * gradient;
1011 }
1012}
1013
1014#[derive(Debug)]
1016pub struct TensorNetworkMLAccelerator {
1017 pub tensor_network: TensorNetwork,
1018 pub bond_dimensions: Vec<usize>,
1019 pub contraction_order: Vec<usize>,
1020}
1021
1022impl TensorNetworkMLAccelerator {
1023 pub fn new(num_qubits: usize, max_bond_dimension: usize) -> Self {
1025 let network = TensorNetwork::new();
1026 let bond_dimensions = vec![max_bond_dimension; num_qubits];
1027
1028 Self {
1029 tensor_network: network,
1030 bond_dimensions,
1031 contraction_order: (0..num_qubits).collect(),
1032 }
1033 }
1034
1035 pub fn decompose_circuit(&mut self, circuit: &Array2<Complex64>) -> Result<(), QuantRS2Error> {
1037 let (u, s, vt) = decompose_svd(circuit)?;
1039
1040 let tensor_u = Tensor::from_array(u, vec![0, 1]);
1042 let s_complex: Array2<Complex64> = s
1043 .diag()
1044 .insert_axis(Axis(1))
1045 .mapv(|x| Complex64::new(x, 0.0))
1046 .to_owned();
1047 let tensor_s = Tensor::from_array(s_complex, vec![1, 2]);
1048 let tensor_vt = Tensor::from_array(vt, vec![2, 3]);
1049
1050 self.tensor_network.add_tensor(tensor_u);
1052 self.tensor_network.add_tensor(tensor_s);
1053 self.tensor_network.add_tensor(tensor_vt);
1054
1055 Ok(())
1056 }
1057
1058 pub fn optimize_contraction(&mut self) -> Result<(), QuantRS2Error> {
1060 let n_tensors = self.tensor_network.tensors().len();
1062
1063 if n_tensors <= 1 {
1064 return Ok(());
1065 }
1066
1067 self.contraction_order = (0..n_tensors).collect();
1069 self.contraction_order
1070 .sort_by_key(|&i| self.tensor_network.tensors()[i].tensor().ndim());
1071
1072 Ok(())
1073 }
1074
1075 pub fn contract_network(&self) -> Result<Array2<Complex64>, QuantRS2Error> {
1077 if self.tensor_network.tensors().is_empty() {
1078 return Err(QuantRS2Error::TensorNetwork(
1079 "Empty tensor network".to_string(),
1080 ));
1081 }
1082
1083 let first_tensor = &self.tensor_network.tensors()[0];
1085 let result = first_tensor.tensor().clone();
1086
1087 let sqrt_dim = (result.len() as f64).sqrt() as usize;
1089 if sqrt_dim * sqrt_dim != result.len() {
1090 return Err(QuantRS2Error::TensorNetwork(
1091 "Invalid tensor dimensions".to_string(),
1092 ));
1093 }
1094
1095 Ok(result
1096 .into_shape_with_order((sqrt_dim, sqrt_dim))
1097 .map_err(|e| QuantRS2Error::TensorNetwork(format!("Shape error: {e}")))?)
1098 }
1099
1100 pub fn complexity_estimate(&self) -> (usize, usize) {
1102 let time_complexity = self.bond_dimensions.iter().product();
1103 let space_complexity = self.bond_dimensions.iter().sum();
1104 (time_complexity, space_complexity)
1105 }
1106}
1107
1108#[cfg(test)]
1109mod tests {
1110 use super::*;
1111 use scirs2_core::ndarray::array;
1112 use scirs2_core::random::prelude::*;
1113
1114 #[test]
1115 fn test_quantum_natural_gradient() {
1116 let mut qng = QuantumNaturalGradient::new(2, 1e-6);
1117 let params = array![PI / 4.0, PI / 3.0];
1118 let state = array![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)];
1119
1120 let circuit_gen =
1121 |_p: &Array1<f64>| -> Result<Array2<Complex64>, QuantRS2Error> { Ok(Array2::eye(2)) };
1122
1123 let result = qng.compute_fisher_information(circuit_gen, ¶ms, &state);
1124 assert!(result.is_ok());
1125 }
1126
1127 #[test]
1128 fn test_parameter_shift_optimizer() {
1129 let optimizer = ParameterShiftOptimizer::default();
1130 let params = array![PI / 4.0, PI / 3.0];
1131
1132 let expectation_fn =
1133 |p: &Array1<f64>| -> Result<f64, QuantRS2Error> { Ok(p[0].cos() + p[1].sin()) };
1134
1135 let gradient = optimizer.compute_gradient(expectation_fn, ¶ms);
1136 assert!(gradient.is_ok());
1137 assert_eq!(
1138 gradient.expect("gradient computation should succeed").len(),
1139 2
1140 );
1141 }
1142
1143 #[test]
1144 fn test_quantum_kernel_optimizer() {
1145 let feature_map = QuantumFeatureMap::ZZFeatureMap {
1146 num_qubits: 2,
1147 depth: 1,
1148 };
1149 let mut optimizer = QuantumKernelOptimizer::new(feature_map);
1150
1151 let data = array![[0.1, 0.2], [0.3, 0.4]];
1152 let result = optimizer.compute_kernel_matrix(&data);
1153
1154 assert!(result.is_ok());
1155 let kernel = result.expect("kernel matrix computation should succeed");
1156 assert_eq!(kernel.shape(), &[2, 2]);
1157 }
1158
1159 #[test]
1160 fn test_hardware_efficient_ml_layer() {
1161 let mut layer = HardwareEfficientMLLayer::new(2, 2, EntanglementPattern::Linear);
1162
1163 let mut rng = thread_rng();
1164 layer.initialize_parameters(&mut rng);
1165
1166 let circuit = layer.build_circuit();
1167 assert!(circuit.is_ok());
1168
1169 let observable = array![
1170 [
1171 Complex64::new(1.0, 0.0),
1172 Complex64::new(0.0, 0.0),
1173 Complex64::new(0.0, 0.0),
1174 Complex64::new(0.0, 0.0)
1175 ],
1176 [
1177 Complex64::new(0.0, 0.0),
1178 Complex64::new(-1.0, 0.0),
1179 Complex64::new(0.0, 0.0),
1180 Complex64::new(0.0, 0.0)
1181 ],
1182 [
1183 Complex64::new(0.0, 0.0),
1184 Complex64::new(0.0, 0.0),
1185 Complex64::new(-1.0, 0.0),
1186 Complex64::new(0.0, 0.0)
1187 ],
1188 [
1189 Complex64::new(0.0, 0.0),
1190 Complex64::new(0.0, 0.0),
1191 Complex64::new(0.0, 0.0),
1192 Complex64::new(1.0, 0.0)
1193 ]
1194 ];
1195 let state = array![
1196 Complex64::new(1.0, 0.0),
1197 Complex64::new(0.0, 0.0),
1198 Complex64::new(0.0, 0.0),
1199 Complex64::new(0.0, 0.0)
1200 ];
1201
1202 let expectation = layer.expectation_value(&observable, &state);
1203 assert!(expectation.is_ok());
1204 }
1205
1206 #[test]
1207 fn test_decompose_svd_reconstructs_matrix() {
1208 let m = array![
1212 [Complex64::new(3.0, 0.0), Complex64::new(0.0, 0.0)],
1213 [Complex64::new(4.0, 0.0), Complex64::new(5.0, 0.0)]
1214 ];
1215
1216 let (u, s, vt) = decompose_svd(&m).expect("SVD should succeed");
1217
1218 assert_eq!(s.len(), 2);
1220 assert!(
1221 (s[0] - 45.0_f64.sqrt()).abs() < 1e-9,
1222 "largest singular value: got {}",
1223 s[0]
1224 );
1225 assert!(
1226 (s[1] - 5.0_f64.sqrt()).abs() < 1e-9,
1227 "smallest singular value: got {}",
1228 s[1]
1229 );
1230 assert!(s[0] >= s[1], "singular values must be descending");
1231
1232 let mut sigma = Array2::<Complex64>::zeros((s.len(), s.len()));
1234 for (i, &sv) in s.iter().enumerate() {
1235 sigma[[i, i]] = Complex64::new(sv, 0.0);
1236 }
1237 let reconstructed = u.dot(&sigma).dot(&vt);
1238 for ((r, c), expected) in m.indexed_iter() {
1239 let got = reconstructed[[r, c]];
1240 assert!(
1241 (got - expected).norm() < 1e-9,
1242 "reconstruction[{r},{c}] = {got:?}, expected {expected:?}"
1243 );
1244 }
1245
1246 let identity = Array2::<Complex64>::eye(2);
1249 let diff_from_identity: f64 = u
1250 .iter()
1251 .zip(identity.iter())
1252 .map(|(a, b)| (a - b).norm_sqr())
1253 .sum();
1254 assert!(
1255 diff_from_identity > 1e-6,
1256 "U must not be the identity for a non-diagonal input"
1257 );
1258
1259 let u_dag = u.t().mapv(|z| z.conj());
1261 let utu = u_dag.dot(&u);
1262 for i in 0..2 {
1263 for j in 0..2 {
1264 let expected = if i == j { 1.0 } else { 0.0 };
1265 assert!(
1266 (utu[[i, j]].re - expected).abs() < 1e-9 && utu[[i, j]].im.abs() < 1e-9,
1267 "UᴴU not identity at [{i},{j}]: {:?}",
1268 utu[[i, j]]
1269 );
1270 }
1271 }
1272 }
1273
1274 #[test]
1275 fn test_minimize_actually_reduces_cost() {
1276 let objective =
1278 |p: &Array1<f64>| -> Result<f64, String> { Ok(p.iter().map(|x| x * x).sum::<f64>()) };
1279 let initial = array![1.5, -2.0, 0.75];
1280 let initial_cost = objective(&initial).expect("eval");
1281 let config = OptimizationConfig::default();
1282
1283 let result = minimize(objective, &initial, &config).expect("minimize");
1284
1285 assert!(
1286 result.cost < initial_cost,
1287 "optimiser must reduce cost: {} !< {}",
1288 result.cost,
1289 initial_cost
1290 );
1291 assert!(
1293 result.cost < 1e-2,
1294 "optimiser should converge near the minimum, got cost {}",
1295 result.cost
1296 );
1297 assert!(result.iterations > 1, "optimiser must actually iterate");
1299 let moved: f64 = result
1301 .parameters
1302 .iter()
1303 .zip(initial.iter())
1304 .map(|(a, b)| (a - b).abs())
1305 .sum();
1306 assert!(
1307 moved > 1e-6,
1308 "parameters must move away from the initial guess"
1309 );
1310 }
1311
1312 #[test]
1313 fn test_tensor_network_ml_accelerator() {
1314 let mut accelerator = TensorNetworkMLAccelerator::new(2, 4);
1315
1316 let circuit = array![
1317 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
1318 [Complex64::new(0.0, 0.0), Complex64::new(-1.0, 0.0)]
1319 ];
1320
1321 let result = accelerator.decompose_circuit(&circuit);
1322 assert!(result.is_ok());
1323
1324 let optimization = accelerator.optimize_contraction();
1325 assert!(optimization.is_ok());
1326
1327 let (time_comp, space_comp) = accelerator.complexity_estimate();
1328 assert!(time_comp > 0);
1329 assert!(space_comp > 0);
1330 }
1331}