from qiskit.circuit import QuantumCircuit from qiskit.compiler import transpile, assemble from qiskit.providers.ibmq import least_busy from qiskit.tools.monitor import job_monitor try: IBMQ.load_accounts() except: print("""WARNING: There's no connection with the API for remote backends. Have you initialized a file with your personal token? For now, there's only access to local simulator backends...""") try: # Making first circuit: bell state qc1 = QuantumCircuit(2, 2, name="bell") qc1.h(0) qc1.cx(0, 1) qc1.measure([0, 1], [0, 1]) # Making another circuit: superpositions qc2 = QuantumCircuit(2, 2, name="superposition") qc2.h([0, 1]) qc2.measure([0, 1], [0, 1]) # Setting up the backend print("(Aer Backends)") for backend in BasicAer.backends(): print(backend.status()) qasm_simulator = BasicAer.get_backend('qasm_simulator')
def test_paper_example(self): """Test synthesis of a diagonal operator from the paper. The diagonal operator in Example 4.2 U|x> = e^(2.pi.i.f(x))|x>, where f(x) = 1/8*(x1^x2 + x0 + x0^x3 + x0^x1^x2 + x0^x1^x3 + x0^x1) The algorithm should take the following matrix as an input: S = [[0, 1, 1, 1, 1, 1], [1, 0, 0, 1, 1, 1], [1, 0, 0, 1, 0, 0], [0, 0, 1, 0, 1, 0]] and only T gates as phase rotations, And should return the following circuit (or an equivalent one): ┌───┐┌───┐ ┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐ ┌───┐ q_0: |0>┤ T ├┤ X ├─────┤ T ├┤ X ├┤ X ├┤ T ├┤ X ├┤ T ├┤ X ├┤ T ├┤ X ├─────┤ X ├ ├───┤└─┬─┘┌───┐└───┘└─┬─┘└─┬─┘└───┘└─┬─┘└───┘└─┬─┘└───┘└─┬─┘┌───┐└─┬─┘ q_1: |0>┤ X ├──┼──┤ T ├───────■────┼─────────┼─────────┼─────────■──┤ X ├──┼── └─┬─┘ │ └───┘ │ │ │ └─┬─┘ │ q_2: |0>──■────┼───────────────────┼─────────■─────────┼──────────────■────┼── │ │ │ │ q_3: |0>───────■───────────────────■───────────────────■───────────────────■── """ cnots = [[0, 1, 1, 1, 1, 1], [1, 0, 0, 1, 1, 1], [1, 0, 0, 1, 0, 0], [0, 0, 1, 0, 1, 0]] angles = ['t'] * 6 c_gray = graysynth(cnots, angles) unitary_gray = UnitaryGate(Operator(c_gray)) # Create the circuit displayed above: q = QuantumRegister(4, 'q') c_compare = QuantumCircuit(q) c_compare.t(q[0]) c_compare.cx(q[2], q[1]) c_compare.cx(q[3], q[0]) c_compare.t(q[0]) c_compare.t(q[1]) c_compare.cx(q[1], q[0]) c_compare.cx(q[3], q[0]) c_compare.t(q[0]) c_compare.cx(q[2], q[0]) c_compare.t(q[0]) c_compare.cx(q[3], q[0]) c_compare.t(q[0]) c_compare.cx(q[1], q[0]) c_compare.cx(q[2], q[1]) c_compare.cx(q[3], q[0]) unitary_compare = UnitaryGate(Operator(c_compare)) # Check if the two circuits are equivalent self.assertEqual(unitary_gray, unitary_compare)
def test_parameter_binding_on_listop(self): """Test passing a ListOp with differing parameters works with the circuit sampler.""" try: from qiskit.providers.aer import Aer except Exception as ex: # pylint: disable=broad-except self.skipTest( "Aer doesn't appear to be installed. Error: '{}'".format( str(ex))) return x, y = Parameter('x'), Parameter('y') circuit1 = QuantumCircuit(1) circuit1.p(0.2, 0) circuit2 = QuantumCircuit(1) circuit2.p(x, 0) circuit3 = QuantumCircuit(1) circuit3.p(y, 0) bindings = {x: -0.4, y: 0.4} listop = ListOp( [StateFn(circuit) for circuit in [circuit1, circuit2, circuit3]]) sampler = CircuitSampler(Aer.get_backend('qasm_simulator')) sampled = sampler.convert(listop, params=bindings) self.assertTrue(all(len(op.parameters) == 0 for op in sampled.oplist))
def __init__(self): """Initialize Pauli preparation basis""" # |0> Zp rotation prep_zp = QuantumCircuit(1, name="PauliPrepZp") # |1> Zm rotation prep_zm = QuantumCircuit(1, name="PauliPrepZm") prep_zm.append(XGate(), [0]) # |+> Xp rotation prep_xp = QuantumCircuit(1, name="PauliPrepXp") prep_xp.append(HGate(), [0]) # |+i> Yp rotation prep_yp = QuantumCircuit(1, name="PauliPrepYp") prep_yp.append(HGate(), [0]) prep_yp.append(SGate(), [0]) super().__init__("PauliPreparationBasis", [prep_zp, prep_zm, prep_xp, prep_yp])
def test_patel_markov_hayes(self): """Test synthesis of a small linear circuit (example from paper, Figure 3). The algorithm should take the following matrix as an input: S = [[1, 1, 0, 0, 0, 0], [1, 0, 0, 1, 1, 0], [0, 1, 0, 0, 1, 0], [1, 1, 1, 1, 1, 1], [1, 1, 0, 1, 1, 1], [0, 0, 1, 1, 1, 0]] And should return the following circuit (or an equivalent one): ┌───┐ q_0: |0>──────────┤ X ├──────────────────────────────────────────■────■────■── └─┬─┘┌───┐ ┌─┴─┐ │ │ q_1: |0>────────────■──┤ X ├────────────────────────────────■──┤ X ├──┼────┼── ┌───┐ └─┬─┘┌───┐ ┌───┐ ┌─┴─┐└───┘ │ │ q_2: |0>─────┤ X ├───────┼──┤ X ├───────■──┤ X ├───────■──┤ X ├───────┼────┼── ┌───┐└─┬─┘ │ └─┬─┘┌───┐┌─┴─┐└─┬─┘ │ └───┘ │ ┌─┴─┐ q_3: |0>┤ X ├──┼─────────■────┼──┤ X ├┤ X ├──■────■────┼──────────────┼──┤ X ├ └─┬─┘ │ │ └─┬─┘├───┤ │ ┌─┴─┐ ┌─┴─┐└───┘ q_4: |0>──■────┼──────────────■────■──┤ X ├───────┼──┤ X ├──────────┤ X ├───── │ └─┬─┘ ┌─┴─┐└───┘ └───┘ q_5: |0>───────■────────────────────────■───────┤ X ├───────────────────────── └───┘ """ state = [[1, 1, 0, 0, 0, 0], [1, 0, 0, 1, 1, 0], [0, 1, 0, 0, 1, 0], [1, 1, 1, 1, 1, 1], [1, 1, 0, 1, 1, 1], [0, 0, 1, 1, 1, 0]] c_patel = cnot_synth(state) # Create the circuit displayed above: q = QuantumRegister(6, 'q') c_compare = QuantumCircuit(q) c_compare.cx(q[4], q[3]) c_compare.cx(q[5], q[2]) c_compare.cx(q[1], q[0]) c_compare.cx(q[3], q[1]) c_compare.cx(q[4], q[2]) c_compare.cx(q[4], q[3]) c_compare.cx(q[5], q[4]) c_compare.cx(q[2], q[3]) c_compare.cx(q[3], q[2]) c_compare.cx(q[3], q[5]) c_compare.cx(q[2], q[4]) c_compare.cx(q[1], q[2]) c_compare.cx(q[0], q[1]) c_compare.cx(q[0], q[4]) c_compare.cx(q[0], q[3]) # Check if the two circuits are equivalent self.assertEqual(c_patel, c_compare)
def test_apply_cx_to_non_register(self): """test applying ccx to non-register raises""" qr = QuantumRegister(10) cr = ClassicalRegister(10) qc = QuantumCircuit(qr, cr) self.assertRaises(CircuitError, qc.cx, qc[0:2], qc[2:4])
def __init__(self): # |0> Zp rotation prep_zp = QuantumCircuit(1, name="PauliPrepZp") # |1> Zm rotation prep_zm = QuantumCircuit(1, name="PauliPrepZm") prep_zm.append(XGate(), [0]) # |+> Xp rotation prep_xp = QuantumCircuit(1, name="PauliPrepXp") prep_xp.append(HGate(), [0]) # |-> Xm rotation prep_xm = QuantumCircuit(1, name="PauliPrepXm") prep_xm.append(HGate(), [0]) prep_xm.append(ZGate(), [0]) # |+i> Yp rotation prep_yp = QuantumCircuit(1, name="PauliPrepYp") prep_yp.append(HGate(), [0]) prep_yp.append(SGate(), [0]) # |-i> Ym rotation prep_ym = QuantumCircuit(1, name="PauliPrepYm") prep_ym.append(HGate(), [0]) prep_ym.append(SdgGate(), [0]) super().__init__( "Pauli6PreparationBasis", [ prep_zp, prep_zm, prep_xp, prep_xm, prep_yp, prep_ym, ], )
def test_dag_depth2(self): """Test barrier increases DAG depth """ q = QuantumRegister(5, 'q') c = ClassicalRegister(1, 'c') qc = QuantumCircuit(q, c) qc.h(q[0]) qc.cx(q[0], q[4]) qc.x(q[2]) qc.x(q[2]) qc.x(q[2]) qc.x(q[4]) qc.cx(q[4], q[1]) qc.barrier(q) qc.measure(q[1], c[0]) dag = circuit_to_dag(qc) self.assertEqual(dag.depth(), 6)
def test_dag_depth3(self): """Test DAG depth for silly circuit. """ q = QuantumRegister(6, 'q') c = ClassicalRegister(1, 'c') qc = QuantumCircuit(q, c) qc.h(q[0]) qc.cx(q[0], q[1]) qc.cx(q[1], q[2]) qc.cx(q[2], q[3]) qc.cx(q[3], q[4]) qc.cx(q[4], q[5]) qc.barrier(q[0]) qc.barrier(q[0]) qc.measure(q[0], c[0]) dag = circuit_to_dag(qc) self.assertEqual(dag.depth(), 6)
def test_nested_parameters_are_recognised(self): """Verify that parameters added inside a control-flow operator get added to the outer circuit table.""" x, y = Parameter("x"), Parameter("y") with self.subTest("if/else"): body1 = QuantumCircuit(1, 1) body1.rx(x, 0) body2 = QuantumCircuit(1, 1) body2.rx(y, 0) main = QuantumCircuit(1, 1) main.if_else((main.clbits[0], 0), body1, body2, [0], [0]) self.assertEqual({x, y}, set(main.parameters)) with self.subTest("while"): body = QuantumCircuit(1, 1) body.rx(x, 0) main = QuantumCircuit(1, 1) main.while_loop((main.clbits[0], 0), body, [0], [0]) self.assertEqual({x}, set(main.parameters)) with self.subTest("for"): body = QuantumCircuit(1, 1) body.rx(x, 0) main = QuantumCircuit(1, 1) main.for_loop(range(1), None, body, [0], [0]) self.assertEqual({x}, set(main.parameters))
def test_nested_parameters_can_be_assigned(self): """Verify that parameters added inside a control-flow operator can be assigned by calls to the outer circuit.""" x, y = Parameter("x"), Parameter("y") with self.subTest("if/else"): body1 = QuantumCircuit(1, 1) body1.rx(x, 0) body2 = QuantumCircuit(1, 1) body2.rx(y, 0) test = QuantumCircuit(1, 1) test.if_else((test.clbits[0], 0), body1, body2, [0], [0]) self.assertEqual({x, y}, set(test.parameters)) assigned = test.assign_parameters({x: math.pi, y: 0.5 * math.pi}) self.assertEqual(set(), set(assigned.parameters)) expected = QuantumCircuit(1, 1) expected.if_else( (expected.clbits[0], 0), body1.assign_parameters({x: math.pi}), body2.assign_parameters({y: 0.5 * math.pi}), [0], [0], ) self.assertEqual(assigned, expected) with self.subTest("while"): body = QuantumCircuit(1, 1) body.rx(x, 0) test = QuantumCircuit(1, 1) test.while_loop((test.clbits[0], 0), body, [0], [0]) self.assertEqual({x}, set(test.parameters)) assigned = test.assign_parameters({x: math.pi}) self.assertEqual(set(), set(assigned.parameters)) expected = QuantumCircuit(1, 1) expected.while_loop( (expected.clbits[0], 0), body.assign_parameters({x: math.pi}), [0], [0], ) self.assertEqual(assigned, expected) with self.subTest("for"): body = QuantumCircuit(1, 1) body.rx(x, 0) test = QuantumCircuit(1, 1) test.for_loop(range(1), None, body, [0], [0]) self.assertEqual({x}, set(test.parameters)) assigned = test.assign_parameters({x: math.pi}) self.assertEqual(set(), set(assigned.parameters)) expected = QuantumCircuit(1, 1) expected.for_loop( range(1), None, body.assign_parameters({x: math.pi}), [0], [0], ) self.assertEqual(assigned, expected)
def _build(self): # do not build the circuit if _data is already populated if self._data is not None: return self._data = [] # check whether the configuration is valid self._check_configuration() circuit = QuantumCircuit(*self.qregs, name=self.name) qr_state = circuit.qubits[:self.num_state_qubits] qr_target = circuit.qubits[self.num_state_qubits] rotation_coeffs = self._get_rotation_coefficients() if self.basis == "x": circuit.rx(self.coeffs[0], qr_target) elif self.basis == "y": circuit.ry(self.coeffs[0], qr_target) else: circuit.rz(self.coeffs[0], qr_target) for c in rotation_coeffs: qr_control = [] if self.reverse: for i, _ in enumerate(c): if c[i] > 0: qr_control.append(qr_state[qr_state.size - i - 1]) else: for i, _ in enumerate(c): if c[i] > 0: qr_control.append(qr_state[i]) # apply controlled rotations if len(qr_control) > 1: if self.basis == "x": circuit.mcrx(rotation_coeffs[c], qr_control, qr_target) elif self.basis == "y": circuit.mcry(rotation_coeffs[c], qr_control, qr_target) else: circuit.mcrz(rotation_coeffs[c], qr_control, qr_target) elif len(qr_control) == 1: if self.basis == "x": circuit.crx(rotation_coeffs[c], qr_control[0], qr_target) elif self.basis == "y": circuit.cry(rotation_coeffs[c], qr_control[0], qr_target) else: circuit.crz(rotation_coeffs[c], qr_control[0], qr_target) self.append(circuit.to_gate(), self.qubits)
def convert( self, operator: CircuitStateFn, params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], ) -> ListOp: r""" Args: operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` for which we compute the QFI. params: The parameters :math:`\omega` with respect to which we are computing the QFI. Returns: A ``ListOp[ListOp]`` where the operator at position ``[k][l]`` corresponds to the matrix element :math:`k, l` of the QFI. Raises: TypeError: If ``operator`` is an unsupported type. """ # QFI & phase fix observable qfi_observable = StateFn(4 * self._aux_meas_op ^ (I ^ operator.num_qubits), is_measurement=True) # Check if the given operator corresponds to a quantum state given as a circuit. if not isinstance(operator, CircuitStateFn): raise TypeError( "LinCombFull is only compatible with states that are given as " f"CircuitStateFn, not {type(operator)}") # If a single parameter is given wrap it into a list. if isinstance(params, ParameterExpression): params = [params] elif isinstance(params, ParameterVector): params = params[:] # unroll to list if self._phase_fix: # First, the operators are computed which can compensate for a potential phase-mismatch # between target and trained state, i.e.〈ψ|∂lψ〉 phase_fix_observable = I ^ operator.num_qubits gradient_states = LinComb( aux_meas_op=(Z - 1j * Y))._gradient_states( operator, meas_op=phase_fix_observable, target_params=params, open_ctrl=False, trim_after_grad_gate=True, ) # pylint: disable=unidiomatic-typecheck if type(gradient_states) == ListOp: phase_fix_states = gradient_states.oplist else: phase_fix_states = [gradient_states] # Get 4 * Re[〈∂kψ|∂lψ] qfi_operators = [] # Add a working qubit qr_work = QuantumRegister(1, "work_qubit") state_qc = QuantumCircuit(*operator.primitive.qregs, qr_work) state_qc.h(qr_work) # unroll separately from the H gate since we need the H gate to be the first # operation in the data attributes of the circuit unrolled = LinComb._transpile_to_supported_operations( operator.primitive, LinComb.SUPPORTED_GATES) state_qc.compose(unrolled, inplace=True) # Get the circuits needed to compute〈∂iψ|∂jψ〉 for i, param_i in enumerate(params): # loop over parameters qfi_ops = [] for j, param_j in enumerate(params[i:], i): # Get the gates of the quantum state which are parameterized by param_i qfi_op = [] param_gates_i = state_qc._parameter_table[param_i] for gate_i, idx_i in param_gates_i: grad_coeffs_i, grad_gates_i = LinComb._gate_gradient_dict( gate_i)[idx_i] # get the location of gate_i, used for trimming location_i = None for idx, (op, _, _) in enumerate(state_qc._data): if op is gate_i: location_i = idx break for grad_coeff_i, grad_gate_i in zip( grad_coeffs_i, grad_gates_i): # Get the gates of the quantum state which are parameterized by param_j param_gates_j = state_qc._parameter_table[param_j] for gate_j, idx_j in param_gates_j: grad_coeffs_j, grad_gates_j = LinComb._gate_gradient_dict( gate_j)[idx_j] # get the location of gate_j, used for trimming location_j = None for idx, (op, _, _) in enumerate(state_qc._data): if op is gate_j: location_j = idx break for grad_coeff_j, grad_gate_j in zip( grad_coeffs_j, grad_gates_j): grad_coeff_ij = np.conj( grad_coeff_i) * grad_coeff_j qfi_circuit = LinComb.apply_grad_gate( state_qc, gate_i, idx_i, grad_gate_i, grad_coeff_ij, qr_work, open_ctrl=True, trim_after_grad_gate=(location_j < location_i), ) # create a copy of the original circuit with the same registers qfi_circuit = LinComb.apply_grad_gate( qfi_circuit, gate_j, idx_j, grad_gate_j, 1, qr_work, open_ctrl=False, trim_after_grad_gate=(location_j >= location_i), ) qfi_circuit.h(qr_work) # Convert the quantum circuit into a CircuitStateFn and add the # coefficients i, j and the original operator coefficient coeff = operator.coeff coeff *= np.sqrt( np.abs(grad_coeff_i) * np.abs(grad_coeff_j)) state = CircuitStateFn(qfi_circuit, coeff=coeff) param_grad = 1 for gate, idx, param in zip( [gate_i, gate_j], [idx_i, idx_j], [param_i, param_j]): param_expression = gate.params[idx] param_grad *= param_expression.gradient( param) meas = param_grad * qfi_observable term = meas @ state qfi_op.append(term) # Compute −4 * Re(〈∂kψ|ψ〉〈ψ|∂lψ〉) def phase_fix_combo_fn(x): return -4 * np.real(x[0] * np.conjugate(x[1])) if self._phase_fix: phase_fix_op = ListOp( [phase_fix_states[i], phase_fix_states[j]], combo_fn=phase_fix_combo_fn) # Add the phase fix quantities to the entries of the QFI # Get 4 * Re[〈∂kψ|∂lψ〉−〈∂kψ|ψ〉〈ψ|∂lψ〉] qfi_ops += [SummedOp(qfi_op) + phase_fix_op] else: qfi_ops += [SummedOp(qfi_op)] qfi_operators.append(ListOp(qfi_ops)) # Return estimate of the full QFI -- A QFI is by definition positive semi-definite. return ListOp(qfi_operators, combo_fn=triu_to_dense)
def test_compose_works(self): """Test that the circuit is constructed when compose is called.""" qc = QuantumCircuit(3) qc.x([0, 1, 2]) circuit = MockBlueprint(3) circuit.compose(qc, inplace=True) reference = QuantumCircuit(3) reference.rx(list(circuit.parameters)[0], 0) reference.h([0, 1, 2]) reference.x([0, 1, 2]) self.assertEqual(reference, circuit)
def test_apply_ccx_to_slice(self): """test applying ccx to register slice""" qcontrol = QuantumRegister(10) qcontrol2 = QuantumRegister(10) qtarget = QuantumRegister(5) qtarget2 = QuantumRegister(10) qc = QuantumCircuit(qcontrol, qtarget) # test slice with skip and full register target qc.ccx(qcontrol[1::2], qcontrol[0::2], qtarget) self.assertEqual(len(qc.data), 5) for i, ictl, (gate, qargs, _) in zip(range(len(qc.data)), range(0, 10, 2), qc.data): self.assertEqual(gate.name, 'ccx') self.assertEqual(len(qargs), 3) self.assertIn(qargs[0].index, [ictl, ictl + 1]) self.assertIn(qargs[1].index, [ictl, ictl + 1]) self.assertEqual(qargs[2].index, i) # test decrementing slice qc = QuantumCircuit(qcontrol, qtarget) qc.ccx(qcontrol[2:0:-1], qcontrol[4:6], qtarget[0:2]) self.assertEqual(len(qc.data), 2) for (gate, qargs, _), ictl1, ictl2, itgt in zip(qc.data, range(2, 0, -1), range(4, 6), range(0, 2)): self.assertEqual(gate.name, 'ccx') self.assertEqual(len(qargs), 3) self.assertEqual(qargs[0].index, ictl1) self.assertEqual(qargs[1].index, ictl2) self.assertEqual(qargs[2].index, itgt) # test register expansion in ccx qc = QuantumCircuit(qcontrol, qcontrol2, qtarget2) qc.ccx(qcontrol, qcontrol2, qtarget2) for i, (gate, qargs, _) in enumerate(qc.data): self.assertEqual(gate.name, 'ccx') self.assertEqual(len(qargs), 3) self.assertEqual(qargs[0].index, i) self.assertEqual(qargs[1].index, i) self.assertEqual(qargs[2].index, i)
def setUp(self): qr1 = QuantumRegister(4) qr2 = QuantumRegister(2) circ = QuantumCircuit(qr1, qr2) circ.h(qr1[0]) circ.cx(qr1[2], qr1[3]) circ.h(qr1[2]) circ.t(qr1[2]) circ.ch(qr1[2], qr1[1]) circ.u2(0.1, 0.2, qr1[3]) circ.ccx(qr2[0], qr2[1], qr1[0]) self.dag = circuit_to_dag(circ)
def test_apply_ccx_to_empty_slice(self): """test applying ccx to non-register raises""" qr = QuantumRegister(10) cr = ClassicalRegister(10) qc = QuantumCircuit(qr, cr) self.assertRaises(CircuitError, qc.ccx, qr[2:0], qr[4:2], qr[7:5])
def setUp(self): self.qr1 = QuantumRegister(4, 'qr1') self.qr2 = QuantumRegister(2, 'qr2') circ1 = QuantumCircuit(self.qr2, self.qr1) circ1.h(self.qr1[0]) circ1.cx(self.qr1[2], self.qr1[3]) circ1.h(self.qr1[2]) circ1.t(self.qr1[2]) circ1.ch(self.qr1[2], self.qr1[1]) circ1.u2(0.1, 0.2, self.qr1[3]) circ1.ccx(self.qr2[0], self.qr2[1], self.qr1[0]) self.dag1 = circuit_to_dag(circ1)
def test_measure_slice(self): """test measure slice""" qr = QuantumRegister(10) cr = ClassicalRegister(10) cr2 = ClassicalRegister(5) qc = QuantumCircuit(qr, cr) qc.measure(qr[0:2], cr[2:4]) for (gate, qargs, cargs), ictrl, itgt in zip(qc.data, range(0, 2), range(2, 4)): self.assertEqual(gate.name, 'measure') self.assertEqual(len(qargs), 1) self.assertEqual(len(cargs), 1) self.assertEqual(qargs[0].index, ictrl) self.assertEqual(cargs[0].index, itgt) # test single element slice qc = QuantumCircuit(qr, cr) qc.measure(qr[0:1], cr[2:3]) for (gate, qargs, cargs), ictrl, itgt in zip(qc.data, range(0, 1), range(2, 3)): self.assertEqual(gate.name, 'measure') self.assertEqual(len(qargs), 1) self.assertEqual(len(cargs), 1) self.assertEqual(qargs[0].index, ictrl) self.assertEqual(cargs[0].index, itgt) # test tuple qc = QuantumCircuit(qr, cr) qc.measure(qr[0], cr[2]) self.assertEqual(len(qc.data), 1) op, qargs, cargs = qc.data[0] self.assertEqual(op.name, 'measure') self.assertEqual(len(qargs), 1) self.assertEqual(len(cargs), 1) self.assertTrue(isinstance(qargs[0], Qubit)) self.assertTrue(isinstance(cargs[0], Clbit)) self.assertEqual(qargs[0].index, 0) self.assertEqual(cargs[0].index, 2) # test full register qc = QuantumCircuit(qr, cr) qc.measure(qr, cr) for (gate, qargs, cargs), ictrl, itgt in zip(qc.data, range(len(qr)), range(len(cr))): self.assertEqual(gate.name, 'measure') self.assertEqual(len(qargs), 1) self.assertEqual(len(cargs), 1) self.assertEqual(qargs[0].index, ictrl) self.assertEqual(cargs[0].index, itgt) # test mix slice full register qc = QuantumCircuit(qr, cr2) qc.measure(qr[::2], cr2) for (gate, qargs, cargs), ictrl, itgt in zip(qc.data, range(0, 10, 2), range(len(cr2))): self.assertEqual(gate.name, 'measure') self.assertEqual(len(qargs), 1) self.assertEqual(len(cargs), 1) self.assertEqual(qargs[0].index, ictrl) self.assertEqual(cargs[0].index, itgt)
def test_dag_eq(self): """DAG equivalence check: True.""" circ2 = QuantumCircuit(self.qr1, self.qr2) circ2.cx(self.qr1[2], self.qr1[3]) circ2.u2(0.1, 0.2, self.qr1[3]) circ2.h(self.qr1[0]) circ2.h(self.qr1[2]) circ2.t(self.qr1[2]) circ2.ch(self.qr1[2], self.qr1[1]) circ2.ccx(self.qr2[0], self.qr2[1], self.qr1[0]) dag2 = circuit_to_dag(circ2) self.assertEqual(self.dag1, dag2)
def __init__(self): """Initialize Pauli measurement basis""" # Z-meas rotation meas_z = QuantumCircuit(1, name="PauliMeasZ") # X-meas rotation meas_x = QuantumCircuit(1, name="PauliMeasX") meas_x.append(HGate(), [0]) # Y-meas rotation meas_y = QuantumCircuit(1, name="PauliMeasY") meas_y.append(SdgGate(), [0]) meas_y.append(HGate(), [0]) super().__init__("PauliMeasurementBasis", [meas_z, meas_x, meas_y])
def test_dag_neq_same_topology(self): """DAG equivalence check: False. Same topology.""" circ2 = QuantumCircuit(self.qr1, self.qr2) circ2.cx(self.qr1[2], self.qr1[3]) circ2.u2(0.1, 0.2, self.qr1[3]) circ2.h(self.qr1[0]) circ2.h(self.qr1[2]) circ2.t(self.qr1[2]) circ2.cx(self.qr1[2], self.qr1[1]) # <--- The difference: ch(qr1[2], qr1[1]) circ2.ccx(self.qr2[0], self.qr2[1], self.qr1[0]) dag2 = circuit_to_dag(circ2) self.assertNotEqual(self.dag1, dag2)
def test_node_middle_of_blocks(self): """Test that a node surrounded by blocks stays in the same place This is a larger test to ensure multiple blocks can all be collected and added back in the correct order. blocks = [['cx', 'id'], ['cx', 'id'], ['id', 'cx'], ['id', 'cx']] q_0: |0>──■───────────────────■── ┌─┴─┐┌────┐ ┌────┐┌─┴─┐ q_1: |0>┤ X ├┤ Id ├─X─┤ Id ├┤ X ├ ├───┤├────┤ │ ├────┤├───┤ q_2: |0>┤ X ├┤ Id ├─X─┤ Id ├┤ X ├ └─┬─┘└────┘ └────┘└─┬─┘ q_3: |0>──■───────────────────■── """ qc = QuantumCircuit(4) qc.cx(0, 1) qc.cx(3, 2) qc.i(1) qc.i(2) qc.swap(1, 2) qc.i(1) qc.i(2) qc.cx(0, 1) qc.cx(3, 2) pass_manager = PassManager() pass_manager.append(Collect2qBlocks()) pass_manager.append(ConsolidateBlocks()) qc1 = pass_manager.run(qc) self.assertEqual(qc, qc1)
def test_dag_depth1(self): """Test DAG depth #1 """ qr1 = QuantumRegister(3, 'q1') qr2 = QuantumRegister(2, 'q2') c = ClassicalRegister(5, 'c') qc = QuantumCircuit(qr1, qr2, c) qc.h(qr1[0]) qc.h(qr1[1]) qc.h(qr1[2]) qc.h(qr2[0]) qc.h(qr2[1]) qc.ccx(qr2[1], qr1[0], qr2[0]) qc.cx(qr1[0], qr1[1]) qc.cx(qr1[1], qr2[1]) qc.cx(qr2[1], qr1[2]) qc.cx(qr1[2], qr2[0]) dag = circuit_to_dag(qc) self.assertEqual(dag.depth(), 6)
def test_gray_synth(self): """Test synthesis of a small parity network via gray_synth. The algorithm should take the following matrix as an input: S = [[0, 1, 1, 0, 1, 1], [0, 1, 1, 0, 1, 0], [0, 0, 0, 1, 1, 0], [1, 0, 0, 1, 1, 1], [0, 1, 0, 0, 1, 0], [0, 1, 0, 0, 1, 0]] Along with some rotation angles: ['s', 't', 'z', 's', 't', 't']) which together specify the Fourier expansion in the sum-over-paths representation of a quantum circuit. And should return the following circuit (or an equivalent one): ┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐ q_0: |0>──────────┤ X ├┤ X ├┤ T ├┤ X ├┤ X ├┤ X ├┤ X ├┤ T ├┤ X ├┤ T ├┤ X ├┤ X ├┤ Z ├┤ X ├ └─┬─┘└─┬─┘└───┘└─┬─┘└─┬─┘└─┬─┘└─┬─┘└───┘└─┬─┘└───┘└─┬─┘└─┬─┘└───┘└─┬─┘ q_1: |0>────────────┼────┼─────────■────┼────┼────┼─────────┼─────────┼────┼─────────■── │ │ │ │ │ │ │ │ q_2: |0>───────■────■────┼──────────────■────┼────┼─────────┼────■────┼────┼──────────── ┌───┐┌─┴─┐┌───┐ │ │ │ │ ┌─┴─┐ │ │ q_3: |0>┤ S ├┤ X ├┤ S ├──■───────────────────┼────┼─────────■──┤ X ├──┼────┼──────────── └───┘└───┘└───┘ │ │ └───┘ │ │ q_4: |0>─────────────────────────────────────■────┼───────────────────■────┼──────────── │ │ q_5: |0>──────────────────────────────────────────■────────────────────────■──────────── """ cnots = [[0, 1, 1, 0, 1, 1], [0, 1, 1, 0, 1, 0], [0, 0, 0, 1, 1, 0], [1, 0, 0, 1, 1, 1], [0, 1, 0, 0, 1, 0], [0, 1, 0, 0, 1, 0]] angles = ['s', 't', 'z', 's', 't', 't'] c_gray = graysynth(cnots, angles) unitary_gray = UnitaryGate(Operator(c_gray)) # Create the circuit displayed above: q = QuantumRegister(6, 'q') c_compare = QuantumCircuit(q) c_compare.s(q[3]) c_compare.cx(q[2], q[3]) c_compare.s(q[3]) c_compare.cx(q[2], q[0]) c_compare.cx(q[3], q[0]) c_compare.t(q[0]) c_compare.cx(q[1], q[0]) c_compare.cx(q[2], q[0]) c_compare.cx(q[4], q[0]) c_compare.cx(q[5], q[0]) c_compare.t(q[0]) c_compare.cx(q[3], q[0]) c_compare.t(q[0]) c_compare.cx(q[2], q[3]) c_compare.cx(q[4], q[0]) c_compare.cx(q[5], q[0]) c_compare.z(q[0]) c_compare.cx(q[1], q[0]) unitary_compare = UnitaryGate(Operator(c_compare)) # Check if the two circuits are equivalent self.assertEqual(unitary_gray, unitary_compare)
def _dec_ucrot(self): """ finds a decomposition of a UC rotation gate into elementary gates (C-NOTs and single-qubit rotations). """ q = QuantumRegister(self.num_qubits) circuit = QuantumCircuit(q) q_target = q[0] q_controls = q[1:] if not q_controls: # equivalent to: if len(q_controls) == 0 if self.rot_axes == "X": if np.abs(self.params[0]) > _EPS: circuit.rx(self.params[0], q_target) if self.rot_axes == "Y": if np.abs(self.params[0]) > _EPS: circuit.ry(self.params[0], q_target) if self.rot_axes == "Z": if np.abs(self.params[0]) > _EPS: circuit.rz(self.params[0], q_target) else: # First, we find the rotation angles of the single-qubit rotations acting # on the target qubit angles = self.params.copy() UCRot._dec_uc_rotations(angles, 0, len(angles), False) # Now, it is easy to place the C-NOT gates to get back the full decomposition.s for (i, angle) in enumerate(angles): if self.rot_axes == "X": if np.abs(angle) > _EPS: circuit.rx(angle, q_target) if self.rot_axes == "Y": if np.abs(angle) > _EPS: circuit.ry(angle, q_target) if self.rot_axes == "Z": if np.abs(angle) > _EPS: circuit.rz(angle, q_target) # Determine the index of the qubit we want to control the C-NOT gate. # Note that it corresponds # to the number of trailing zeros in the binary representaiton of i+1 if not i == len(angles) - 1: binary_rep = np.binary_repr(i + 1) q_contr_index = len(binary_rep) - len( binary_rep.rstrip('0')) else: # Handle special case: q_contr_index = len(q_controls) - 1 # For X rotations, we have to additionally place some Ry gates around the # C-NOT gates. They change the basis of the NOT operation, such that the # decomposition of for uniformly controlled X rotations works correctly by symmetry # with the decomposition of uniformly controlled Z or Y rotations if self.rot_axes == "X": circuit.ry(np.pi / 2, q_target) circuit.cx(q_controls[q_contr_index], q_target) if self.rot_axes == "X": circuit.ry(-np.pi / 2, q_target) return circuit
def rzx_xz(theta: float = None): """Template for CX - RXGate - CX.""" if theta is None: theta = Parameter('ϴ') qc = QuantumCircuit(2) qc.cx(1, 0) qc.rx(theta, 1) qc.cx(1, 0) qc.rz(np.pi / 2, 0) qc.rx(np.pi / 2, 0) qc.rz(np.pi / 2, 0) qc.rzx(-1 * theta, 0, 1) qc.rz(np.pi / 2, 0) qc.rx(np.pi / 2, 0) qc.rz(np.pi / 2, 0) return qc
def _multiplex(self, target_gate, list_of_angles, last_cnot=True): """ Return a recursive implementation of a multiplexor circuit, where each instruction itself has a decomposition based on smaller multiplexors. The LSB is the multiplexor "data" and the other bits are multiplexor "select". Args: target_gate (Gate): Ry or Rz gate to apply to target qubit, multiplexed over all other "select" qubits list_of_angles (list[float]): list of rotation angles to apply Ry and Rz last_cnot (bool): add the last cnot if last_cnot = True Returns: DAGCircuit: the circuit implementing the multiplexor's action """ list_len = len(list_of_angles) local_num_qubits = int(math.log2(list_len)) + 1 q = QuantumRegister(local_num_qubits) circuit = QuantumCircuit(q, name="multiplex" + local_num_qubits.__str__()) lsb = q[0] msb = q[local_num_qubits - 1] # case of no multiplexing: base case for recursion if local_num_qubits == 1: circuit.append(target_gate(list_of_angles[0]), [q[0]]) return circuit # calc angle weights, assuming recursion (that is the lower-level # requested angles have been correctly implemented by recursion angle_weight = np.kron([[0.5, 0.5], [0.5, -0.5]], np.identity(2 ** (local_num_qubits - 2))) # calc the combo angles list_of_angles = angle_weight.dot(np.array(list_of_angles)).tolist() # recursive step on half the angles fulfilling the above assumption multiplex_1 = self._multiplex(target_gate, list_of_angles[0:(list_len // 2)], False) circuit.append(multiplex_1.to_instruction(), q[0:-1]) # attach CNOT as follows, thereby flipping the LSB qubit circuit.append(CXGate(), [msb, lsb]) # implement extra efficiency from the paper of cancelling adjacent # CNOTs (by leaving out last CNOT and reversing (NOT inverting) the # second lower-level multiplex) multiplex_2 = self._multiplex(target_gate, list_of_angles[(list_len // 2):], False) if list_len > 1: circuit.append(multiplex_2.to_instruction().reverse_ops(), q[0:-1]) else: circuit.append(multiplex_2.to_instruction(), q[0:-1]) # attach a final CNOT if last_cnot: circuit.append(CXGate(), [msb, lsb]) return circuit
def test_multiple_conditionals_multiple_registers(self): """Verify disassemble handles multiple conditionals and registers.""" qr = QuantumRegister(3) cr1 = ClassicalRegister(3) cr2 = ClassicalRegister(5) cr3 = ClassicalRegister(6) cr4 = ClassicalRegister(1) qc = QuantumCircuit(qr, cr1, cr2, cr3, cr4) qc.x(qr[1]) qc.h(qr) qc.cx(qr[1], qr[0]).c_if(cr3, 14) qc.ccx(qr[0], qr[2], qr[1]).c_if(cr4, 1) qc.h(qr).c_if(cr1, 3) qobj = assemble(qc) circuits, run_config_out, header = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 3) self.assertEqual(run_config_out.memory_slots, 15) self.assertEqual(len(circuits), 1) self.assertEqual(circuits[0], qc) self.assertEqual({}, header)
def test_setting_data_is_validated(self): """Verify setting circuit.data is broadcast and validated.""" qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.data = [(HGate(), [qr[0]], []), (CnotGate(), [0, 1], []), (HGate(), [(qr, 1)], [])] expected_qc = QuantumCircuit(qr) expected_qc.h(0) expected_qc.cx(0, 1) expected_qc.h(1) self.assertEqual(qc, expected_qc) with self.assertRaises(CircuitError): qc.data = [(HGate(), [qr[0], qr[1]], [])] with self.assertRaises(CircuitError): qc.data = [(HGate(), [], [qr[0]])]