1#![allow(clippy::should_implement_trait, clippy::needless_range_loop, clippy::manual_range_contains)]
23
24use crate::quantum::{BaseGate, Circuit, Gate, StateVector};
25
26#[derive(Clone, Copy, Debug)]
31pub struct C {
32 pub re: f64,
33 pub im: f64,
34}
35impl C {
36 const ZERO: C = C { re: 0.0, im: 0.0 };
37 const ONE: C = C { re: 1.0, im: 0.0 };
38 #[inline]
39 fn new(re: f64, im: f64) -> C {
40 C { re, im }
41 }
42 #[inline]
43 fn add(self, o: C) -> C {
44 C { re: self.re + o.re, im: self.im + o.im }
45 }
46 #[inline]
47 fn sub(self, o: C) -> C {
48 C { re: self.re - o.re, im: self.im - o.im }
49 }
50 #[inline]
51 fn mul(self, o: C) -> C {
52 C { re: self.re * o.re - self.im * o.im, im: self.re * o.im + self.im * o.re }
53 }
54 #[inline]
55 fn conj(self) -> C {
56 C { re: self.re, im: -self.im }
57 }
58 #[inline]
59 fn scale(self, s: f64) -> C {
60 C { re: self.re * s, im: self.im * s }
61 }
62 #[inline]
63 fn norm2(self) -> f64 {
64 self.re * self.re + self.im * self.im
65 }
66}
67
68fn jacobi_svd(mut m: Vec<Vec<C>>, rows: usize, cols: usize) -> (Vec<Vec<C>>, Vec<f64>, Vec<Vec<C>>) {
78 let mut v: Vec<Vec<C>> = (0..cols)
80 .map(|j| (0..cols).map(|i| if i == j { C::ONE } else { C::ZERO }).collect())
81 .collect();
82 let eps = 1e-14;
83 for _sweep in 0..60 {
84 let mut off = 0.0f64;
85 for p in 0..cols {
86 for q in (p + 1)..cols {
87 let mut alpha = 0.0; let mut beta = 0.0; let mut gamma = C::ZERO; for k in 0..rows {
92 alpha += m[p][k].norm2();
93 beta += m[q][k].norm2();
94 gamma = gamma.add(m[p][k].conj().mul(m[q][k]));
95 }
96 let g2 = gamma.norm2();
97 off += g2;
98 if g2 <= eps * alpha * beta || g2 == 0.0 {
99 continue;
100 }
101 let gabs = g2.sqrt();
102 let ph = C::new(gamma.re / gabs, gamma.im / gabs); let cph = ph.conj();
105 for k in 0..rows {
106 m[q][k] = m[q][k].mul(cph);
107 }
108 for k in 0..cols {
109 v[q][k] = v[q][k].mul(cph);
110 }
111 let tau = (beta - alpha) / (2.0 * gabs);
113 let t = if tau >= 0.0 {
114 1.0 / (tau + (1.0 + tau * tau).sqrt())
115 } else {
116 -1.0 / (-tau + (1.0 + tau * tau).sqrt())
117 };
118 let cs = 1.0 / (1.0 + t * t).sqrt();
119 let sn = t * cs;
120 for k in 0..rows {
121 let x = m[p][k];
122 let y = m[q][k];
123 m[p][k] = x.scale(cs).sub(y.scale(sn));
124 m[q][k] = x.scale(sn).add(y.scale(cs));
125 }
126 for k in 0..cols {
127 let x = v[p][k];
128 let y = v[q][k];
129 v[p][k] = x.scale(cs).sub(y.scale(sn));
130 v[q][k] = x.scale(sn).add(y.scale(cs));
131 }
132 }
133 }
134 if off <= eps {
135 break;
136 }
137 }
138 let mut s = vec![0.0; cols];
140 for j in 0..cols {
141 s[j] = (0..rows).map(|k| m[j][k].norm2()).sum::<f64>().sqrt();
142 }
143 let mut u: Vec<Vec<C>> = m;
144 for j in 0..cols {
145 if s[j] > 1e-300 {
146 let inv = 1.0 / s[j];
147 for k in 0..rows {
148 u[j][k] = u[j][k].scale(inv);
149 }
150 }
151 }
152 let mut order: Vec<usize> = (0..cols).collect();
154 order.sort_by(|&a, &b| s[b].partial_cmp(&s[a]).unwrap_or(std::cmp::Ordering::Equal));
155 let s2: Vec<f64> = order.iter().map(|&i| s[i]).collect();
156 let u2: Vec<Vec<C>> = order.iter().map(|&i| u[i].clone()).collect();
157 let v2: Vec<Vec<C>> = order.iter().map(|&i| v[i].clone()).collect();
158 (u2, s2, v2)
159}
160
161pub struct Mps {
168 pub n: u8,
169 a: Vec<Vec<C>>,
170 dl: Vec<usize>,
171 dr: Vec<usize>,
172 chi_max: usize,
173 max_bond: usize,
174 retained: f64, }
176
177#[inline]
178fn idx(l: usize, s: usize, r: usize, dr: usize) -> usize {
179 (l * 2 + s) * dr + r
180}
181
182impl Mps {
183 pub fn zero(n: u8, chi_max: usize) -> Mps {
185 let a: Vec<Vec<C>> = (0..n).map(|_| vec![C::ONE, C::ZERO]).collect(); Mps {
187 n,
188 a,
189 dl: vec![1; n as usize],
190 dr: vec![1; n as usize],
191 chi_max: chi_max.max(1),
192 max_bond: 1,
193 retained: 1.0,
194 }
195 }
196
197 fn apply1(&mut self, i: usize, g: [[C; 2]; 2]) {
199 let (dl, dr) = (self.dl[i], self.dr[i]);
200 let ai = &mut self.a[i];
201 for l in 0..dl {
202 for r in 0..dr {
203 let a0 = ai[idx(l, 0, r, dr)];
204 let a1 = ai[idx(l, 1, r, dr)];
205 ai[idx(l, 0, r, dr)] = g[0][0].mul(a0).add(g[0][1].mul(a1));
206 ai[idx(l, 1, r, dr)] = g[1][0].mul(a0).add(g[1][1].mul(a1));
207 }
208 }
209 }
210
211 fn apply2_adjacent(&mut self, i: usize, g: [[C; 4]; 4]) {
214 let (dl, dc, dr) = (self.dl[i], self.dr[i], self.dr[i + 1]);
215 debug_assert_eq!(dc, self.dl[i + 1]);
216 let rows = dl * 2;
220 let cols = 2 * dr;
221 let mut mcols: Vec<Vec<C>> = vec![vec![C::ZERO; rows]; cols];
222 for l in 0..dl {
223 for r in 0..dr {
224 let mut th = [C::ZERO; 4];
226 for s1 in 0..2 {
227 for s2 in 0..2 {
228 let mut acc = C::ZERO;
229 for c in 0..dc {
230 acc = acc.add(self.a[i][idx(l, s1, c, dc)].mul(self.a[i + 1][idx(c, s2, r, dr)]));
231 }
232 th[s1 * 2 + s2] = acc;
233 }
234 }
235 for t1 in 0..2 {
236 for t2 in 0..2 {
237 let mut val = C::ZERO;
238 for s in 0..4 {
239 val = val.add(g[t1 * 2 + t2][s].mul(th[s]));
240 }
241 let row = l * 2 + t1;
242 let col = t2 * dr + r;
243 mcols[col][row] = val;
244 }
245 }
246 }
247 }
248 let (u, s, v) = jacobi_svd(mcols, rows, cols);
249 let total: f64 = s.iter().map(|x| x * x).sum();
251 let mut chi = 0;
252 for (k, sv) in s.iter().enumerate() {
253 if k >= self.chi_max || *sv <= 1e-12 * s[0].max(1e-300) {
254 break;
255 }
256 chi += 1;
257 }
258 let chi = chi.max(1).min(s.len());
259 let kept: f64 = s.iter().take(chi).map(|x| x * x).sum();
260 if total > 0.0 {
261 self.retained *= kept / total;
262 }
263 self.max_bond = self.max_bond.max(chi);
264 let mut na = vec![C::ZERO; dl * 2 * chi];
266 for l in 0..dl {
267 for t1 in 0..2 {
268 for k in 0..chi {
269 na[idx(l, t1, k, chi)] = u[k][l * 2 + t1];
270 }
271 }
272 }
273 let mut nb = vec![C::ZERO; chi * 2 * dr];
275 for k in 0..chi {
276 for t2 in 0..2 {
277 for r in 0..dr {
278 nb[idx(k, t2, r, dr)] = v[k][t2 * dr + r].conj().scale(s[k]);
279 }
280 }
281 }
282 self.a[i] = na;
283 self.dr[i] = chi;
284 self.a[i + 1] = nb;
285 self.dl[i + 1] = chi;
286 }
287
288 fn swap_adjacent(&mut self, i: usize) {
290 self.apply2_adjacent(i, swap_gate());
291 }
292
293 fn apply2(&mut self, a: usize, b: usize, g4: [[C; 4]; 4]) {
296 let (lo, hi) = (a.min(b), a.max(b));
297 if hi == lo + 1 {
298 self.apply2_adjacent(lo, g4);
299 return;
300 }
301 for k in (lo + 1..hi).rev() {
303 self.swap_adjacent(k);
304 }
305 self.apply2_adjacent(lo, g4);
306 for k in lo + 1..hi {
307 self.swap_adjacent(k);
308 }
309 }
310
311 pub fn to_statevector(&self) -> Vec<C> {
314 let dim = 1usize << self.n;
315 let mut out = vec![C::ZERO; dim];
316 for k in 0..dim {
317 let mut vec = vec![C::ONE]; for i in 0..self.n as usize {
320 let s = (k >> i) & 1;
321 let (dl, dr) = (self.dl[i], self.dr[i]);
322 let mut nv = vec![C::ZERO; dr];
323 for r in 0..dr {
324 let mut acc = C::ZERO;
325 for l in 0..dl {
326 acc = acc.add(vec[l].mul(self.a[i][idx(l, s, r, dr)]));
327 }
328 nv[r] = acc;
329 }
330 vec = nv;
331 }
332 out[k] = vec[0];
333 }
334 let norm: f64 = out.iter().map(|c| c.norm2()).sum::<f64>().sqrt();
336 if norm > 0.0 {
337 for c in out.iter_mut() {
338 *c = c.scale(1.0 / norm);
339 }
340 }
341 out
342 }
343
344 pub fn max_bond(&self) -> usize {
345 self.max_bond
346 }
347 pub fn retained_weight(&self) -> f64 {
353 self.retained
354 }
355}
356
357fn m1(base: BaseGate, param: u16) -> [[C; 2]; 2] {
362 let s = std::f64::consts::FRAC_1_SQRT_2;
363 let z = C::ZERO;
364 let o = C::ONE;
365 let i = C::new(0.0, 1.0);
366 let ni = C::new(0.0, -1.0);
367 let no = C::new(-1.0, 0.0);
368 let phase = |k: u16| {
369 let th = 2.0 * std::f64::consts::PI / (1u64 << k) as f64;
370 C::new(th.cos(), th.sin())
371 };
372 match base {
373 BaseGate::I => [[o, z], [z, o]],
374 BaseGate::X => [[z, o], [o, z]],
375 BaseGate::Y => [[z, ni], [i, z]],
376 BaseGate::Z => [[o, z], [z, no]],
377 BaseGate::H => [[C::new(s, 0.0), C::new(s, 0.0)], [C::new(s, 0.0), C::new(-s, 0.0)]],
378 BaseGate::S => [[o, z], [z, i]],
379 BaseGate::Sdg => [[o, z], [z, ni]],
380 BaseGate::T => [[o, z], [z, phase(3)]],
381 BaseGate::Tdg => [[o, z], [z, phase(3).conj()]],
382 BaseGate::P => [[o, z], [z, phase(param)]],
383 }
384}
385
386fn controlled4(control_is_lo: bool, u: [[C; 2]; 2]) -> [[C; 4]; 4] {
389 let mut g = [[C::ZERO; 4]; 4];
390 for a in 0..2 {
391 for b in 0..2 {
392 let inb = a * 2 + b;
393 let (ctrl, tgt) = if control_is_lo { (a, b) } else { (b, a) };
394 if ctrl == 0 {
395 g[inb][inb] = C::ONE; } else {
397 for tt in 0..2 {
399 let (oa, ob) = if control_is_lo { (a, tt) } else { (tt, b) };
400 let out = oa * 2 + ob;
401 g[out][inb] = u[tt][tgt];
402 }
403 }
404 }
405 }
406 g
407}
408
409fn swap_gate() -> [[C; 4]; 4] {
410 let mut g = [[C::ZERO; 4]; 4];
411 g[0][0] = C::ONE;
412 g[3][3] = C::ONE;
413 g[1][2] = C::ONE; g[2][1] = C::ONE;
415 g
416}
417
418fn decompose_cc(g: &Gate) -> Option<Vec<Gate>> {
421 if g.controls.len() != 2 {
422 return None;
423 }
424 let (a, b, c) = (g.controls[0], g.controls[1], g.target);
425 let g1 = |base, t| Gate { base, controls: vec![], target: t, param: 0 };
426 let cx = |ctrl, t| Gate { base: BaseGate::X, controls: vec![ctrl], target: t, param: 0 };
427 let mut ops = Vec::new();
428 if g.base == BaseGate::Z {
429 ops.push(g1(BaseGate::H, c));
430 }
431 ops.push(g1(BaseGate::H, c));
433 ops.push(cx(b, c));
434 ops.push(g1(BaseGate::Tdg, c));
435 ops.push(cx(a, c));
436 ops.push(g1(BaseGate::T, c));
437 ops.push(cx(b, c));
438 ops.push(g1(BaseGate::Tdg, c));
439 ops.push(cx(a, c));
440 ops.push(g1(BaseGate::T, b));
441 ops.push(g1(BaseGate::T, c));
442 ops.push(cx(a, b));
443 ops.push(g1(BaseGate::H, c));
444 ops.push(g1(BaseGate::T, a));
445 ops.push(g1(BaseGate::Tdg, b));
446 ops.push(cx(a, b));
447 if g.base == BaseGate::Z {
448 ops.push(g1(BaseGate::H, c));
449 }
450 Some(ops)
451}
452
453pub struct MpsRun {
455 pub max_bond: usize,
456 pub retained_weight: f64,
457 pub fidelity_vs_dense: Option<f64>,
460}
461
462#[derive(Debug)]
464pub enum MpsError {
465 Unsupported(String),
467}
468
469pub fn run(circuit: &Circuit, chi_max: usize) -> Result<(Mps, MpsRun), MpsError> {
472 let mut mps = Mps::zero(circuit.n_qubits, chi_max);
473 for g in &circuit.ops {
474 apply_gate(&mut mps, g)?;
475 }
476 let run = MpsRun {
477 max_bond: mps.max_bond(),
478 retained_weight: mps.retained_weight(),
479 fidelity_vs_dense: None,
480 };
481 Ok((mps, run))
482}
483
484fn apply_gate(mps: &mut Mps, g: &Gate) -> Result<(), MpsError> {
485 match g.controls.len() {
486 0 => mps.apply1(g.target as usize, m1(g.base, g.param)),
487 1 => {
488 let ctrl = g.controls[0] as usize;
489 let tgt = g.target as usize;
490 let control_is_lo = ctrl < tgt;
491 let g4 = controlled4(control_is_lo, m1(g.base, g.param));
492 mps.apply2(ctrl, tgt, g4);
493 }
494 2 => {
495 let ops = decompose_cc(g)
496 .ok_or_else(|| MpsError::Unsupported(format!("{:?} with 2 controls", g.base)))?;
497 for sub in &ops {
498 apply_gate(mps, sub)?;
499 }
500 }
501 k => return Err(MpsError::Unsupported(format!("{k} controls"))),
502 }
503 Ok(())
504}
505
506pub fn fidelity(mps: &Mps, dense: &StateVector) -> f64 {
509 let a = mps.to_statevector();
510 let scale = (1u64 << crate::quantum::FRAC) as f64;
511 let mut overlap = C::ZERO;
512 let mut nd = 0.0;
513 for (k, amp) in dense.amps.iter().enumerate() {
514 let d = C::new(amp.re as f64 / scale, amp.im as f64 / scale);
515 overlap = overlap.add(a[k].conj().mul(d));
516 nd += d.norm2();
517 }
518 if nd <= 0.0 {
519 return 0.0;
520 }
521 overlap.norm2() / nd }
523
524#[cfg(test)]
525mod tests {
526 use super::*;
527
528 fn approx(a: f64, b: f64, tol: f64) -> bool {
529 (a - b).abs() < tol
530 }
531 fn ghz(n: u8) -> Circuit {
532 let mut c = Circuit::new(n);
533 c.h(0);
534 for i in 1..n {
535 c.cx(i - 1, i);
536 }
537 c
538 }
539 fn bell() -> Circuit {
540 let mut c = Circuit::new(2);
541 c.h(0);
542 c.cx(0, 1);
543 c
544 }
545 fn entangling(n: u8) -> Circuit {
547 let mut c = Circuit::new(n);
548 for _ in 0..3 {
549 for q in 0..n {
550 c.h(q);
551 c.t(q);
552 }
553 let mut q = 0;
554 while q + 1 < n {
555 c.cx(q, q + 1);
556 q += 2;
557 }
558 let mut q = 1;
559 while q + 1 < n {
560 c.cx(q, q + 1);
561 q += 2;
562 }
563 }
564 c
565 }
566
567 #[test]
568 fn product_state_stays_bond_one() {
569 let mut c = Circuit::new(5);
571 for q in 0..5 {
572 c.h(q);
573 }
574 let (mps, run) = run(&c, 16).unwrap();
575 assert_eq!(run.max_bond, 1, "product state must stay bond-1");
576 assert!(approx(fidelity(&mps, &c.simulate().unwrap()), 1.0, 1e-9));
577 }
578
579 #[test]
580 fn ghz_is_exact_at_bond_two() {
581 let c = ghz(6);
582 let (mps, run) = run(&c, 8).unwrap();
583 assert_eq!(run.max_bond, 2, "GHZ is a bond-2 state");
584 assert!(approx(fidelity(&mps, &c.simulate().unwrap()), 1.0, 1e-9), "GHZ must be exact");
585 }
586
587 #[test]
588 fn bell_and_small_circuits_match_dense() {
589 for c in [bell(), ghz(4)] {
590 let (mps, _) = run(&c, 16).unwrap();
591 let f = fidelity(&mps, &c.simulate().unwrap());
592 assert!(approx(f, 1.0, 1e-8), "MPS should match dense, got {f}");
593 }
594 }
595
596 #[test]
597 fn qft_matches_dense_at_full_bond() {
598 let c = Circuit::qft(6);
600 let (mps, _) = run(&c, 64).unwrap();
601 let f = fidelity(&mps, &c.simulate().unwrap());
602 assert!(approx(f, 1.0, 1e-7), "QFT full-bond fidelity {f}");
603 }
604
605 #[test]
606 fn ccz_decomposition_matches_dense() {
607 let mut c = Circuit::new(3);
610 c.h(0);
611 c.h(1);
612 c.h(2);
613 c.ops.push(Gate { base: BaseGate::Z, controls: vec![0, 1], target: 2, param: 0 });
614 let (mps, _) = run(&c, 32).unwrap();
615 let f = fidelity(&mps, &c.simulate().unwrap());
616 assert!(approx(f, 1.0, 1e-6), "CCZ decomposition fidelity {f}");
617 }
618
619 #[test]
620 fn nonadjacent_two_qubit_gate() {
621 let mut c = Circuit::new(5);
623 c.h(0);
624 c.cx(0, 4);
625 let (mps, _) = run(&c, 8).unwrap();
626 assert!(approx(fidelity(&mps, &c.simulate().unwrap()), 1.0, 1e-8));
627 }
628
629 #[test]
630 fn truncation_is_measured_not_hidden() {
631 let c = entangling(6);
635 let dense = c.simulate().unwrap();
636 let (mps, st) = run(&c, 2).unwrap();
637 assert!(st.retained_weight < 0.999, "squeezed bond must report lost weight: {}", st.retained_weight);
638 assert!(st.retained_weight > 0.0, "some weight is kept");
639 assert!(st.max_bond <= 2, "bond cap respected");
640 assert!(fidelity(&mps, &dense) < 0.999, "aggressive truncation must measurably reduce fidelity");
641 let (mps2, st2) = run(&c, 8).unwrap();
643 assert!(approx(st2.retained_weight, 1.0, 1e-6), "full bond keeps all weight: {}", st2.retained_weight);
644 assert!(approx(fidelity(&mps2, &dense), 1.0, 1e-6));
645 }
646
647 #[test]
648 fn deterministic() {
649 let c = Circuit::qft(6);
650 let a = run(&c, 8).unwrap().0.to_statevector();
651 let b = run(&c, 8).unwrap().0.to_statevector();
652 assert!(a.iter().zip(&b).all(|(x, y)| x.re.to_bits() == y.re.to_bits() && x.im.to_bits() == y.im.to_bits()),
653 "MPS must be bit-reproducible");
654 }
655}