1use crate::{
7 error::{QuantRS2Error, QuantRS2Result},
8 gate::GateOp,
9 linalg_stubs::svd,
10 register::Register,
11};
12use scirs2_core::ndarray::{Array, Array2, ArrayD, IxDyn};
13use scirs2_core::Complex;
14use std::collections::{HashMap, HashSet};
16
17type Complex64 = Complex<f64>;
19
20#[derive(Debug, Clone)]
22pub struct Tensor {
23 pub id: usize,
25 pub data: ArrayD<Complex64>,
27 pub indices: Vec<String>,
29 pub shape: Vec<usize>,
31}
32
33impl Tensor {
34 pub fn new(id: usize, data: ArrayD<Complex64>, indices: Vec<String>) -> Self {
36 let shape = data.shape().to_vec();
37 Self {
38 id,
39 data,
40 indices,
41 shape,
42 }
43 }
44
45 pub fn from_matrix(
47 id: usize,
48 matrix: Array2<Complex64>,
49 in_idx: String,
50 out_idx: String,
51 ) -> Self {
52 let shape = matrix.shape().to_vec();
53 let data = matrix.into_dyn();
54 Self {
55 id,
56 data,
57 indices: vec![in_idx, out_idx],
58 shape,
59 }
60 }
61
62 pub fn qubit_zero(id: usize, idx: String) -> Self {
64 let mut data = Array::zeros(IxDyn(&[2]));
65 data[[0]] = Complex64::new(1.0, 0.0);
66 Self {
67 id,
68 data,
69 indices: vec![idx],
70 shape: vec![2],
71 }
72 }
73
74 pub fn qubit_one(id: usize, idx: String) -> Self {
76 let mut data = Array::zeros(IxDyn(&[2]));
77 data[[1]] = Complex64::new(1.0, 0.0);
78 Self {
79 id,
80 data,
81 indices: vec![idx],
82 shape: vec![2],
83 }
84 }
85
86 pub fn from_array<D>(
88 array: scirs2_core::ndarray::ArrayBase<scirs2_core::ndarray::OwnedRepr<Complex64>, D>,
89 indices: Vec<usize>,
90 ) -> Self
91 where
92 D: scirs2_core::ndarray::Dimension,
93 {
94 let shape = array.shape().to_vec();
95 let data = array.into_dyn();
96 let index_labels: Vec<String> = indices.iter().map(|i| format!("idx_{i}")).collect();
97 Self {
98 id: 0, data,
100 indices: index_labels,
101 shape,
102 }
103 }
104
105 pub fn rank(&self) -> usize {
107 self.indices.len()
108 }
109
110 pub const fn tensor(&self) -> &ArrayD<Complex64> {
112 &self.data
113 }
114
115 pub fn ndim(&self) -> usize {
117 self.data.ndim()
118 }
119
120 pub fn contract(&self, other: &Self, self_idx: &str, other_idx: &str) -> QuantRS2Result<Self> {
122 let self_pos = self
124 .indices
125 .iter()
126 .position(|s| s == self_idx)
127 .ok_or_else(|| {
128 QuantRS2Error::InvalidInput(format!("Index {self_idx} not found in tensor"))
129 })?;
130 let other_pos = other
131 .indices
132 .iter()
133 .position(|s| s == other_idx)
134 .ok_or_else(|| {
135 QuantRS2Error::InvalidInput(format!("Index {other_idx} not found in tensor"))
136 })?;
137
138 if self.shape[self_pos] != other.shape[other_pos] {
140 return Err(QuantRS2Error::InvalidInput(format!(
141 "Cannot contract indices with different dimensions: {} vs {}",
142 self.shape[self_pos], other.shape[other_pos]
143 )));
144 }
145
146 let contracted = self.contract_indices(&other, self_pos, other_pos)?;
148
149 let mut new_indices = Vec::new();
151 for (i, idx) in self.indices.iter().enumerate() {
152 if i != self_pos {
153 new_indices.push(idx.clone());
154 }
155 }
156 for (i, idx) in other.indices.iter().enumerate() {
157 if i != other_pos {
158 new_indices.push(idx.clone());
159 }
160 }
161
162 Ok(Self::new(
163 self.id.max(other.id) + 1,
164 contracted,
165 new_indices,
166 ))
167 }
168
169 fn contract_indices(
171 &self,
172 other: &Self,
173 self_idx: usize,
174 other_idx: usize,
175 ) -> QuantRS2Result<ArrayD<Complex64>> {
176 let self_shape = self.data.shape();
178 let other_shape = other.data.shape();
179
180 let mut self_left_dims = 1;
182 let mut self_right_dims = 1;
183 for i in 0..self_idx {
184 self_left_dims *= self_shape[i];
185 }
186 for i in (self_idx + 1)..self_shape.len() {
187 self_right_dims *= self_shape[i];
188 }
189
190 let mut other_left_dims = 1;
191 let mut other_right_dims = 1;
192 for i in 0..other_idx {
193 other_left_dims *= other_shape[i];
194 }
195 for i in (other_idx + 1)..other_shape.len() {
196 other_right_dims *= other_shape[i];
197 }
198
199 let contract_dim = self_shape[self_idx];
200
201 let self_mat = self
203 .data
204 .view()
205 .into_shape_with_order((self_left_dims, contract_dim * self_right_dims))
206 .map_err(|e| QuantRS2Error::InvalidInput(format!("Shape error: {e}")))?
207 .to_owned();
208 let other_mat = other
209 .data
210 .view()
211 .into_shape_with_order((other_left_dims * contract_dim, other_right_dims))
212 .map_err(|e| QuantRS2Error::InvalidInput(format!("Shape error: {e}")))?
213 .to_owned();
214
215 let _result_mat: Array2<Complex64> = Array2::zeros((
217 self_left_dims * self_right_dims,
218 other_left_dims * other_right_dims,
219 ));
220
221 let mut result_vec = Vec::new();
223 for i in 0..self_left_dims {
224 for j in 0..self_right_dims {
225 for k in 0..other_left_dims {
226 for l in 0..other_right_dims {
227 let mut sum = Complex64::new(0.0, 0.0);
228 for c in 0..contract_dim {
229 sum += self_mat[[i, c * self_right_dims + j]]
233 * other_mat[[k * contract_dim + c, l]];
234 }
235 result_vec.push(sum);
236 }
237 }
238 }
239 }
240
241 let mut result_shape = Vec::new();
243 for i in 0..self_idx {
244 result_shape.push(self_shape[i]);
245 }
246 for i in (self_idx + 1)..self_shape.len() {
247 result_shape.push(self_shape[i]);
248 }
249 for i in 0..other_idx {
250 result_shape.push(other_shape[i]);
251 }
252 for i in (other_idx + 1)..other_shape.len() {
253 result_shape.push(other_shape[i]);
254 }
255
256 ArrayD::from_shape_vec(IxDyn(&result_shape), result_vec)
257 .map_err(|e| QuantRS2Error::InvalidInput(format!("Shape error: {e}")))
258 }
259
260 pub fn svd_decompose(
262 &self,
263 idx: usize,
264 max_rank: Option<usize>,
265 ) -> QuantRS2Result<(Self, Self)> {
266 if idx >= self.rank() {
267 return Err(QuantRS2Error::InvalidInput(format!(
268 "Index {} out of bounds for tensor with rank {}",
269 idx,
270 self.rank()
271 )));
272 }
273
274 let shape = self.data.shape();
276 let mut left_dim = 1;
277 let mut right_dim = 1;
278
279 for i in 0..=idx {
280 left_dim *= shape[i];
281 }
282 for i in (idx + 1)..shape.len() {
283 right_dim *= shape[i];
284 }
285
286 let matrix = self
288 .data
289 .view()
290 .into_shape_with_order((left_dim, right_dim))
291 .map_err(|e| QuantRS2Error::InvalidInput(format!("Shape error: {e}")))?
292 .to_owned();
293
294 let real_matrix = matrix.mapv(|c| c.re);
296 let (u, s, vt) = svd(&real_matrix.view(), false, None)
297 .map_err(|e| QuantRS2Error::ComputationError(format!("SVD failed: {e:?}")))?;
298
299 let rank = if let Some(max_r) = max_rank {
301 max_r.min(s.len())
302 } else {
303 s.len()
304 };
305
306 let u_trunc = u.slice(scirs2_core::ndarray::s![.., ..rank]).to_owned();
308 let s_trunc = s.slice(scirs2_core::ndarray::s![..rank]).to_owned();
309 let vt_trunc = vt.slice(scirs2_core::ndarray::s![..rank, ..]).to_owned();
310
311 let mut s_mat = Array2::zeros((rank, rank));
313 for i in 0..rank {
314 s_mat[[i, i]] = Complex64::new(s_trunc[i].sqrt(), 0.0);
315 }
316
317 let left_data = u_trunc.mapv(|x| Complex64::new(x, 0.0)).dot(&s_mat);
319 let right_data = s_mat.dot(&vt_trunc.mapv(|x| Complex64::new(x, 0.0)));
320
321 let mut left_indices = self.indices[..=idx].to_vec();
323 left_indices.push(format!("bond_{}", self.id));
324
325 let mut right_indices = vec![format!("bond_{}", self.id)];
326 right_indices.extend_from_slice(&self.indices[(idx + 1)..]);
327
328 let left_tensor = Self::new(self.id * 2, left_data.into_dyn(), left_indices);
329
330 let right_tensor = Self::new(self.id * 2 + 1, right_data.into_dyn(), right_indices);
331
332 Ok((left_tensor, right_tensor))
333 }
334}
335
336#[derive(Debug, Clone, PartialEq, Eq, Hash)]
338pub struct TensorEdge {
339 pub tensor1: usize,
341 pub index1: String,
343 pub tensor2: usize,
345 pub index2: String,
347}
348
349#[derive(Debug)]
351pub struct TensorNetwork {
352 pub tensors: HashMap<usize, Tensor>,
354 pub edges: Vec<TensorEdge>,
356 pub open_indices: HashMap<usize, Vec<String>>,
358 next_id: usize,
360}
361
362impl TensorNetwork {
363 pub fn new() -> Self {
365 Self {
366 tensors: HashMap::new(),
367 edges: Vec::new(),
368 open_indices: HashMap::new(),
369 next_id: 0,
370 }
371 }
372
373 pub fn add_tensor(&mut self, tensor: Tensor) -> usize {
375 let id = tensor.id;
376 self.open_indices.insert(id, tensor.indices.clone());
377 self.tensors.insert(id, tensor);
378 self.next_id = self.next_id.max(id + 1);
379 id
380 }
381
382 pub fn connect(
384 &mut self,
385 tensor1: usize,
386 index1: String,
387 tensor2: usize,
388 index2: String,
389 ) -> QuantRS2Result<()> {
390 if !self.tensors.contains_key(&tensor1) {
392 return Err(QuantRS2Error::InvalidInput(format!(
393 "Tensor {tensor1} not found"
394 )));
395 }
396 if !self.tensors.contains_key(&tensor2) {
397 return Err(QuantRS2Error::InvalidInput(format!(
398 "Tensor {tensor2} not found"
399 )));
400 }
401
402 let t1 = &self.tensors[&tensor1];
404 let t2 = &self.tensors[&tensor2];
405
406 let idx1_pos = t1
407 .indices
408 .iter()
409 .position(|s| s == &index1)
410 .ok_or_else(|| {
411 QuantRS2Error::InvalidInput(format!("Index {index1} not found in tensor {tensor1}"))
412 })?;
413 let idx2_pos = t2
414 .indices
415 .iter()
416 .position(|s| s == &index2)
417 .ok_or_else(|| {
418 QuantRS2Error::InvalidInput(format!("Index {index2} not found in tensor {tensor2}"))
419 })?;
420
421 if t1.shape[idx1_pos] != t2.shape[idx2_pos] {
422 return Err(QuantRS2Error::InvalidInput(format!(
423 "Connected indices must have same dimension: {} vs {}",
424 t1.shape[idx1_pos], t2.shape[idx2_pos]
425 )));
426 }
427
428 self.edges.push(TensorEdge {
430 tensor1,
431 index1: index1.clone(),
432 tensor2,
433 index2: index2.clone(),
434 });
435
436 if let Some(indices) = self.open_indices.get_mut(&tensor1) {
438 indices.retain(|s| s != &index1);
439 }
440 if let Some(indices) = self.open_indices.get_mut(&tensor2) {
441 indices.retain(|s| s != &index2);
442 }
443
444 Ok(())
445 }
446
447 pub fn find_contraction_order(&self) -> Vec<(usize, usize)> {
449 let mut remaining_tensors: HashSet<_> = self.tensors.keys().copied().collect();
451 let mut order = Vec::new();
452
453 let mut adjacency: HashMap<usize, Vec<usize>> = HashMap::new();
455 for edge in &self.edges {
456 adjacency
457 .entry(edge.tensor1)
458 .or_insert_with(Vec::new)
459 .push(edge.tensor2);
460 adjacency
461 .entry(edge.tensor2)
462 .or_insert_with(Vec::new)
463 .push(edge.tensor1);
464 }
465
466 while remaining_tensors.len() > 1 {
467 let mut best_pair = None;
468 let mut min_cost = usize::MAX;
469
470 for &t1 in &remaining_tensors {
472 if let Some(neighbors) = adjacency.get(&t1) {
473 for &t2 in neighbors {
474 if t2 > t1 && remaining_tensors.contains(&t2) {
475 let cost = self.estimate_contraction_cost(t1, t2);
477 if cost < min_cost {
478 min_cost = cost;
479 best_pair = Some((t1, t2));
480 }
481 }
482 }
483 }
484 }
485
486 if let Some((t1, t2)) = best_pair {
487 order.push((t1, t2));
488 remaining_tensors.remove(&t1);
489 remaining_tensors.remove(&t2);
490
491 let virtual_id = self.next_id + order.len();
493 remaining_tensors.insert(virtual_id);
494
495 let mut virtual_neighbors = HashSet::new();
497 if let Some(n1) = adjacency.get(&t1) {
498 virtual_neighbors.extend(
499 n1.iter()
500 .filter(|&&n| n != t2 && remaining_tensors.contains(&n)),
501 );
502 }
503 if let Some(n2) = adjacency.get(&t2) {
504 virtual_neighbors.extend(
505 n2.iter()
506 .filter(|&&n| n != t1 && remaining_tensors.contains(&n)),
507 );
508 }
509 adjacency.insert(virtual_id, virtual_neighbors.into_iter().collect());
510 } else {
511 break;
512 }
513 }
514
515 order
516 }
517
518 const fn estimate_contraction_cost(&self, _t1: usize, _t2: usize) -> usize {
520 1000 }
524
525 pub fn contract_all(&mut self) -> QuantRS2Result<Tensor> {
527 if self.tensors.is_empty() {
528 return Err(QuantRS2Error::InvalidInput(
529 "Cannot contract empty tensor network".into(),
530 ));
531 }
532
533 if self.tensors.len() == 1 {
534 return self
535 .tensors
536 .values()
537 .next()
538 .map(|t| t.clone())
539 .ok_or_else(|| {
540 QuantRS2Error::InvalidInput("Single tensor expected but not found".into())
541 });
542 }
543
544 let order = self.find_contraction_order();
546
547 let mut tensor_map = self.tensors.clone();
549 let mut next_id = self.next_id;
550
551 for (t1_id, t2_id) in order {
552 let edge = self
554 .edges
555 .iter()
556 .find(|e| {
557 (e.tensor1 == t1_id && e.tensor2 == t2_id)
558 || (e.tensor1 == t2_id && e.tensor2 == t1_id)
559 })
560 .ok_or_else(|| QuantRS2Error::InvalidInput("Tensors not connected".into()))?;
561
562 let t1 = tensor_map
563 .remove(&t1_id)
564 .ok_or_else(|| QuantRS2Error::InvalidInput("Tensor not found".into()))?;
565 let t2 = tensor_map
566 .remove(&t2_id)
567 .ok_or_else(|| QuantRS2Error::InvalidInput("Tensor not found".into()))?;
568
569 let contracted = if edge.tensor1 == t1_id {
571 t1.contract(&t2, &edge.index1, &edge.index2)?
572 } else {
573 t1.contract(&t2, &edge.index2, &edge.index1)?
574 };
575
576 let mut new_tensor = contracted;
578 new_tensor.id = next_id;
579 tensor_map.insert(next_id, new_tensor);
580 next_id += 1;
581 }
582
583 tensor_map
585 .into_values()
586 .next()
587 .ok_or_else(|| QuantRS2Error::InvalidInput("Contraction failed".into()))
588 }
589
590 pub fn to_mps(&self, max_bond_dim: Option<usize>) -> QuantRS2Result<Vec<Tensor>> {
612 let mut work = TensorNetwork {
614 tensors: self.tensors.clone(),
615 edges: self.edges.clone(),
616 open_indices: self.open_indices.clone(),
617 next_id: self.next_id,
618 };
619 let full = work.contract_all()?;
620
621 let total: usize = full.shape.iter().product();
627 let flat: Vec<Complex64> = full.data.clone().into_raw_vec_and_offset().0;
628 if flat.len() != total {
629 return Err(QuantRS2Error::ComputationError(format!(
630 "contracted tensor buffer length {} does not match element count {total}",
631 flat.len()
632 )));
633 }
634
635 if total == 0 || (total & (total - 1)) != 0 {
637 return Err(QuantRS2Error::UnsupportedOperation(format!(
638 "MPS construction expects a qubit state (2^n amplitudes); got {total}"
639 )));
640 }
641 let n_sites = total.trailing_zeros() as usize;
642 if n_sites == 0 {
643 return Err(QuantRS2Error::InvalidInput(
644 "cannot build an MPS from a scalar (rank-0) tensor".into(),
645 ));
646 }
647 let phys_dims: Vec<usize> = vec![2usize; n_sites];
648
649 let mut mps = Vec::with_capacity(n_sites);
650
651 let mut left_bond = 1usize;
654 let mut psi = flat;
655 let mut remaining = total; for site in 0..n_sites {
658 let d = phys_dims[site];
659 remaining /= d;
660 let rows = left_bond * d;
663 let cols = remaining;
664 let mut m = Array2::<Complex64>::zeros((rows, cols));
665 for lb in 0..left_bond {
666 for phys in 0..d {
667 for rc in 0..cols {
668 let src = (lb * d + phys) * cols + rc;
670 m[[lb * d + phys, rc]] = psi[src];
671 }
672 }
673 }
674
675 if site == n_sites - 1 {
676 let right_bond = 1usize;
679 let data = Array::from_shape_vec(
681 IxDyn(&[left_bond, d, right_bond]),
682 (0..left_bond * d * right_bond)
683 .map(|idx| {
684 let lb = idx / d;
685 let phys = idx % d;
686 m[[lb * d + phys, 0]]
687 })
688 .collect(),
689 )
690 .map_err(|e| QuantRS2Error::InvalidInput(format!("Shape error: {e}")))?;
691 mps.push(Tensor::new(
692 site,
693 data,
694 vec![
695 format!("bond_{site}"),
696 format!("phys_{site}"),
697 format!("bond_{}", site + 1),
698 ],
699 ));
700 break;
701 }
702
703 let (u, s, vh) = Self::complex_svd(&m)?;
705
706 let mut rank = s.len();
708 let tol = 1e-12 * s.first().copied().unwrap_or(0.0).max(1.0);
709 while rank > 1 && s[rank - 1] <= tol {
710 rank -= 1;
711 }
712 if let Some(max_b) = max_bond_dim {
713 rank = rank.min(max_b.max(1));
714 }
715 rank = rank.max(1);
716
717 let mut a_data = Array::zeros(IxDyn(&[left_bond, d, rank]));
719 for lb in 0..left_bond {
720 for phys in 0..d {
721 for r in 0..rank {
722 a_data[[lb, phys, r]] = u[[lb * d + phys, r]];
723 }
724 }
725 }
726 mps.push(Tensor::new(
727 site,
728 a_data,
729 vec![
730 format!("bond_{site}"),
731 format!("phys_{site}"),
732 format!("bond_{}", site + 1),
733 ],
734 ));
735
736 let mut new_psi = vec![Complex64::new(0.0, 0.0); rank * cols];
739 for r in 0..rank {
740 let sigma = Complex64::new(s[r], 0.0);
741 for c in 0..cols {
742 new_psi[r * cols + c] = sigma * vh[[r, c]];
743 }
744 }
745 psi = new_psi;
746 left_bond = rank;
747 }
748
749 Ok(mps)
750 }
751
752 pub fn apply_mpo(&mut self, _mpo: &[Tensor], _qubits: &[usize]) -> QuantRS2Result<()> {
760 Err(QuantRS2Error::UnsupportedOperation(
761 "MPO application requires MPS form; call to_mps first".into(),
762 ))
763 }
764
765 fn complex_svd(
775 m: &Array2<Complex64>,
776 ) -> QuantRS2Result<(Array2<Complex64>, Vec<f64>, Array2<Complex64>)> {
777 let (rows, cols) = (m.nrows(), m.ncols());
778
779 let transposed = rows < cols;
783 let a0 = if transposed {
784 m.mapv(|z| z.conj()).t().to_owned() } else {
786 m.clone()
787 };
788 let (p, q) = (a0.nrows(), a0.ncols()); let mut a = a0; let mut v = Array2::<Complex64>::eye(q); let max_sweeps = 60;
794 let eps = 1e-15;
795 for _sweep in 0..max_sweeps {
796 let mut off = 0.0_f64;
797 for i in 0..q {
798 for j in (i + 1)..q {
799 let mut alpha = 0.0_f64; let mut beta = 0.0_f64; let mut gamma = Complex64::new(0.0, 0.0); for r in 0..p {
804 let ai = a[[r, i]];
805 let aj = a[[r, j]];
806 alpha += ai.norm_sqr();
807 beta += aj.norm_sqr();
808 gamma += ai.conj() * aj;
809 }
810 let gamma_abs = gamma.norm();
811 off += gamma_abs;
812 if gamma_abs <= eps * (alpha.sqrt() * beta.sqrt()).max(eps) {
813 continue;
814 }
815
816 let phase = gamma / gamma_abs;
819 let zeta = (beta - alpha) / (2.0 * gamma_abs);
820 let t = zeta.signum() / (zeta.abs() + (1.0 + zeta * zeta).sqrt());
821 let c = 1.0 / (1.0 + t * t).sqrt();
822 let sgn = c * t; let s_ij = phase * Complex64::new(sgn, 0.0);
824
825 for r in 0..p {
829 let ai = a[[r, i]];
830 let aj = a[[r, j]];
831 a[[r, i]] = Complex64::new(c, 0.0) * ai - s_ij.conj() * aj;
832 a[[r, j]] = s_ij * ai + Complex64::new(c, 0.0) * aj;
833 }
834 for r in 0..q {
836 let vi = v[[r, i]];
837 let vj = v[[r, j]];
838 v[[r, i]] = Complex64::new(c, 0.0) * vi - s_ij.conj() * vj;
839 v[[r, j]] = s_ij * vi + Complex64::new(c, 0.0) * vj;
840 }
841 }
842 }
843 if off <= eps {
844 break;
845 }
846 }
847
848 let mut sigma: Vec<(f64, usize)> = (0..q)
851 .map(|j| {
852 let norm = (0..p).map(|r| a[[r, j]].norm_sqr()).sum::<f64>().sqrt();
853 (norm, j)
854 })
855 .collect();
856 sigma.sort_by(|x, y| y.0.total_cmp(&x.0));
858
859 let k = q; let mut u_mat = Array2::<Complex64>::zeros((p, k));
861 let mut s_vec = vec![0.0_f64; k];
862 let mut v_sorted = Array2::<Complex64>::zeros((q, k));
863 for (new_idx, &(norm, old_idx)) in sigma.iter().enumerate() {
864 s_vec[new_idx] = norm;
865 if norm > 1e-300 {
866 for r in 0..p {
867 u_mat[[r, new_idx]] = a[[r, old_idx]] / Complex64::new(norm, 0.0);
868 }
869 } else {
870 u_mat[[0.min(p - 1), new_idx]] = Complex64::new(0.0, 0.0);
872 }
873 for r in 0..q {
874 v_sorted[[r, new_idx]] = v[[r, old_idx]];
875 }
876 }
877
878 if transposed {
880 let u_m = v_sorted; let vh_m = u_mat.mapv(|z| z.conj()).t().to_owned(); Ok((u_m, s_vec, vh_m))
885 } else {
886 let vh_m = v_sorted.mapv(|z| z.conj()).t().to_owned(); Ok((u_mat, s_vec, vh_m))
888 }
889 }
890
891 pub fn tensors(&self) -> Vec<&Tensor> {
893 self.tensors.values().collect()
894 }
895
896 pub fn tensor(&self, id: usize) -> Option<&Tensor> {
898 self.tensors.get(&id)
899 }
900}
901
902pub struct TensorNetworkBuilder {
904 network: TensorNetwork,
905 qubit_indices: HashMap<usize, String>,
906 current_indices: HashMap<usize, String>,
907}
908
909impl TensorNetworkBuilder {
910 pub fn new(num_qubits: usize) -> Self {
912 let mut network = TensorNetwork::new();
913 let mut qubit_indices = HashMap::new();
914 let mut current_indices = HashMap::new();
915
916 for i in 0..num_qubits {
918 let idx = format!("q{i}_0");
919 let tensor = Tensor::qubit_zero(i, idx.clone());
920 network.add_tensor(tensor);
921 qubit_indices.insert(i, idx.clone());
922 current_indices.insert(i, idx);
923 }
924
925 Self {
926 network,
927 qubit_indices,
928 current_indices,
929 }
930 }
931
932 pub fn apply_single_qubit_gate(
934 &mut self,
935 gate: &dyn GateOp,
936 qubit: usize,
937 ) -> QuantRS2Result<()> {
938 let matrix_vec = gate.matrix()?;
939 let matrix = Array2::from_shape_vec((2, 2), matrix_vec)
940 .map_err(|e| QuantRS2Error::InvalidInput(format!("Shape error: {e}")))?;
941
942 let in_idx = self.current_indices[&qubit].clone();
944 let out_idx = format!("q{}_{}", qubit, self.network.next_id);
945 let gate_tensor = Tensor::from_matrix(
946 self.network.next_id,
947 matrix,
948 in_idx.clone(),
949 out_idx.clone(),
950 );
951
952 let gate_id = self.network.add_tensor(gate_tensor);
954
955 if let Some(prev_tensor) = self.find_tensor_with_index(&in_idx) {
957 self.network
958 .connect(prev_tensor, in_idx.clone(), gate_id, in_idx)?;
959 }
960
961 self.current_indices.insert(qubit, out_idx);
963
964 Ok(())
965 }
966
967 pub fn apply_two_qubit_gate(
969 &mut self,
970 gate: &dyn GateOp,
971 qubit1: usize,
972 qubit2: usize,
973 ) -> QuantRS2Result<()> {
974 let matrix_vec = gate.matrix()?;
975 let matrix = Array2::from_shape_vec((4, 4), matrix_vec)
976 .map_err(|e| QuantRS2Error::InvalidInput(format!("Shape error: {e}")))?;
977
978 let tensor_data = matrix
980 .into_shape_with_order((2, 2, 2, 2))
981 .map_err(|e| QuantRS2Error::InvalidInput(format!("Shape error: {e}")))?
982 .into_dyn();
983
984 let in1_idx = self.current_indices[&qubit1].clone();
986 let in2_idx = self.current_indices[&qubit2].clone();
987 let out1_idx = format!("q{}_{}", qubit1, self.network.next_id);
988 let out2_idx = format!("q{}_{}", qubit2, self.network.next_id);
989
990 let gate_tensor = Tensor::new(
991 self.network.next_id,
992 tensor_data,
993 vec![
994 in1_idx.clone(),
995 in2_idx.clone(),
996 out1_idx.clone(),
997 out2_idx.clone(),
998 ],
999 );
1000
1001 let gate_id = self.network.add_tensor(gate_tensor);
1003
1004 if let Some(prev1) = self.find_tensor_with_index(&in1_idx) {
1006 self.network
1007 .connect(prev1, in1_idx.clone(), gate_id, in1_idx)?;
1008 }
1009 if let Some(prev2) = self.find_tensor_with_index(&in2_idx) {
1010 self.network
1011 .connect(prev2, in2_idx.clone(), gate_id, in2_idx)?;
1012 }
1013
1014 self.current_indices.insert(qubit1, out1_idx);
1016 self.current_indices.insert(qubit2, out2_idx);
1017
1018 Ok(())
1019 }
1020
1021 fn find_tensor_with_index(&self, index: &str) -> Option<usize> {
1023 for (id, tensor) in &self.network.tensors {
1024 if tensor.indices.iter().any(|idx| idx == index) {
1025 return Some(*id);
1026 }
1027 }
1028 None
1029 }
1030
1031 pub fn build(self) -> TensorNetwork {
1033 self.network
1034 }
1035
1036 #[must_use]
1038 pub fn to_statevector(&mut self) -> QuantRS2Result<Vec<Complex64>> {
1039 let final_tensor = self.network.contract_all()?;
1040 Ok(final_tensor.data.into_raw_vec_and_offset().0)
1041 }
1042}
1043
1044pub struct TensorNetworkSimulator {
1046 max_bond_dim: usize,
1048 use_compression: bool,
1050 parallel_threshold: usize,
1052}
1053
1054impl TensorNetworkSimulator {
1055 pub const fn new() -> Self {
1057 Self {
1058 max_bond_dim: 64,
1059 use_compression: true,
1060 parallel_threshold: 1000,
1061 }
1062 }
1063
1064 #[must_use]
1066 pub const fn with_max_bond_dim(mut self, dim: usize) -> Self {
1067 self.max_bond_dim = dim;
1068 self
1069 }
1070
1071 #[must_use]
1073 pub const fn with_compression(mut self, compress: bool) -> Self {
1074 self.use_compression = compress;
1075 self
1076 }
1077
1078 pub fn simulate<const N: usize>(
1080 &self,
1081 gates: &[Box<dyn GateOp>],
1082 ) -> QuantRS2Result<Register<N>> {
1083 let mut builder = TensorNetworkBuilder::new(N);
1084
1085 for gate in gates {
1087 let qubits = gate.qubits();
1088 match qubits.len() {
1089 1 => builder.apply_single_qubit_gate(gate.as_ref(), qubits[0].0 as usize)?,
1090 2 => builder.apply_two_qubit_gate(
1091 gate.as_ref(),
1092 qubits[0].0 as usize,
1093 qubits[1].0 as usize,
1094 )?,
1095 _ => {
1096 return Err(QuantRS2Error::UnsupportedOperation(format!(
1097 "Gates with {} qubits not supported in tensor network",
1098 qubits.len()
1099 )))
1100 }
1101 }
1102 }
1103
1104 let amplitudes = builder.to_statevector()?;
1106 Register::with_amplitudes(amplitudes)
1107 }
1108}
1109
1110pub mod contraction_optimization {
1112 use super::*;
1113
1114 pub struct DynamicProgrammingOptimizer {
1116 memo: HashMap<Vec<usize>, (usize, Vec<(usize, usize)>)>,
1117 }
1118
1119 impl DynamicProgrammingOptimizer {
1120 pub fn new() -> Self {
1121 Self {
1122 memo: HashMap::new(),
1123 }
1124 }
1125
1126 pub fn optimize(&mut self, network: &TensorNetwork) -> Vec<(usize, usize)> {
1128 let tensor_ids: Vec<_> = network.tensors.keys().copied().collect();
1129 self.find_optimal_order(&tensor_ids, network).1
1130 }
1131
1132 fn find_optimal_order(
1133 &mut self,
1134 tensors: &[usize],
1135 network: &TensorNetwork,
1136 ) -> (usize, Vec<(usize, usize)>) {
1137 if tensors.len() <= 1 {
1138 return (0, vec![]);
1139 }
1140
1141 let key = tensors.to_vec();
1142 if let Some(result) = self.memo.get(&key) {
1143 return result.clone();
1144 }
1145
1146 let mut best_cost = usize::MAX;
1147 let mut best_order = vec![];
1148
1149 for i in 0..tensors.len() {
1151 for j in (i + 1)..tensors.len() {
1152 if self.are_connected(tensors[i], tensors[j], network) {
1154 let cost = network.estimate_contraction_cost(tensors[i], tensors[j]);
1155
1156 let mut remaining = vec![];
1158 for (k, &t) in tensors.iter().enumerate() {
1159 if k != i && k != j {
1160 remaining.push(t);
1161 }
1162 }
1163 remaining.push(network.next_id + remaining.len()); let (sub_cost, sub_order) = self.find_optimal_order(&remaining, network);
1166 let total_cost = cost + sub_cost;
1167
1168 if total_cost < best_cost {
1169 best_cost = total_cost;
1170 best_order = vec![(tensors[i], tensors[j])];
1171 best_order.extend(sub_order);
1172 }
1173 }
1174 }
1175 }
1176
1177 self.memo.insert(key, (best_cost, best_order.clone()));
1178 (best_cost, best_order)
1179 }
1180
1181 fn are_connected(&self, t1: usize, t2: usize, network: &TensorNetwork) -> bool {
1182 network.edges.iter().any(|e| {
1183 (e.tensor1 == t1 && e.tensor2 == t2) || (e.tensor1 == t2 && e.tensor2 == t1)
1184 })
1185 }
1186 }
1187}
1188
1189#[cfg(test)]
1190mod tests {
1191 use super::*;
1192
1193 #[test]
1194 fn test_tensor_creation() {
1195 let data = ArrayD::zeros(IxDyn(&[2, 2]));
1196 let tensor = Tensor::new(0, data, vec!["in".to_string(), "out".to_string()]);
1197 assert_eq!(tensor.rank(), 2);
1198 assert_eq!(tensor.shape, vec![2, 2]);
1199 }
1200
1201 #[test]
1202 fn test_qubit_tensors() {
1203 let t0 = Tensor::qubit_zero(0, "q0".to_string());
1204 assert_eq!(t0.data[[0]], Complex64::new(1.0, 0.0));
1205 assert_eq!(t0.data[[1]], Complex64::new(0.0, 0.0));
1206
1207 let t1 = Tensor::qubit_one(1, "q1".to_string());
1208 assert_eq!(t1.data[[0]], Complex64::new(0.0, 0.0));
1209 assert_eq!(t1.data[[1]], Complex64::new(1.0, 0.0));
1210 }
1211
1212 #[test]
1213 fn test_tensor_network_builder() {
1214 let builder = TensorNetworkBuilder::new(2);
1215 assert_eq!(builder.network.tensors.len(), 2);
1216 }
1217
1218 fn contract_mps(mps: &[Tensor]) -> Vec<Complex64> {
1222 let mut acc: Vec<Complex64> = vec![Complex64::new(1.0, 0.0)];
1225 let mut left_bond = 1usize;
1226 for t in mps {
1227 let lb = t.shape[0];
1228 let d = t.shape[1];
1229 let rb = t.shape[2];
1230 assert_eq!(lb, left_bond, "bond mismatch while contracting MPS");
1231 let cols = acc.len() / left_bond; let mut new_acc = vec![Complex64::new(0.0, 0.0); rb * cols * d];
1234 for r in 0..rb {
1235 for cidx in 0..cols {
1236 for phys in 0..d {
1237 let mut sum = Complex64::new(0.0, 0.0);
1238 for l in 0..lb {
1239 let a_val = acc[l * cols + cidx];
1241 let t_val = t.data[[l, phys, r]];
1242 sum += a_val * t_val;
1243 }
1244 new_acc[(r * cols + cidx) * d + phys] = sum;
1245 }
1246 }
1247 }
1248 acc = new_acc;
1249 left_bond = rb;
1250 }
1251 acc
1252 }
1253
1254 fn single_tensor_network(amps: Vec<Complex64>, n: usize) -> TensorNetwork {
1259 let shape: Vec<usize> = vec![2usize; n];
1260 let data = Array::from_shape_vec(IxDyn(&shape), amps).expect("state tensor");
1261 let indices: Vec<String> = (0..n).map(|i| format!("phys_{i}")).collect();
1262 let mut net = TensorNetwork::new();
1263 net.add_tensor(Tensor::new(0, data, indices));
1264 net
1265 }
1266
1267 #[test]
1268 fn test_to_mps_reconstructs_bell_state() {
1269 let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
1271 let bell = vec![
1272 Complex64::new(inv_sqrt2, 0.0),
1273 Complex64::new(0.0, 0.0),
1274 Complex64::new(0.0, 0.0),
1275 Complex64::new(inv_sqrt2, 0.0),
1276 ];
1277 let net = single_tensor_network(bell.clone(), 2);
1278 let mps = net.to_mps(None).expect("to_mps");
1279 assert_eq!(mps.len(), 2, "one MPS tensor per qubit");
1280 assert_eq!(mps[0].shape[2], 2, "Bell state needs bond dimension 2");
1282
1283 let recon = contract_mps(&mps);
1284 let err: f64 = recon
1285 .iter()
1286 .zip(bell.iter())
1287 .map(|(a, b)| (a - b).norm_sqr())
1288 .sum::<f64>()
1289 .sqrt();
1290 assert!(err < 1e-9, "Bell MPS reconstruction error {err}");
1291 }
1292
1293 #[test]
1294 fn test_to_mps_reconstructs_ghz_state() {
1295 let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
1297 let mut ghz = vec![Complex64::new(0.0, 0.0); 8];
1298 ghz[0] = Complex64::new(inv_sqrt2, 0.0);
1299 ghz[7] = Complex64::new(inv_sqrt2, 0.0);
1300 let net = single_tensor_network(ghz.clone(), 3);
1301 let mps = net.to_mps(None).expect("to_mps");
1302 assert_eq!(mps.len(), 3, "one MPS tensor per qubit");
1303
1304 let recon = contract_mps(&mps);
1305 let err: f64 = recon
1306 .iter()
1307 .zip(ghz.iter())
1308 .map(|(a, b)| (a - b).norm_sqr())
1309 .sum::<f64>()
1310 .sqrt();
1311 assert!(err < 1e-9, "GHZ MPS reconstruction error {err}");
1312 }
1313
1314 #[test]
1315 fn test_to_mps_reconstructs_generic_state() {
1316 let raw = [
1318 Complex64::new(0.3, 0.1),
1319 Complex64::new(-0.2, 0.4),
1320 Complex64::new(0.5, -0.25),
1321 Complex64::new(0.1, 0.35),
1322 ];
1323 let norm = raw.iter().map(|z| z.norm_sqr()).sum::<f64>().sqrt();
1324 let state: Vec<Complex64> = raw.iter().map(|z| z / norm).collect();
1325 let net = single_tensor_network(state.clone(), 2);
1326 let mps = net.to_mps(None).expect("to_mps");
1327 let recon = contract_mps(&mps);
1328 let err: f64 = recon
1329 .iter()
1330 .zip(state.iter())
1331 .map(|(a, b)| (a - b).norm_sqr())
1332 .sum::<f64>()
1333 .sqrt();
1334 assert!(err < 1e-9, "generic MPS reconstruction error {err}");
1335 }
1336
1337 #[test]
1338 fn test_to_mps_truncation_keeps_bond_dim() {
1339 let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
1343 let bell = vec![
1344 Complex64::new(inv_sqrt2, 0.0),
1345 Complex64::new(0.0, 0.0),
1346 Complex64::new(0.0, 0.0),
1347 Complex64::new(inv_sqrt2, 0.0),
1348 ];
1349 let net = single_tensor_network(bell, 2);
1350 let mps = net.to_mps(Some(1)).expect("to_mps truncated");
1351 for t in &mps {
1352 assert!(
1353 t.shape[0] <= 1 && t.shape[2] <= 1,
1354 "bond dimension exceeded max_bond_dim=1: {:?}",
1355 t.shape
1356 );
1357 }
1358 }
1359
1360 #[test]
1361 fn test_apply_mpo_honest_error() {
1362 let mut network = TensorNetwork::new();
1364 let result = network.apply_mpo(&[], &[0]);
1365 assert!(matches!(
1366 result,
1367 Err(QuantRS2Error::UnsupportedOperation(_))
1368 ));
1369 }
1370
1371 #[test]
1372 fn test_complex_svd_roundtrip() {
1373 let m = Array2::from_shape_vec(
1375 (3, 2),
1376 vec![
1377 Complex64::new(1.0, 0.5),
1378 Complex64::new(-0.3, 0.2),
1379 Complex64::new(0.4, -0.1),
1380 Complex64::new(0.7, 0.0),
1381 Complex64::new(-0.2, 0.9),
1382 Complex64::new(0.1, 0.1),
1383 ],
1384 )
1385 .expect("matrix");
1386 let (u, s, vh) = TensorNetwork::complex_svd(&m).expect("svd");
1387 let k = s.len();
1389 let mut s_mat = Array2::<Complex64>::zeros((k, k));
1390 for i in 0..k {
1391 s_mat[[i, i]] = Complex64::new(s[i], 0.0);
1392 }
1393 let recon = u.dot(&s_mat).dot(&vh);
1394 let err: f64 = recon
1395 .iter()
1396 .zip(m.iter())
1397 .map(|(a, b)| (a - b).norm_sqr())
1398 .sum::<f64>()
1399 .sqrt();
1400 assert!(err < 1e-9, "complex SVD reconstruction error {err}");
1401 for i in 1..s.len() {
1403 assert!(s[i] <= s[i - 1] + 1e-12);
1404 assert!(s[i] >= -1e-12);
1405 }
1406 }
1407
1408 #[test]
1409 fn test_network_connection() {
1410 let mut network = TensorNetwork::new();
1411
1412 let t1 = Tensor::qubit_zero(0, "q0".to_string());
1413 let t2 = Tensor::qubit_zero(1, "q1".to_string());
1414
1415 let id1 = network.add_tensor(t1);
1416 let id2 = network.add_tensor(t2);
1417
1418 assert!(network
1420 .connect(id1, "bond".to_string(), id2, "bond".to_string())
1421 .is_err());
1422 }
1423}