def _decompose_(self, qubits): qubits = list(qubits) controls = qubits[:self.reg_size] target = qubits[self.reg_size] ancilla = qubits[self.reg_size + 1:] if len(controls) == 2: yield ops.CCX(controls[0], controls[1], target) elif len(controls) == 1: yield ops.CNOT(controls[0], target) else: # Build the list depth = int(np.ceil(np.log2(len(controls))) - 1) new_bits = controls curr_ancil = 0 store_gates = [] for layer in range(depth): bits = new_bits new_bits = [] for i in range(len(bits) // 2): g = ops.CCX(bits[2 * i], bits[2 * i + 1], ancilla[curr_ancil]) yield g store_gates.append(g) new_bits.append(ancilla[curr_ancil]) curr_ancil = curr_ancil + 1 if len(bits) % 2 == 1: new_bits.append(bits[-1]) yield ops.CCX(new_bits[0], new_bits[1], target) # Get the ancilla back yield from cirq.inverse(store_gates)
def _decompose_(self, qubits): qubits = list(qubits) controls = qubits[:self.reg_size - 1] target = qubits[self.reg_size - 1] bbits = qubits[self.reg_size:] if len(controls) == 2: yield ops.CCX(controls[0], controls[1], target) elif len(controls) == 1: yield ops.CNOT(controls[0], target) else: # Build the list bits = [] bits.append(controls[0]) for i in range(1, len(controls) - 1): bits.append(controls[i]) bits.append(bbits[i - 1]) bits.append(controls[-1]) bits.append(target) for i in range(len(bits) - 1, 0, -2): yield ops.CCX(bits[i - 2], bits[i - 1], bits[i]) for i in range(4, len(bits) - 2, 2): yield ops.CCX(bits[i - 2], bits[i - 1], bits[i]) for i in range(len(bits) - 1, 0, -2): yield ops.CCX(bits[i - 2], bits[i - 1], bits[i]) for i in range(4, len(bits) - 2, 2): yield ops.CCX(bits[i - 2], bits[i - 1], bits[i])
def _decompose_(self, qubits): qubits = list(qubits) assert len(qubits) == 2 * self.register_size A = qubits[:self.register_size] B = qubits[self.register_size:] for i in range(1, self.register_size): yield ops.CNOT(A[i], B[i]) for i in reversed(range(1, self.register_size - 1)): yield ops.CNOT(A[i], A[i + 1]) for i in range(self.register_size - 1): yield ops.CCX(A[i], B[i], A[i + 1]) for i in reversed(range(1, self.register_size)): yield ops.CNOT(A[i], B[i]) yield ops.CCX(A[i - 1], B[i - 1], A[i]) for i in range(1, self.register_size - 1): yield ops.CNOT(A[i], A[i + 1]) for i in range(self.register_size): yield ops.CNOT(A[i], B[i])
def _linear_increment_n_bb(self, qubits): # Expecting qubits = [x1, A, x2, B, x3, C, ..., Z] i.e. alternating bb with ob = output bit for i in range(1, len(qubits) - 2, 2): yield ops.CNOT(qubits[0], qubits[i]) yield ops.X(qubits[i + 1]) yield ops.X(qubits[-1]) for i in range(2, len(qubits) - 1, 2): yield ops.CNOT(qubits[i - 2], qubits[i - 1]) yield ops.CCX(qubits[i], qubits[i - 1], qubits[i - 2]) yield ops.CCX(qubits[i - 2], qubits[i - 1], qubits[i]) yield ops.CNOT(qubits[-2], qubits[-1]) for i in reversed(range(2, len(qubits) - 1, 2)): yield ops.CCX(qubits[i - 2], qubits[i - 1], qubits[i]) yield ops.CCX(qubits[i], qubits[i - 1], qubits[i - 2]) yield ops.CNOT(qubits[i], qubits[i - 1]) for i in range(2, len(qubits) - 1, 2): yield ops.X(qubits[i]) for i in range(2, len(qubits) - 1, 2): yield ops.CNOT(qubits[i - 2], qubits[i - 1]) yield ops.CCX(qubits[i], qubits[i - 1], qubits[i - 2]) yield ops.CCX(qubits[i - 2], qubits[i - 1], qubits[i]) yield ops.CNOT(qubits[-2], qubits[-1]) for i in reversed(range(2, len(qubits) - 1, 2)): yield ops.CCX(qubits[i - 2], qubits[i - 1], qubits[i]) yield ops.CCX(qubits[i], qubits[i - 1], qubits[i - 2]) yield ops.CNOT(qubits[i], qubits[i - 1]) for i in range(1, len(qubits) - 2, 2): yield ops.CNOT(qubits[0], qubits[i])
def _decompose_half(self, qubits): if len(qubits) % 2 != 0: # Expecting list of qubits of type [A, B, x1, C, x2, D, x3, ..., Z, T] where xi are borrowed bits for i in range(len(qubits) - 1, 0, -2): yield ops.CCX(qubits[i-2], qubits[i-1], qubits[i]) for i in range(4, len(qubits) - 2, 2): yield ops.CCX(qubits[i-2], qubits[i-1], qubits[i]) for i in range(len(qubits) - 1, 0, -2): yield ops.CCX(qubits[i-2], qubits[i-1], qubits[i]) for i in range(4, len(qubits) - 2, 2): yield ops.CCX(qubits[i-2], qubits[i-1], qubits[i]) else: # Expecting list of qubits of type [A, x1, B, x2, ..., x_n-2, Z, T] for i in range(len(qubits) - 1, 1, -2): yield ops.CCX(qubits[i-2], qubits[i-1], qubits[i]) # Handle the top CNOT separately yield ops.CNOT(qubits[0], qubits[1]) for i in range(3, len(qubits) - 2, 2): yield ops.CCX(qubits[i-2], qubits[i-1], qubits[i]) for i in range(len(qubits) - 1, 1, -2): yield ops.CCX(qubits[i-2], qubits[i-1], qubits[i]) # Handle the top CNOT separately yield ops.CNOT(qubits[0], qubits[1]) for i in range(3, len(qubits) - 2, 2): yield ops.CCX(qubits[i-2], qubits[i-1], qubits[i])
def default_decompose(self, qubits): # Qubits [q0, q1, ..., qn-1, qn] where qn-1 is target, qn is a borrowed bit qbits = list(qubits) if len(qbits) == 4: yield ops.CCX(qbits[0], qbits[1], qbits[2]) elif len(qbits) == 3: yield ops.CNOT(qbits[0], qbits[1]) elif len(qbits) == 5: yield ops.CCX(qbits[0], qbits[1], qbits[-1]) yield ops.CCX(qbits[2], qbits[-1], qbits[3]) yield ops.CCX(qbits[0], qbits[1], qbits[-1]) yield ops.CCX(qbits[2], qbits[-1], qbits[3]) else: yield self._decompose(qbits)
def backward(groups): for g in groups[1:-1]: if len(g) == 3: yield ops.CCX(*g) elif len(g) == 2: yield ops.CNOT(*g) else: half_borrowed_gate = CnUHalfBorrowedGate(register_size=len(g)) yield half_borrowed_gate(*g, *find_dirty(groups, g))
def _decompose_(self, qubits): qubits = list(qubits) if len(qubits) == 2: yield ops.CNOT(*qubits) elif len(qubits) == 1: yield ops.X(*qubits) elif len(qubits) == 3: yield ops.CCX(*qubits) else: yield self._startdecompose(qubits)
def _maj(reg): """ applies the MAJ operator to a three bit register ------X--@---- ---X--|--@---- ---@--@--X---- """ yield ops.CNOT(reg[2], reg[1]) yield ops.CNOT(reg[2], reg[0]) yield ops.CCX(reg[0], reg[1], reg[2])
def _uma_parallel(reg): """ applies the UMA operator which maximizes parallism ------@---@----X----- --X---X---@--X-|--X-- ----------X----@--@-- """ yield ops.X(reg[1]) yield ops.CNOT(reg[0], reg[1]) yield ops.CCX(reg[0], reg[1], reg[2]) yield ops.X(reg[1]) yield ops.CNOT(reg[2], reg[0]) yield ops.CNOT(reg[2], reg[1])
def _decompose_(self, qubits): qubits = list(qubits) assert len( qubits ) > self.control_size, f'MultiControlGate cannot be applied to fewer than {self.control_size + 1} bits' if len(qubits) == self.control_size + 1: cnx_inplace = CnXLinearGate(reg_size=len(qubits)) yield cnx_inplace(*qubits) else: controls = qubits[:self.control_size] target = qubits[self.control_size] ancilla = qubits[(self.control_size + 1):(self.control_size + self.ancilla_size + 1)] dirty = qubits[(self.control_size + self.ancilla_size + 1):] n = len(controls) m = len(ancilla) self._multi_control(n, m) base_controls, base_target, base_ancilla, base_dirty = yield from self._prep_gates( qubits, n, m) if len(base_controls) == 2: yield ops.CCX(base_controls[0], base_controls[1], target) elif len(base_controls) == 1: yield ops.CNOT(base_controls[0], target) else: if len(base_ancilla) == 0: dirty_gate = CnUHalfBorrowedGate(len(base_controls) + 1) dirt = base_dirty + base_ancilla dirty_qubit_list = base_controls + [target] + dirt[:( len(base_controls) - 2)] apply_dirty = dirty_gate.on(*dirty_qubit_list) yield apply_dirty yield (cirq.inverse(self._prep_gates(qubits, n, m)))
def _decompose_(self, qubits): def find_dirty(groups, g): d = [] for f in groups: for e in f: if e not in g and e not in d: d.append(e) if len(d) == len(g) - 3: return d return d assert len(qubits) >= self.n + 1, f'Invalid application of a {self.n} control gate to {len(qubits)}' qubits = list(qubits) controls = qubits[:self.n] target = qubits[self.n] ancilla = qubits[self.n+1:] m = len(ancilla) assert m > 0, f'If 0 ancilla are provided, please use a cnx_inplace gate instead.' if len(controls) == 2: yield ops.CCX(controls[0], controls[1], target) elif len(controls) == 1: yield ops.CNOT(controls[0], target) else: if len(ancilla) > self.n - 2: half_borrowed_gate = CnUHalfBorrowedGate(register_size=self.n+1) yield half_borrowed_gate(*controls, target, *ancilla[:self.n - 2]) else: # Group the controls into m + 1 groups, do as many toffoli's as possible then start filling until can't be done groups = [[] for _ in range(m + 1)] #for i in range(1, m): # groups[i].append(ancilla[i-1]) groups[0].append(controls[0]) for i in range(m + 1): groups[i].append(controls[i+1]) i = m+2 which_group = -1 while i < len(controls): if which_group == 0: groups[0].append(controls[i]) i += 1 which_group = -1 else: g = groups[-1] free = sum([len(gg) if gg != g else 0 for gg in groups]) + len(ancilla) - 1 while len(g) + 1 < free + 2: g.append(controls[i]) i += 1 if i >= len(controls): break if i >= len(controls): break which_group = 0 for i in range(m + 1): if i != 0: groups[i].insert(0, groups[i-1][-1]) if i < m: groups[i].append(ancilla.pop(0)) else: groups[i].append(target) def forward(groups): for g in reversed(groups): if len(g) == 3: yield ops.CCX(*g) elif len(g) == 2: yield ops.CNOT(*g) else: half_borrowed_gate = CnUHalfBorrowedGate(register_size=len(g)) yield half_borrowed_gate(*g, *find_dirty(groups, g)) def backward(groups): for g in groups[1:-1]: if len(g) == 3: yield ops.CCX(*g) elif len(g) == 2: yield ops.CNOT(*g) else: half_borrowed_gate = CnUHalfBorrowedGate(register_size=len(g)) yield half_borrowed_gate(*g, *find_dirty(groups, g)) yield forward(groups) yield backward(groups) yield forward(groups) yield backward(groups)