def test_mcmt_v_chain_ancilla_test(self): """Test too few and too many ancillas for the MCMT V-chain mode.""" with self.subTest(msg="insufficient number of auxiliary qubits on gate"): qc = QuantumCircuit(5) mcmt = MCMTVChain(ZGate(), 3, 1) with self.assertRaises(QiskitError): qc.append(mcmt, range(5)) with self.subTest(msg="too many auxiliary qubits on gate"): qc = QuantumCircuit(9) mcmt = MCMTVChain(ZGate(), 3, 1) with self.assertRaises(QiskitError): qc.append(mcmt, range(9))
def test_mcmt_v_chain_simulation(self, cgate, num_controls, num_targets): """Test the MCMT V-chain implementation test on a simulation.""" controls = QuantumRegister(num_controls) targets = QuantumRegister(num_targets) subsets = [tuple(range(i)) for i in range(num_controls + 1)] for subset in subsets: qc = QuantumCircuit(targets, controls) # Initialize all targets to 1, just to be sure that # the generic gate has some effect (f.e. Z gate has no effect # on a 0 state) qc.x(targets) num_ancillas = max(0, num_controls - 1) if num_ancillas > 0: ancillas = QuantumRegister(num_ancillas) qc.add_register(ancillas) qubits = controls[:] + targets[:] + ancillas[:] else: qubits = controls[:] + targets[:] for i in subset: qc.x(controls[i]) mcmt = MCMTVChain(cgate, num_controls, num_targets) qc.compose(mcmt, qubits, inplace=True) for i in subset: qc.x(controls[i]) vec = Statevector.from_label("0" * qc.num_qubits).evolve(qc) # target register is initially |11...1>, with length equal to 2**(n_targets) vec_exp = np.array([0] * (2 ** (num_targets) - 1) + [1]) if isinstance(cgate, CZGate): # Z gate flips the last qubit only if it's applied an odd number of times if len(subset) == num_controls and (num_controls % 2) == 1: vec_exp[-1] = -1 elif isinstance(cgate, CHGate): # if all the control qubits have been activated, # we repeatedly apply the kronecker product of the Hadamard # with itself and then multiply the results for the original # state of the target qubits if len(subset) == num_controls: h_i = 1 / np.sqrt(2) * np.array([[1, 1], [1, -1]]) h_tot = np.array([1]) for _ in range(num_targets): h_tot = np.kron(h_tot, h_i) vec_exp = np.dot(h_tot, vec_exp) else: raise ValueError(f"Test not implement for gate: {cgate}") # append the remaining part of the state vec_exp = np.concatenate( (vec_exp, [0] * (2 ** (num_controls + num_ancillas + num_targets) - vec_exp.size)) ) f_i = state_fidelity(vec, vec_exp) self.assertAlmostEqual(f_i, 1)
def test_mcmt_v_chain_ancilla_test(self): """Test too few and too many ancillas for the MCMT V-chain mode.""" with self.subTest(msg='insufficient number of ancillas on gate'): qc = QuantumCircuit(5) mcmt = MCMTVChain(ZGate(), 3, 1) with self.assertRaises(QiskitError): qc.append(mcmt, [0, 1, 2, 3, 4]) with self.subTest(msg='insufficient number of ancillas on method'): qc = QuantumCircuit(5) mcmt = MCMTVChain(ZGate(), 3, 1) with self.assertRaises(QiskitError): qc.append(mcmt, [0, 1, 2, 3, 4], []) with self.subTest(msg='too many ancillas works on method'): qc = QuantumCircuit(8) qc.mcmt(CZGate(), [0, 1, 2], 3, [4, 5, 6, 7])
def control(num_ctrl_qubits=1, label=None, ctrl_state=None): qr_state = QuantumRegister(self.num_state_qubits + 1) if self.num_state_qubits > 1: qr_ancilla = AncillaRegister(max(1, self.num_state_qubits - 1)) qc_control = QuantumCircuit(qr_state, qr_ancilla, name="off_diags") else: qc_control = QuantumCircuit(qr_state, name="off_diags") qr_ancilla = None # Control will be qr[0] q_control = qr_state[0] qr = qr_state[1:] qc_control.cu(-2 * theta, 3 * np.pi / 2, np.pi / 2, 0, q_control, qr[0]) for i in range(0, self.num_state_qubits - 1): q_controls = [] q_controls.append(q_control) qc_control.cx(qr[i], qr[i + 1]) q_controls.append(qr[i + 1]) # Now we want controlled by 0 qc_control.x(qr[i]) for j in range(i, 0, -1): qc_control.cx(qr[i], qr[j - 1]) q_controls.append(qr[j - 1]) qc_control.x(qr[i]) # Multicontrolled x rotation if len(q_controls) > 1: ugate = UGate(-2 * theta, 3 * np.pi / 2, np.pi / 2) qc_control.append( MCMTVChain(ugate, len(q_controls), 1), q_controls[:] + [qr[i]] + qr_ancilla[:len(q_controls) - 1], ) else: qc_control.cu(-2 * theta, 3 * np.pi / 2, np.pi / 2, 0, q_controls[0], qr[i]) # Uncompute qc_control.x(qr[i]) for j in range(0, i): qc_control.cx(qr[i], qr[j]) qc_control.x(qr[i]) qc_control.cx(qr[i], qr[i + 1]) return qc_control
def _off_diag_circ(self, theta: float = 1) -> QuantumCircuit: """Circuit implementing the matrix consisting of entries in the off diagonals. Args: theta: Scale factor for the off diagonal entries (e.g. evolution_time/trotter_steps). Returns: The quantum circuit implementing the matrix consisting of entries in the off diagonals. """ theta *= self.off_diag qr = QuantumRegister(self.num_state_qubits) if self.num_state_qubits > 1: qr_ancilla = AncillaRegister(max(1, self.num_state_qubits - 2)) qc = QuantumCircuit(qr, qr_ancilla, name="off_diags") else: qc = QuantumCircuit(qr, name="off_diags") qr_ancilla = None qc.u(-2 * theta, 3 * np.pi / 2, np.pi / 2, qr[0]) for i in range(0, self.num_state_qubits - 1): q_controls = [] qc.cx(qr[i], qr[i + 1]) q_controls.append(qr[i + 1]) # Now we want controlled by 0 qc.x(qr[i]) for j in range(i, 0, -1): qc.cx(qr[i], qr[j - 1]) q_controls.append(qr[j - 1]) qc.x(qr[i]) # Multicontrolled rotation if len(q_controls) > 1: ugate = UGate(-2 * theta, 3 * np.pi / 2, np.pi / 2) qc.append( MCMTVChain(ugate, len(q_controls), 1), q_controls[:] + [qr[i]] + qr_ancilla[:len(q_controls) - 1], ) else: qc.cu(-2 * theta, 3 * np.pi / 2, np.pi / 2, 0, q_controls[0], qr[i]) # Uncompute qc.x(qr[i]) for j in range(0, i): qc.cx(qr[i], qr[j]) qc.x(qr[i]) qc.cx(qr[i], qr[i + 1]) # pylint: disable=unused-argument def control(num_ctrl_qubits=1, label=None, ctrl_state=None): qr_state = QuantumRegister(self.num_state_qubits + 1) if self.num_state_qubits > 1: qr_ancilla = AncillaRegister(max(1, self.num_state_qubits - 1)) qc_control = QuantumCircuit(qr_state, qr_ancilla, name="off_diags") else: qc_control = QuantumCircuit(qr_state, name="off_diags") qr_ancilla = None # Control will be qr[0] q_control = qr_state[0] qr = qr_state[1:] qc_control.cu(-2 * theta, 3 * np.pi / 2, np.pi / 2, 0, q_control, qr[0]) for i in range(0, self.num_state_qubits - 1): q_controls = [] q_controls.append(q_control) qc_control.cx(qr[i], qr[i + 1]) q_controls.append(qr[i + 1]) # Now we want controlled by 0 qc_control.x(qr[i]) for j in range(i, 0, -1): qc_control.cx(qr[i], qr[j - 1]) q_controls.append(qr[j - 1]) qc_control.x(qr[i]) # Multicontrolled x rotation if len(q_controls) > 1: ugate = UGate(-2 * theta, 3 * np.pi / 2, np.pi / 2) qc_control.append( MCMTVChain(ugate, len(q_controls), 1), q_controls[:] + [qr[i]] + qr_ancilla[:len(q_controls) - 1], ) else: qc_control.cu(-2 * theta, 3 * np.pi / 2, np.pi / 2, 0, q_controls[0], qr[i]) # Uncompute qc_control.x(qr[i]) for j in range(0, i): qc_control.cx(qr[i], qr[j]) qc_control.x(qr[i]) qc_control.cx(qr[i], qr[i + 1]) return qc_control qc.control = control return qc