|
| 1 | +// This code is part of Qiskit. |
| 2 | +// |
| 3 | +// (C) Copyright IBM 2024 |
| 4 | +// |
| 5 | +// This code is licensed under the Apache License, Version 2.0. You may |
| 6 | +// obtain a copy of this license in the LICENSE.txt file in the root directory |
| 7 | +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. |
| 8 | +// |
| 9 | +// Any modifications or derivative works of this code must retain this |
| 10 | +// copyright notice, and modified files need to carry a notice indicating |
| 11 | +// that they have been altered from the originals. |
| 12 | + |
| 13 | +use std::iter::once; |
| 14 | + |
| 15 | +use hashbrown::HashMap; |
| 16 | +use itertools::Itertools; |
| 17 | +use ndarray::{Array1, ArrayView2}; |
| 18 | + |
| 19 | +use qiskit_circuit::{ |
| 20 | + operations::{Param, StandardGate}, |
| 21 | + Qubit, |
| 22 | +}; |
| 23 | +use smallvec::{smallvec, SmallVec}; |
| 24 | + |
| 25 | +use crate::synthesis::permutation::{_append_cx_stage1, _append_cx_stage2}; |
| 26 | + |
| 27 | +// A sequence of Lnn gates |
| 28 | +// Represents the return type for Lnn Synthesis algorithms |
| 29 | +pub(crate) type LnnGatesVec = Vec<(StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>)>; |
| 30 | + |
| 31 | +/// A pattern denoted by Pj in [1] for odd number of qubits: |
| 32 | +/// [n-2, n-4, n-4, ..., 3, 3, 1, 1, 0, 0, 2, 2, ..., n-3, n-3] |
| 33 | +fn _odd_pattern1(n: usize) -> Vec<usize> { |
| 34 | + once(n - 2) |
| 35 | + .chain((0..((n - 3) / 2)).flat_map(|i| [(n - 2 * i - 4); 2])) |
| 36 | + .chain((0..((n - 1) / 2)).flat_map(|i| [2 * i; 2])) |
| 37 | + .collect() |
| 38 | +} |
| 39 | + |
| 40 | +/// A pattern denoted by Pk in [1] for odd number of qubits: |
| 41 | +/// [2, 2, 4, 4, ..., n-1, n-1, n-2, n-2, n-4, n-4, ..., 5, 5, 3, 3, 1] |
| 42 | +fn _odd_pattern2(n: usize) -> Vec<usize> { |
| 43 | + (0..((n - 1) / 2)) |
| 44 | + .flat_map(|i| [(2 * i + 2); 2]) |
| 45 | + .chain((0..((n - 3) / 2)).flat_map(|i| [n - 2 * i - 2; 2])) |
| 46 | + .chain(once(1)) |
| 47 | + .collect() |
| 48 | +} |
| 49 | + |
| 50 | +/// A pattern denoted by Pj in [1] for even number of qubits: |
| 51 | +/// [n-1, n-3, n-3, n-5, n-5, ..., 1, 1, 0, 0, 2, 2, ..., n-4, n-4, n-2] |
| 52 | +fn _even_pattern1(n: usize) -> Vec<usize> { |
| 53 | + once(n - 1) |
| 54 | + .chain((0..((n - 2) / 2)).flat_map(|i| [n - 2 * i - 3; 2])) |
| 55 | + .chain((0..((n - 2) / 2)).flat_map(|i| [2 * i; 2])) |
| 56 | + .chain(once(n - 2)) |
| 57 | + .collect() |
| 58 | +} |
| 59 | + |
| 60 | +/// A pattern denoted by Pk in [1] for even number of qubits: |
| 61 | +/// [2, 2, 4, 4, ..., n-2, n-2, n-1, n-1, ..., 3, 3, 1, 1] |
| 62 | +fn _even_pattern2(n: usize) -> Vec<usize> { |
| 63 | + (0..((n - 2) / 2)) |
| 64 | + .flat_map(|i| [2 * (i + 1); 2]) |
| 65 | + .chain((0..(n / 2)).flat_map(|i| [(n - 2 * i - 1); 2])) |
| 66 | + .collect() |
| 67 | +} |
| 68 | + |
| 69 | +/// Creating the patterns for the phase layers. |
| 70 | +fn _create_patterns(n: usize) -> HashMap<(usize, usize), (usize, usize)> { |
| 71 | + let (pat1, pat2) = if n % 2 == 0 { |
| 72 | + (_even_pattern1(n), _even_pattern2(n)) |
| 73 | + } else { |
| 74 | + (_odd_pattern1(n), _odd_pattern2(n)) |
| 75 | + }; |
| 76 | + |
| 77 | + let ind = if n % 2 == 0 { |
| 78 | + (2 * n - 4) / 2 |
| 79 | + } else { |
| 80 | + (2 * n - 4) / 2 - 1 |
| 81 | + }; |
| 82 | + |
| 83 | + HashMap::from_iter((0..n).map(|i| ((0, i), (i, i))).chain( |
| 84 | + (0..(n / 2)).cartesian_product(0..n).map(|(layer, i)| { |
| 85 | + ( |
| 86 | + (layer + 1, i), |
| 87 | + (pat1[ind - (2 * layer) + i], pat2[(2 * layer) + i]), |
| 88 | + ) |
| 89 | + }), |
| 90 | + )) |
| 91 | +} |
| 92 | + |
| 93 | +/// Appends correct phase gate during CZ synthesis |
| 94 | +fn _append_phase_gate(pat_val: usize, gates: &mut LnnGatesVec, qubit: usize) { |
| 95 | + // Add phase gates: s, sdg or z |
| 96 | + let gate_id = pat_val % 4; |
| 97 | + if gate_id != 0 { |
| 98 | + let gate = match gate_id { |
| 99 | + 1 => StandardGate::SdgGate, |
| 100 | + 2 => StandardGate::ZGate, |
| 101 | + 3 => StandardGate::SGate, |
| 102 | + _ => unreachable!(), // unreachable as we have modulo 4 |
| 103 | + }; |
| 104 | + gates.push((gate, smallvec![], smallvec![Qubit(qubit as u32)])); |
| 105 | + } |
| 106 | +} |
| 107 | + |
| 108 | +/// Synthesis of a CZ circuit for linear nearest neighbor (LNN) connectivity, |
| 109 | +/// based on Maslov and Roetteler. |
| 110 | +pub(super) fn synth_cz_depth_line_mr_inner(matrix: ArrayView2<bool>) -> (usize, LnnGatesVec) { |
| 111 | + let num_qubits = matrix.raw_dim()[0]; |
| 112 | + let pats = _create_patterns(num_qubits); |
| 113 | + |
| 114 | + // s_gates[i] = 0, 1, 2 or 3 for a gate id, sdg, z or s on qubit i respectively |
| 115 | + let mut s_gates = Array1::<usize>::zeros(num_qubits); |
| 116 | + |
| 117 | + let mut patlist: Vec<(usize, usize)> = Vec::new(); |
| 118 | + |
| 119 | + let mut gates = LnnGatesVec::new(); |
| 120 | + |
| 121 | + for i in 0..num_qubits { |
| 122 | + for j in (i + 1)..num_qubits { |
| 123 | + if matrix[[i, j]] { |
| 124 | + // CZ(i,j) gate |
| 125 | + s_gates[[i]] += 2; // qc.z[i] |
| 126 | + s_gates[[j]] += 2; // qc.z[j] |
| 127 | + patlist.push((i, j - 1)); |
| 128 | + patlist.push((i, j)); |
| 129 | + patlist.push((i + 1, j - 1)); |
| 130 | + patlist.push((i + 1, j)); |
| 131 | + } |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + for i in 0..((num_qubits + 1) / 2) { |
| 136 | + for j in 0..num_qubits { |
| 137 | + let pat_val = pats[&(i, j)]; |
| 138 | + if patlist.contains(&pat_val) { |
| 139 | + // patcnt should be 0 or 1, which checks if a Sdg gate should be added |
| 140 | + let patcnt = patlist.iter().filter(|val| **val == pat_val).count(); |
| 141 | + s_gates[[j]] += patcnt; // qc.sdg[j] |
| 142 | + } |
| 143 | + |
| 144 | + _append_phase_gate(s_gates[[j]], &mut gates, j) |
| 145 | + } |
| 146 | + |
| 147 | + _append_cx_stage1(&mut gates, num_qubits); |
| 148 | + _append_cx_stage2(&mut gates, num_qubits); |
| 149 | + s_gates = Array1::<usize>::zeros(num_qubits); |
| 150 | + } |
| 151 | + |
| 152 | + if num_qubits % 2 == 0 { |
| 153 | + let i = num_qubits / 2; |
| 154 | + |
| 155 | + for j in 0..num_qubits { |
| 156 | + let pat_val = pats[&(i, j)]; |
| 157 | + if patlist.contains(&pat_val) && pat_val.0 != pat_val.1 { |
| 158 | + // patcnt should be 0 or 1, which checks if a Sdg gate should be added |
| 159 | + let patcnt = patlist.iter().filter(|val| **val == pat_val).count(); |
| 160 | + |
| 161 | + s_gates[[j]] += patcnt; // qc.sdg[j] |
| 162 | + } |
| 163 | + |
| 164 | + _append_phase_gate(s_gates[[j]], &mut gates, j) |
| 165 | + } |
| 166 | + |
| 167 | + _append_cx_stage1(&mut gates, num_qubits); |
| 168 | + } |
| 169 | + |
| 170 | + (num_qubits, gates) |
| 171 | +} |
0 commit comments