1use crate::builder::Circuit;
7use crate::dag::{circuit_to_dag, CircuitDag, DagNode};
8use quantrs2_core::{
10 error::{QuantRS2Error, QuantRS2Result},
11 gate::GateOp,
12 qubit::QubitId,
13};
14use scirs2_core::ndarray::{Array2, ArrayView2};
15use scirs2_core::Complex64;
16use scirs2_linalg::svd;
17use std::collections::{HashMap, HashSet};
18use std::f64::consts::PI;
19
20type C64 = Complex64;
22
23#[derive(Debug, Clone)]
25pub struct Tensor {
26 pub data: Vec<C64>,
28 pub shape: Vec<usize>,
30 pub indices: Vec<String>,
32}
33
34impl Tensor {
35 #[must_use]
37 pub fn new(data: Vec<C64>, shape: Vec<usize>, indices: Vec<String>) -> Self {
38 assert_eq!(shape.len(), indices.len());
39 let total_size: usize = shape.iter().product();
40 assert_eq!(data.len(), total_size);
41
42 Self {
43 data,
44 shape,
45 indices,
46 }
47 }
48
49 #[must_use]
51 pub fn identity(dim: usize, in_label: String, out_label: String) -> Self {
52 let mut data = vec![C64::new(0.0, 0.0); dim * dim];
53 for i in 0..dim {
54 data[i * dim + i] = C64::new(1.0, 0.0);
55 }
56
57 Self::new(data, vec![dim, dim], vec![in_label, out_label])
58 }
59
60 #[must_use]
62 pub fn rank(&self) -> usize {
63 self.shape.len()
64 }
65
66 #[must_use]
68 pub fn size(&self) -> usize {
69 self.data.len()
70 }
71
72 pub fn contract(&self, other: &Self, self_idx: &str, other_idx: &str) -> QuantRS2Result<Self> {
74 let self_pos = self
76 .indices
77 .iter()
78 .position(|s| s == self_idx)
79 .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Index {self_idx} not found")))?;
80 let other_pos = other
81 .indices
82 .iter()
83 .position(|s| s == other_idx)
84 .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Index {other_idx} not found")))?;
85
86 if self.shape[self_pos] != other.shape[other_pos] {
88 return Err(QuantRS2Error::InvalidInput(format!(
89 "Dimension mismatch: {} vs {}",
90 self.shape[self_pos], other.shape[other_pos]
91 )));
92 }
93
94 let mut new_shape = Vec::new();
96 let mut new_indices = Vec::new();
97
98 for (i, (dim, idx)) in self.shape.iter().zip(&self.indices).enumerate() {
99 if i != self_pos {
100 new_shape.push(*dim);
101 new_indices.push(idx.clone());
102 }
103 }
104
105 for (i, (dim, idx)) in other.shape.iter().zip(&other.indices).enumerate() {
106 if i != other_pos {
107 new_shape.push(*dim);
108 new_indices.push(idx.clone());
109 }
110 }
111
112 let new_size: usize = new_shape.iter().product();
127 let mut new_data = vec![C64::new(0.0, 0.0); new_size];
128 let contract_dim = self.shape[self_pos];
129
130 let self_strides = Self::row_major_strides(&self.shape);
132 let other_strides = Self::row_major_strides(&other.shape);
133
134 let self_free: Vec<(usize, usize)> = self
136 .shape
137 .iter()
138 .enumerate()
139 .filter(|(i, _)| *i != self_pos)
140 .map(|(i, &dim)| (self_strides[i], dim))
141 .collect();
142 let other_free: Vec<(usize, usize)> = other
143 .shape
144 .iter()
145 .enumerate()
146 .filter(|(i, _)| *i != other_pos)
147 .map(|(i, &dim)| (other_strides[i], dim))
148 .collect();
149
150 let self_contract_stride = self_strides[self_pos];
151 let other_contract_stride = other_strides[other_pos];
152
153 let self_free_count: usize = self_free.iter().map(|(_, dim)| *dim).product();
155 let other_free_count: usize = other_free.iter().map(|(_, dim)| *dim).product();
156
157 for self_free_idx in 0..self_free_count {
161 let self_base = Self::flat_offset(self_free_idx, &self_free);
162 for other_free_idx in 0..other_free_count {
163 let other_base = Self::flat_offset(other_free_idx, &other_free);
164
165 let mut acc = C64::new(0.0, 0.0);
166 for k in 0..contract_dim {
167 let self_flat = self_base + k * self_contract_stride;
168 let other_flat = other_base + k * other_contract_stride;
169 acc += self.data[self_flat] * other.data[other_flat];
170 }
171
172 let out_flat = self_free_idx * other_free_count + other_free_idx;
173 new_data[out_flat] = acc;
174 }
175 }
176
177 Ok(Self::new(new_data, new_shape, new_indices))
178 }
179
180 pub fn reshape(&mut self, new_shape: Vec<usize>) -> QuantRS2Result<()> {
182 let new_size: usize = new_shape.iter().product();
183 if new_size != self.size() {
184 return Err(QuantRS2Error::InvalidInput(format!(
185 "Cannot reshape {} elements to shape {:?}",
186 self.size(),
187 new_shape
188 )));
189 }
190
191 self.shape = new_shape;
192 Ok(())
193 }
194
195 fn row_major_strides(shape: &[usize]) -> Vec<usize> {
201 let mut strides = vec![1usize; shape.len()];
202 for i in (0..shape.len().saturating_sub(1)).rev() {
203 strides[i] = strides[i + 1] * shape[i + 1];
204 }
205 strides
206 }
207
208 fn flat_offset(mut linear: usize, free_axes: &[(usize, usize)]) -> usize {
215 let mut offset = 0usize;
216 for &(stride, extent) in free_axes.iter().rev() {
217 let coord = linear % extent;
218 linear /= extent;
219 offset += coord * stride;
220 }
221 offset
222 }
223}
224
225#[derive(Debug)]
227pub struct TensorNetwork {
228 tensors: Vec<Tensor>,
230 bonds: Vec<(usize, String, usize, String)>,
232 open_indices: HashMap<String, (usize, usize)>, }
235
236impl Default for TensorNetwork {
237 fn default() -> Self {
238 Self::new()
239 }
240}
241
242impl TensorNetwork {
243 #[must_use]
245 pub fn new() -> Self {
246 Self {
247 tensors: Vec::new(),
248 bonds: Vec::new(),
249 open_indices: HashMap::new(),
250 }
251 }
252
253 pub fn add_tensor(&mut self, tensor: Tensor) -> usize {
255 let idx = self.tensors.len();
256
257 for (pos, index) in tensor.indices.iter().enumerate() {
259 self.open_indices.insert(index.clone(), (idx, pos));
260 }
261
262 self.tensors.push(tensor);
263 idx
264 }
265
266 pub fn add_bond(
268 &mut self,
269 t1: usize,
270 idx1: String,
271 t2: usize,
272 idx2: String,
273 ) -> QuantRS2Result<()> {
274 if t1 >= self.tensors.len() || t2 >= self.tensors.len() {
275 return Err(QuantRS2Error::InvalidInput(
276 "Tensor index out of range".to_string(),
277 ));
278 }
279
280 self.open_indices.remove(&idx1);
282 self.open_indices.remove(&idx2);
283
284 self.bonds.push((t1, idx1, t2, idx2));
285 Ok(())
286 }
287
288 pub fn contract_all(&self) -> QuantRS2Result<Tensor> {
290 if self.tensors.is_empty() {
291 return Err(QuantRS2Error::InvalidInput(
292 "Empty tensor network".to_string(),
293 ));
294 }
295
296 let mut result = self.tensors[0].clone();
299
300 for bond in &self.bonds {
301 let (t1, idx1, t2, idx2) = bond;
302 if *t1 == 0 {
303 result = result.contract(&self.tensors[*t2], idx1, idx2)?;
304 }
305 }
306
307 Ok(result)
308 }
309
310 pub fn compress(&mut self, max_bond_dim: usize, tolerance: f64) -> QuantRS2Result<()> {
318 let bond_indices: Vec<usize> = (0..self.bonds.len()).collect();
321
322 for bond_idx in bond_indices {
323 let (t1_idx, ref idx1, t2_idx, ref idx2) = self.bonds[bond_idx].clone();
324
325 if t1_idx >= self.tensors.len() || t2_idx >= self.tensors.len() {
326 continue;
327 }
328
329 let rows = self.tensors[t1_idx].size();
332 let cols = self.tensors[t2_idx].size();
333
334 if rows == 0 || cols == 0 {
335 continue;
336 }
337
338 let mut mat_data = Vec::with_capacity(rows * cols);
340 for i in 0..rows {
341 let a = self.tensors[t1_idx].data[i];
342 for j in 0..cols {
343 let b = self.tensors[t2_idx].data[j];
344 mat_data.push(a.re * b.re + a.im * b.im);
346 }
347 }
348
349 let mat = Array2::from_shape_vec((rows, cols), mat_data).map_err(|e| {
350 QuantRS2Error::RuntimeError(format!("SVD matrix build failed: {e}"))
351 })?;
352
353 let svd_result = svd(&mat.view(), false, None).map_err(|e| {
355 QuantRS2Error::RuntimeError(format!("SVD failed on bond {bond_idx}: {e}"))
356 });
357
358 let (u_mat, s_vec, vt_mat) = match svd_result {
359 Ok(result) => result,
360 Err(_) => {
361 continue;
363 }
364 };
365
366 let s_total: f64 = s_vec.iter().copied().sum();
368 let mut rank = s_vec.len();
369
370 if s_total > 0.0 {
372 let mut cumulative = 0.0;
373 for (k, &sv) in s_vec.iter().enumerate() {
374 cumulative += sv / s_total;
375 if cumulative >= 1.0 - tolerance {
376 rank = k + 1;
377 break;
378 }
379 }
380 }
381
382 rank = rank.min(max_bond_dim).min(s_vec.len());
384
385 if rank == 0 {
386 rank = 1;
387 }
388
389 let mut new_t1_data: Vec<C64> = self.tensors[t1_idx].data.clone();
392 let mut new_t2_data: Vec<C64> = self.tensors[t2_idx].data.clone();
393
394 for i in 0..rows {
397 let mut proj = 0.0f64;
398 for k in 0..rank {
399 proj += u_mat[[i, k]] * s_vec[k];
400 }
401 let original_norm = (new_t1_data[i].norm_sqr() + 1e-300_f64).sqrt();
403 let scale = proj.abs() / (original_norm + 1e-300_f64);
404 new_t1_data[i] = C64::new(new_t1_data[i].re * scale, new_t1_data[i].im * scale);
405 }
406
407 for j in 0..cols {
409 let mut proj = 0.0f64;
410 for k in 0..rank {
411 proj += vt_mat[[k, j]];
412 }
413 let original_norm = (new_t2_data[j].norm_sqr() + 1e-300_f64).sqrt();
414 let scale = proj.abs() / (original_norm + 1e-300_f64);
415 new_t2_data[j] = C64::new(new_t2_data[j].re * scale, new_t2_data[j].im * scale);
416 }
417
418 self.tensors[t1_idx].data = new_t1_data;
419 self.tensors[t2_idx].data = new_t2_data;
420 }
421
422 Ok(())
423 }
424}
425
426pub struct CircuitToTensorNetwork<const N: usize> {
428 max_bond_dim: Option<usize>,
430 tolerance: f64,
432}
433
434impl<const N: usize> Default for CircuitToTensorNetwork<N> {
435 fn default() -> Self {
436 Self::new()
437 }
438}
439
440impl<const N: usize> CircuitToTensorNetwork<N> {
441 #[must_use]
443 pub const fn new() -> Self {
444 Self {
445 max_bond_dim: None,
446 tolerance: 1e-10,
447 }
448 }
449
450 #[must_use]
452 pub const fn with_max_bond_dim(mut self, dim: usize) -> Self {
453 self.max_bond_dim = Some(dim);
454 self
455 }
456
457 #[must_use]
459 pub const fn with_tolerance(mut self, tol: f64) -> Self {
460 self.tolerance = tol;
461 self
462 }
463
464 pub fn convert(&self, circuit: &Circuit<N>) -> QuantRS2Result<TensorNetwork> {
466 let mut tn = TensorNetwork::new();
467 let mut qubit_wires: HashMap<usize, String> = HashMap::new();
468
469 for i in 0..N {
471 qubit_wires.insert(i, format!("q{i}_in"));
472 }
473
474 for (gate_idx, gate) in circuit.gates().iter().enumerate() {
476 let tensor = self.gate_to_tensor(gate.as_ref(), gate_idx)?;
477 let tensor_idx = tn.add_tensor(tensor);
478
479 for qubit in gate.qubits() {
481 let q = qubit.id() as usize;
482 let prev_wire = qubit_wires
483 .get(&q)
484 .ok_or_else(|| {
485 QuantRS2Error::InvalidInput(format!("Qubit wire {q} not found"))
486 })?
487 .clone();
488 let new_wire = format!("q{q}_g{gate_idx}");
489
490 if gate_idx > 0 || prev_wire.contains("_g") {
492 tn.add_bond(
493 tensor_idx - 1,
494 prev_wire.clone(),
495 tensor_idx,
496 format!("in_{q}"),
497 )?;
498 }
499
500 qubit_wires.insert(q, new_wire);
502 }
503 }
504
505 Ok(tn)
506 }
507
508 fn gate_to_tensor(&self, gate: &dyn GateOp, gate_idx: usize) -> QuantRS2Result<Tensor> {
510 let qubits = gate.qubits();
511 let n_qubits = qubits.len();
512
513 match n_qubits {
514 1 => {
515 let matrix = self.get_single_qubit_matrix(gate)?;
517 let q = qubits[0].id() as usize;
518
519 Ok(Tensor::new(
520 matrix,
521 vec![2, 2],
522 vec![format!("in_{}", q), format!("out_{}", q)],
523 ))
524 }
525 2 => {
526 let matrix = self.get_two_qubit_matrix(gate)?;
528 let q0 = qubits[0].id() as usize;
529 let q1 = qubits[1].id() as usize;
530
531 Ok(Tensor::new(
532 matrix,
533 vec![2, 2, 2, 2],
534 vec![
535 format!("in_{}", q0),
536 format!("in_{}", q1),
537 format!("out_{}", q0),
538 format!("out_{}", q1),
539 ],
540 ))
541 }
542 _ => Err(QuantRS2Error::UnsupportedOperation(format!(
543 "{n_qubits}-qubit gates not yet supported for tensor networks"
544 ))),
545 }
546 }
547
548 fn get_single_qubit_matrix(&self, gate: &dyn GateOp) -> QuantRS2Result<Vec<C64>> {
550 match gate.name() {
552 "H" => Ok(vec![
553 C64::new(1.0 / 2.0_f64.sqrt(), 0.0),
554 C64::new(1.0 / 2.0_f64.sqrt(), 0.0),
555 C64::new(1.0 / 2.0_f64.sqrt(), 0.0),
556 C64::new(-1.0 / 2.0_f64.sqrt(), 0.0),
557 ]),
558 "X" => Ok(vec![
559 C64::new(0.0, 0.0),
560 C64::new(1.0, 0.0),
561 C64::new(1.0, 0.0),
562 C64::new(0.0, 0.0),
563 ]),
564 "Y" => Ok(vec![
565 C64::new(0.0, 0.0),
566 C64::new(0.0, -1.0),
567 C64::new(0.0, 1.0),
568 C64::new(0.0, 0.0),
569 ]),
570 "Z" => Ok(vec![
571 C64::new(1.0, 0.0),
572 C64::new(0.0, 0.0),
573 C64::new(0.0, 0.0),
574 C64::new(-1.0, 0.0),
575 ]),
576 _ => Ok(vec![
577 C64::new(1.0, 0.0),
578 C64::new(0.0, 0.0),
579 C64::new(0.0, 0.0),
580 C64::new(1.0, 0.0),
581 ]),
582 }
583 }
584
585 fn get_two_qubit_matrix(&self, gate: &dyn GateOp) -> QuantRS2Result<Vec<C64>> {
587 if gate.name() == "CNOT" {
589 let mut matrix = vec![C64::new(0.0, 0.0); 16];
590 matrix[0] = C64::new(1.0, 0.0); matrix[5] = C64::new(1.0, 0.0); matrix[15] = C64::new(1.0, 0.0); matrix[10] = C64::new(1.0, 0.0); Ok(matrix)
595 } else {
596 let mut matrix = vec![C64::new(0.0, 0.0); 16];
598 for i in 0..16 {
599 matrix[i * 16 + i] = C64::new(1.0, 0.0);
600 }
601 Ok(matrix)
602 }
603 }
604}
605
606#[derive(Debug)]
608pub struct MatrixProductState {
609 tensors: Vec<Tensor>,
611 bond_dims: Vec<usize>,
613 n_qubits: usize,
615}
616
617impl MatrixProductState {
618 pub fn from_circuit<const N: usize>(circuit: &Circuit<N>) -> QuantRS2Result<Self> {
630 if N == 0 {
631 return Ok(Self {
632 tensors: Vec::new(),
633 bond_dims: Vec::new(),
634 n_qubits: 0,
635 });
636 }
637
638 let converter = CircuitToTensorNetwork::<N>::new();
639 let mut bond_dims = vec![1usize; N.saturating_sub(1)];
641
642 let mut site_tensors: Vec<Vec<C64>> = (0..N)
645 .map(|_| {
646 vec![C64::new(1.0, 0.0), C64::new(0.0, 0.0)]
648 })
649 .collect();
650
651 let gate_to_single_mat = |g: &dyn GateOp| -> Option<[C64; 4]> {
653 match g.name() {
654 "H" => Some([
655 C64::new(1.0 / 2.0_f64.sqrt(), 0.0),
656 C64::new(1.0 / 2.0_f64.sqrt(), 0.0),
657 C64::new(1.0 / 2.0_f64.sqrt(), 0.0),
658 C64::new(-1.0 / 2.0_f64.sqrt(), 0.0),
659 ]),
660 "X" => Some([
661 C64::new(0.0, 0.0),
662 C64::new(1.0, 0.0),
663 C64::new(1.0, 0.0),
664 C64::new(0.0, 0.0),
665 ]),
666 "Y" => Some([
667 C64::new(0.0, 0.0),
668 C64::new(0.0, -1.0),
669 C64::new(0.0, 1.0),
670 C64::new(0.0, 0.0),
671 ]),
672 "Z" => Some([
673 C64::new(1.0, 0.0),
674 C64::new(0.0, 0.0),
675 C64::new(0.0, 0.0),
676 C64::new(-1.0, 0.0),
677 ]),
678 "RY" | "RZ" | "RX" | "S" | "T" | "SX" | "ID" | "I" => {
679 Some([
681 C64::new(1.0, 0.0),
682 C64::new(0.0, 0.0),
683 C64::new(0.0, 0.0),
684 C64::new(1.0, 0.0),
685 ])
686 }
687 _ => None,
688 }
689 };
690
691 let cnot_mat: [C64; 16] = {
693 let mut m = [C64::new(0.0, 0.0); 16];
694 m[0] = C64::new(1.0, 0.0); m[5] = C64::new(1.0, 0.0); m[14] = C64::new(1.0, 0.0); m[11] = C64::new(1.0, 0.0); m
699 };
700
701 let max_bd = 32usize;
703
704 for gate in circuit.gates() {
705 let qubits = gate.qubits();
706 match qubits.len() {
707 1 => {
708 let qi = qubits[0].id() as usize;
709 if qi >= N {
710 continue;
711 }
712 if let Some(u) = gate_to_single_mat(gate.as_ref()) {
713 let old = site_tensors[qi].clone();
717 let phys = old.len(); let half = phys / 2;
720 let mut new_site = vec![C64::new(0.0, 0.0); phys];
721 for alpha in 0..half {
722 let s0 = old[alpha]; let s1 = old[alpha + half]; new_site[alpha] = u[0] * s0 + u[1] * s1; new_site[alpha + half] = u[2] * s0 + u[3] * s1; }
727 site_tensors[qi] = new_site;
728 }
729 }
730 2 => {
731 let qi = qubits[0].id() as usize;
732 let qj = qubits[1].id() as usize;
733 if qi >= N || qj >= N || qj != qi + 1 {
735 continue;
736 }
737 let gate_name = gate.name();
738 let unitary_mat: [C64; 16] = if gate_name == "CNOT" || gate_name == "CX" {
739 cnot_mat
740 } else {
741 let mut id = [C64::new(0.0, 0.0); 16];
743 id[0] = C64::new(1.0, 0.0);
744 id[5] = C64::new(1.0, 0.0);
745 id[10] = C64::new(1.0, 0.0);
746 id[15] = C64::new(1.0, 0.0);
747 id
748 };
749
750 let left = &site_tensors[qi];
753 let right = &site_tensors[qj];
754 let left_phys = left.len(); let right_phys = right.len(); let chi_m = bond_dims.get(qi).copied().unwrap_or(1);
757 let chi_l = left_phys / 2; let chi_r = right_phys / 2; let nrows = chi_l * 2;
763 let ncols = chi_r * 2;
764 let mut theta = vec![C64::new(0.0, 0.0); nrows * ncols];
765
766 for sigma_i in 0..2usize {
770 for alpha in 0..chi_l {
771 let l_val = left
772 .get(sigma_i * chi_l + alpha)
773 .copied()
774 .unwrap_or(C64::new(0.0, 0.0));
775 for sigma_j in 0..2usize {
776 for beta in 0..chi_r {
777 let r_val = right
778 .get(sigma_j * chi_r + beta)
779 .copied()
780 .unwrap_or(C64::new(0.0, 0.0));
781 let row = alpha * 2 + sigma_i;
782 let col = sigma_j * chi_r + beta;
783 if row < nrows && col < ncols {
784 theta[row * ncols + col] += l_val * r_val;
785 }
786 }
787 }
788 }
789 }
790
791 let mut theta_prime = vec![C64::new(0.0, 0.0); nrows * ncols];
795 for alpha in 0..chi_l {
796 for sigma_i_out in 0..2usize {
797 for sigma_j_out in 0..2usize {
798 for beta in 0..chi_r {
799 let row_out = alpha * 2 + sigma_i_out;
800 let col_out = sigma_j_out * chi_r + beta;
801 let mut val = C64::new(0.0, 0.0);
802 for sigma_i_in in 0..2usize {
803 for sigma_j_in in 0..2usize {
804 let u_idx = (sigma_i_out * 2 + sigma_j_out) * 4
805 + sigma_i_in * 2
806 + sigma_j_in;
807 let u_val = unitary_mat
808 .get(u_idx)
809 .copied()
810 .unwrap_or(C64::new(0.0, 0.0));
811 let row_in = alpha * 2 + sigma_i_in;
812 let col_in = sigma_j_in * chi_r + beta;
813 val += u_val
814 * theta
815 .get(row_in * ncols + col_in)
816 .copied()
817 .unwrap_or(C64::new(0.0, 0.0));
818 }
819 }
820 if row_out < nrows && col_out < ncols {
821 theta_prime[row_out * ncols + col_out] = val;
822 }
823 }
824 }
825 }
826 }
827
828 let real_mat_data: Vec<f64> = theta_prime.iter().map(|c| c.re).collect();
830 let real_mat =
831 Array2::from_shape_vec((nrows, ncols), real_mat_data).map_err(|e| {
832 QuantRS2Error::RuntimeError(format!("MPS matrix reshape failed: {e}"))
833 })?;
834
835 let svd_res = svd(&real_mat.view(), false, None)
836 .map_err(|e| QuantRS2Error::RuntimeError(format!("MPS SVD failed: {e}")));
837
838 let (u_mat, s_vec, vt_mat) = match svd_res {
839 Ok(r) => r,
840 Err(_) => {
841 continue;
843 }
844 };
845
846 let new_chi_m = s_vec.len().min(max_bd);
848
849 let mut new_left = vec![C64::new(0.0, 0.0); chi_l * 2 * new_chi_m];
852 for row in 0..nrows {
853 for k in 0..new_chi_m {
854 let sv = s_vec[k].max(0.0).sqrt();
855 let idx = row * new_chi_m + k;
856 new_left[idx] = C64::new(u_mat[[row, k]] * sv, 0.0);
857 }
858 }
859
860 let mut new_right = vec![C64::new(0.0, 0.0); new_chi_m * chi_r * 2];
863 for k in 0..new_chi_m {
864 let sv = s_vec[k].max(0.0).sqrt();
865 for col in 0..ncols {
866 let idx = k * ncols + col;
867 new_right[idx] = C64::new(vt_mat[[k, col]] * sv, 0.0);
868 }
869 }
870
871 site_tensors[qi] = new_left;
872 site_tensors[qj] = new_right;
873 if qi < bond_dims.len() {
874 bond_dims[qi] = new_chi_m;
875 }
876 }
877 _ => {
878 }
880 }
881 }
882
883 let tensors: Vec<Tensor> = site_tensors
885 .into_iter()
886 .enumerate()
887 .map(|(i, data)| {
888 let chi_l = if i == 0 { 1 } else { bond_dims[i - 1] };
889 let chi_r = if i + 1 < N { bond_dims[i] } else { 1 };
890 let shape = vec![chi_l, 2, chi_r];
891 let indices = vec![
892 format!("bond_left_{i}"),
893 format!("phys_{i}"),
894 format!("bond_right_{i}"),
895 ];
896 let expected = chi_l * 2 * chi_r;
898 let mut padded = data;
899 padded.resize(expected, C64::new(0.0, 0.0));
900 Tensor::new(padded, shape, indices)
901 })
902 .collect();
903
904 Ok(Self {
905 tensors,
906 bond_dims,
907 n_qubits: N,
908 })
909 }
910
911 pub fn compress(&mut self, max_bond_dim: usize, tolerance: f64) -> QuantRS2Result<()> {
921 let n = self.n_qubits;
922 if n <= 1 {
923 return Ok(());
924 }
925
926 for i in 0..(n - 1) {
927 if i + 1 >= self.tensors.len() {
928 break;
929 }
930
931 let chi_l_i = self.tensors[i].shape.first().copied().unwrap_or(1);
932 let chi_r_i = self.tensors[i].shape.get(2).copied().unwrap_or(1); let chi_r_j = self.tensors[i + 1].shape.get(2).copied().unwrap_or(1);
934
935 let nrows = chi_l_i * 2;
936 let ncols = chi_r_j * 2;
937
938 let left = &self.tensors[i].data;
941 let right = &self.tensors[i + 1].data;
942 let mut theta_real = vec![0.0f64; nrows * ncols];
943
944 for alpha in 0..chi_l_i {
945 for sigma_i in 0..2usize {
946 for m in 0..chi_r_i {
947 let l_idx = (alpha * 2 + sigma_i) * chi_r_i + m;
948 let l_val = left.get(l_idx).map(|c| c.re).unwrap_or(0.0);
949 if l_val == 0.0 {
950 continue;
951 }
952 for sigma_j in 0..2usize {
953 for beta in 0..chi_r_j {
954 let r_idx = (m * 2 + sigma_j) * chi_r_j + beta;
955 let r_val = right.get(r_idx).map(|c| c.re).unwrap_or(0.0);
956 let row = alpha * 2 + sigma_i;
957 let col = sigma_j * chi_r_j + beta;
958 if row < nrows && col < ncols {
959 theta_real[row * ncols + col] += l_val * r_val;
960 }
961 }
962 }
963 }
964 }
965 }
966
967 let mat = Array2::from_shape_vec((nrows, ncols), theta_real).map_err(|e| {
968 QuantRS2Error::RuntimeError(format!("MPS compress reshape failed: {e}"))
969 })?;
970
971 let svd_res = svd(&mat.view(), false, None).map_err(|e| {
972 QuantRS2Error::RuntimeError(format!("MPS compress SVD failed at bond {i}: {e}"))
973 });
974
975 let (u_mat, s_vec, vt_mat) = match svd_res {
976 Ok(r) => r,
977 Err(_) => continue,
978 };
979
980 let sigma_max = s_vec.first().copied().unwrap_or(0.0);
982 let rank = if sigma_max > 0.0 {
983 s_vec
984 .iter()
985 .take_while(|&&sv| sv / sigma_max > tolerance)
986 .count()
987 } else {
988 1
989 };
990 let new_chi_m = rank.min(max_bond_dim).min(s_vec.len()).max(1);
991
992 let new_left_size = chi_l_i * 2 * new_chi_m;
994 let mut new_left = vec![C64::new(0.0, 0.0); new_left_size];
995 for row in 0..(chi_l_i * 2) {
996 for k in 0..new_chi_m {
997 let sv = s_vec[k].max(0.0).sqrt();
998 let flat_idx = row * new_chi_m + k;
999 new_left[flat_idx] = C64::new(u_mat[[row, k]] * sv, 0.0);
1000 }
1001 }
1002
1003 let new_right_size = new_chi_m * 2 * chi_r_j;
1005 let mut new_right = vec![C64::new(0.0, 0.0); new_right_size];
1006 for k in 0..new_chi_m {
1007 let sv = s_vec[k].max(0.0).sqrt();
1008 for col in 0..(2 * chi_r_j) {
1009 let flat_idx = k * 2 * chi_r_j + col;
1010 new_right[flat_idx] = C64::new(vt_mat[[k, col]] * sv, 0.0);
1011 }
1012 }
1013
1014 self.tensors[i].data = new_left;
1016 self.tensors[i].shape = vec![chi_l_i, 2, new_chi_m];
1017
1018 self.tensors[i + 1].data = new_right;
1019 self.tensors[i + 1].shape = vec![new_chi_m, 2, chi_r_j];
1020
1021 if i < self.bond_dims.len() {
1022 self.bond_dims[i] = new_chi_m;
1023 }
1024 }
1025
1026 Ok(())
1027 }
1028
1029 pub fn overlap(&self, other: &Self) -> QuantRS2Result<C64> {
1031 if self.n_qubits != other.n_qubits {
1032 return Err(QuantRS2Error::InvalidInput(
1033 "MPS have different number of qubits".to_string(),
1034 ));
1035 }
1036
1037 Ok(C64::new(1.0, 0.0)) }
1040
1041 pub const fn expectation_value(&self, observable: &TensorNetwork) -> QuantRS2Result<f64> {
1043 Ok(0.0) }
1046}
1047
1048pub struct TensorNetworkCompressor {
1050 max_bond_dim: usize,
1052 tolerance: f64,
1054 method: CompressionMethod,
1056}
1057
1058#[derive(Debug, Clone)]
1059pub enum CompressionMethod {
1060 SVD,
1062 DMRG,
1064 TEBD,
1066}
1067
1068impl TensorNetworkCompressor {
1069 #[must_use]
1071 pub const fn new(max_bond_dim: usize) -> Self {
1072 Self {
1073 max_bond_dim,
1074 tolerance: 1e-10,
1075 method: CompressionMethod::SVD,
1076 }
1077 }
1078
1079 #[must_use]
1081 pub const fn with_method(mut self, method: CompressionMethod) -> Self {
1082 self.method = method;
1083 self
1084 }
1085
1086 pub fn compress<const N: usize>(
1088 &self,
1089 circuit: &Circuit<N>,
1090 ) -> QuantRS2Result<CompressedCircuit<N>> {
1091 let mps = MatrixProductState::from_circuit(circuit)?;
1092
1093 Ok(CompressedCircuit {
1094 mps,
1095 original_gates: circuit.num_gates(),
1096 compression_ratio: 1.0, })
1098 }
1099}
1100
1101#[derive(Debug)]
1103pub struct CompressedCircuit<const N: usize> {
1104 mps: MatrixProductState,
1106 original_gates: usize,
1108 compression_ratio: f64,
1110}
1111
1112impl<const N: usize> CompressedCircuit<N> {
1113 #[must_use]
1115 pub const fn compression_ratio(&self) -> f64 {
1116 self.compression_ratio
1117 }
1118
1119 pub fn decompress(&self) -> QuantRS2Result<Circuit<N>> {
1121 Ok(Circuit::<N>::new())
1124 }
1125
1126 pub const fn fidelity(&self, original: &Circuit<N>) -> QuantRS2Result<f64> {
1128 Ok(0.99) }
1131}
1132
1133#[cfg(test)]
1134mod tests {
1135 use super::*;
1136 use quantrs2_core::gate::single::Hadamard;
1137
1138 #[test]
1139 fn test_tensor_creation() {
1140 let data = vec![
1141 C64::new(1.0, 0.0),
1142 C64::new(0.0, 0.0),
1143 C64::new(0.0, 0.0),
1144 C64::new(1.0, 0.0),
1145 ];
1146 let tensor = Tensor::new(data, vec![2, 2], vec!["in".to_string(), "out".to_string()]);
1147
1148 assert_eq!(tensor.rank(), 2);
1149 assert_eq!(tensor.size(), 4);
1150 }
1151
1152 #[test]
1153 fn test_contract_matrix_product() {
1154 let a = Tensor::new(
1156 vec![
1157 C64::new(1.0, 0.0),
1158 C64::new(2.0, 0.0),
1159 C64::new(3.0, 0.0),
1160 C64::new(4.0, 0.0),
1161 ],
1162 vec![2, 2],
1163 vec!["i".to_string(), "k".to_string()],
1164 );
1165 let b = Tensor::new(
1167 vec![
1168 C64::new(5.0, 0.0),
1169 C64::new(6.0, 0.0),
1170 C64::new(7.0, 0.0),
1171 C64::new(8.0, 0.0),
1172 ],
1173 vec![2, 2],
1174 vec!["k".to_string(), "j".to_string()],
1175 );
1176
1177 let result = a.contract(&b, "k", "k").expect("contraction must succeed");
1179
1180 assert_eq!(result.shape, vec![2, 2]);
1181 assert_eq!(result.indices, vec!["i".to_string(), "j".to_string()]);
1182
1183 let expected = [19.0, 22.0, 43.0, 50.0];
1184 for (got, want) in result.data.iter().zip(expected.iter()) {
1185 assert!(
1186 (got.re - want).abs() < 1e-12,
1187 "got {}, want {}",
1188 got.re,
1189 want
1190 );
1191 assert!(got.im.abs() < 1e-12);
1192 }
1193 }
1194
1195 #[test]
1196 fn test_contract_rectangular_strides() {
1197 let a = Tensor::new(
1199 (1..=6).map(|v| C64::new(f64::from(v), 0.0)).collect(),
1200 vec![2, 3],
1201 vec!["i".to_string(), "k".to_string()],
1202 );
1203 let b = Tensor::new(
1205 (7..=12).map(|v| C64::new(f64::from(v), 0.0)).collect(),
1206 vec![3, 2],
1207 vec!["k".to_string(), "j".to_string()],
1208 );
1209
1210 let result = a.contract(&b, "k", "k").expect("contraction must succeed");
1211 assert_eq!(result.shape, vec![2, 2]);
1212
1213 let expected = [58.0, 64.0, 139.0, 154.0];
1217 for (got, want) in result.data.iter().zip(expected.iter()) {
1218 assert!(
1219 (got.re - want).abs() < 1e-12,
1220 "got {}, want {}",
1221 got.re,
1222 want
1223 );
1224 }
1225 }
1226
1227 #[test]
1228 fn test_tensor_network() {
1229 let mut tn = TensorNetwork::new();
1230
1231 let t1 = Tensor::identity(2, "a".to_string(), "b".to_string());
1232 let t2 = Tensor::identity(2, "c".to_string(), "d".to_string());
1233
1234 let idx1 = tn.add_tensor(t1);
1235 let idx2 = tn.add_tensor(t2);
1236
1237 tn.add_bond(idx1, "b".to_string(), idx2, "c".to_string())
1238 .expect("Failed to add bond between tensors");
1239
1240 assert_eq!(tn.tensors.len(), 2);
1241 assert_eq!(tn.bonds.len(), 1);
1242 }
1243
1244 #[test]
1245 fn test_circuit_to_tensor_network() {
1246 let mut circuit = Circuit::<2>::new();
1247 circuit
1248 .add_gate(Hadamard { target: QubitId(0) })
1249 .expect("Failed to add Hadamard gate");
1250
1251 let converter = CircuitToTensorNetwork::<2>::new();
1252 let tn = converter
1253 .convert(&circuit)
1254 .expect("Failed to convert circuit to tensor network");
1255
1256 assert!(!tn.tensors.is_empty());
1257 }
1258
1259 #[test]
1260 fn test_compression() {
1261 let circuit = Circuit::<2>::new();
1262 let compressor = TensorNetworkCompressor::new(32);
1263
1264 let compressed = compressor
1265 .compress(&circuit)
1266 .expect("Failed to compress circuit");
1267 assert!(compressed.compression_ratio() <= 1.0);
1268 }
1269
1270 #[test]
1271 fn test_tensor_network_svd_compress() {
1272 use quantrs2_core::gate::multi::CNOT;
1273
1274 let mut circuit = Circuit::<2>::new();
1276 circuit
1277 .add_gate(Hadamard { target: QubitId(0) })
1278 .expect("H gate");
1279 circuit
1280 .add_gate(CNOT {
1281 control: QubitId(0),
1282 target: QubitId(1),
1283 })
1284 .expect("CNOT gate");
1285
1286 let converter = CircuitToTensorNetwork::<2>::new();
1287 let mut tn = converter.convert(&circuit).expect("Convert to TN");
1288
1289 tn.compress(4, 1e-6).expect("TN compress");
1291 assert_eq!(tn.tensors.len(), 2);
1293 }
1294
1295 #[test]
1296 fn test_mps_from_circuit_trivial() {
1297 let circuit = Circuit::<2>::new();
1299 let mps = MatrixProductState::from_circuit(&circuit).expect("MPS from empty circuit");
1300 assert_eq!(mps.n_qubits, 2);
1301 assert_eq!(mps.tensors.len(), 2);
1302 }
1303
1304 #[test]
1305 fn test_mps_from_circuit_with_hadamard() {
1306 use quantrs2_core::gate::single::Hadamard;
1307
1308 let mut circuit = Circuit::<3>::new();
1309 circuit
1310 .add_gate(Hadamard { target: QubitId(0) })
1311 .expect("H gate");
1312
1313 let mps = MatrixProductState::from_circuit(&circuit).expect("MPS from H circuit");
1314 assert_eq!(mps.n_qubits, 3);
1315 assert_eq!(mps.tensors.len(), 3);
1316 }
1317
1318 #[test]
1319 fn test_mps_compress_reduces_bond_dim() {
1320 use quantrs2_core::gate::multi::CNOT;
1321
1322 let mut circuit = Circuit::<2>::new();
1324 circuit
1325 .add_gate(Hadamard { target: QubitId(0) })
1326 .expect("H gate");
1327 circuit
1328 .add_gate(CNOT {
1329 control: QubitId(0),
1330 target: QubitId(1),
1331 })
1332 .expect("CNOT gate");
1333
1334 let mut mps = MatrixProductState::from_circuit(&circuit).expect("MPS from Bell circuit");
1335
1336 mps.compress(1, 1e-10).expect("MPS compress");
1338 for &bd in &mps.bond_dims {
1340 assert!(bd <= 1, "Bond dim {} exceeds max", bd);
1341 }
1342 }
1343}