예제 #1
0
 def test_get_instructions_for_qargs(self):
     with self.assertRaises(KeyError):
         self.empty_target.operations_for_qargs((0, ))
     expected = [RZGate(self.theta), IGate(), SXGate(), XGate(), Measure()]
     res = self.ibm_target.operations_for_qargs((0, ))
     for gate in expected:
         self.assertIn(gate, res)
     expected = [ECRGate()]
     res = self.fake_backend_target.operations_for_qargs((1, 0))
     for gate in expected:
         self.assertIn(gate, res)
     expected = [CXGate()]
     res = self.fake_backend_target.operations_for_qargs((0, 1))
     self.assertEqual(expected, res)
     ideal_sim_expected = [
         UGate(self.theta, self.phi, self.lam),
         RXGate(self.theta),
         RYGate(self.theta),
         RZGate(self.theta),
         CXGate(),
         ECRGate(),
         CCXGate(),
         Measure(),
     ]
     for gate in ideal_sim_expected:
         self.assertIn(gate,
                       self.ideal_sim_target.operations_for_qargs(None))
예제 #2
0
    def _generate_xxyy_test_case(self):
        """
        Generates a random pentuple of values (a_source, b_source), beta, (a_target, b_target) s.t.

            CAN(a_source, b_source) * exp(-i(s ZI + t IZ)) * CAN(beta)
                                    =
            exp(-i(u ZI + v IZ)) * CAN(a_target, b_target) * exp(-i(x ZI + y IZ))

        admits a solution in (s, t, u, v, x, y).

        Returns (source_coordinate, interaction, target_coordinate).
        """
        source_coordinate = [self.rng.random(), self.rng.random(), 0.0]
        source_coordinate = [
            source_coordinate[0] * np.pi / 8,
            source_coordinate[1] * source_coordinate[0] * np.pi / 8,
            0.0,
        ]
        interaction = [self.rng.random() * np.pi / 8]
        z_angles = [
            self.rng.random() * np.pi / 8,
            self.rng.random() * np.pi / 8
        ]
        prod = (canonical_matrix(*source_coordinate) @ np.kron(
            RZGate(2 * z_angles[0]).to_matrix(),
            RZGate(2 * z_angles[1]).to_matrix()) @ canonical_matrix(
                interaction[0], 0.0, 0.0))
        target_coordinate = weyl_coordinates(prod)

        self.assertAlmostEqual(target_coordinate[-1], 0.0, delta=EPSILON)

        return source_coordinate, interaction, target_coordinate
예제 #3
0
    def test_commutative_circuit3(self):
        """
        A simple circuit where three CNOTs commute, the first and the last cancel,
        also two X gates cancel and two Rz gates combine.

        qr0:-------.------------------.-------------     qr0:-------------
                   |                  |
        qr1:------(+)------(+)--[X]--(+)-------[X]--  =  qr1:--------(+)--
                            |                                         |
        qr2:------[Rz]--.---.----.---[Rz]-[T]--[S]--     qr2:--[U1]---.---
                        |        |
        qr3:-[Rz]--[X]-(+)------(+)--[X]-[Rz]-------     qr3:--[Rz]-------
        """

        qr = QuantumRegister(4, "qr")
        circuit = QuantumCircuit(qr)

        circuit.cx(qr[0], qr[1])
        circuit.rz(np.pi / 3, qr[2])
        circuit.rz(np.pi / 3, qr[3])
        circuit.x(qr[3])
        circuit.cx(qr[2], qr[3])
        circuit.cx(qr[2], qr[1])
        circuit.cx(qr[2], qr[3])
        circuit.rz(np.pi / 3, qr[2])
        circuit.t(qr[2])
        circuit.x(qr[3])
        circuit.rz(np.pi / 3, qr[3])
        circuit.s(qr[2])
        circuit.x(qr[1])
        circuit.cx(qr[0], qr[1])
        circuit.x(qr[1])

        passmanager = PassManager()
        passmanager.append(
            [
                CommutationAnalysis(),
                CommutativeCancellation(),
                Size(),
                FixedPoint("size")
            ],
            do_while=lambda property_set: not property_set["size_fixed_point"],
        )
        new_circuit = passmanager.run(circuit)
        expected = QuantumCircuit(qr)
        expected.append(RZGate(np.pi * 17 / 12), [qr[2]])
        expected.append(RZGate(np.pi * 2 / 3), [qr[3]])
        expected.cx(qr[2], qr[1])

        self.assertEqual(
            expected,
            new_circuit,
            msg=f"expected:\n{expected}\nnew_circuit:\n{new_circuit}")
    def test_gates_with_parameters(self):
        """Check commutativity between (non-parameterized) gates with parameters."""

        comm_checker = CommutationChecker()
        res = comm_checker.commute(RZGate(0), [0], [], XGate(), [0], [])
        self.assertTrue(res)

        res = comm_checker.commute(RZGate(np.pi / 2), [0], [], XGate(), [0],
                                   [])
        self.assertFalse(res)

        res = comm_checker.commute(RZGate(np.pi / 2), [0], [], RZGate(0), [0],
                                   [])
        self.assertTrue(res)
예제 #5
0
    def test_parameterized_gates_do_not_cancel(self):
        """Test that parameterized gates do not cancel.
        This test should be modified when inverse and commutativity checking
        get improved to handle parameterized gates.
        """
        gate = RZGate(Parameter("Theta"))

        circuit = QuantumCircuit(1)
        circuit.append(gate, [0])
        circuit.append(gate.inverse(), [0])

        passmanager = PassManager(CommutativeInverseCancellation())
        new_circuit = passmanager.run(circuit)
        self.assertEqual(circuit, new_circuit)
예제 #6
0
    def test_is_identity(self):
        """ The is_identity function determines whether a pair of gates
            forms the identity, when ignoring control qubits.
        """
        seq = [DAGNode({'type': 'op', 'op': XGate().control()}),
               DAGNode({'type': 'op', 'op': XGate().control(2)})]
        self.assertTrue(HoareOptimizer()._is_identity(seq))

        seq = [DAGNode({'type': 'op', 'op': RZGate(-pi/2).control()}),
               DAGNode({'type': 'op', 'op': RZGate(pi/2).control(2)})]
        self.assertTrue(HoareOptimizer()._is_identity(seq))

        seq = [DAGNode({'type': 'op', 'op': CSwapGate()}),
               DAGNode({'type': 'op', 'op': SwapGate()})]
        self.assertTrue(HoareOptimizer()._is_identity(seq))
    def test_is_identity(self):
        """The is_identity function determines whether a pair of gates
        forms the identity, when ignoring control qubits.
        """
        seq = [DAGOpNode(op=XGate().control()), DAGOpNode(op=XGate().control(2))]
        self.assertTrue(HoareOptimizer()._is_identity(seq))

        seq = [
            DAGOpNode(op=RZGate(-pi / 2).control()),
            DAGOpNode(op=RZGate(pi / 2).control(2)),
        ]
        self.assertTrue(HoareOptimizer()._is_identity(seq))

        seq = [DAGOpNode(op=CSwapGate()), DAGOpNode(op=SwapGate())]
        self.assertTrue(HoareOptimizer()._is_identity(seq))
예제 #8
0
 def test_grz_equivalence(self):
     """Test global RZ gate is same as 3 individual RZ gates."""
     circuit = GRZ(num_qubits=3, phi=2 * np.pi / 3)
     expected = QuantumCircuit(3, name="grz")
     for i in range(3):
         expected.append(RZGate(phi=2 * np.pi / 3), [i])
     self.assertEqual(expected, circuit)
예제 #9
0
    def exp_i(self) -> OperatorBase:
        """ Return a ``CircuitOp`` equivalent to e^-iH for this operator H. """
        # if only one qubit is significant, we can perform the evolution
        corrected_x = self.primitive.x[::-1]  # type: ignore
        corrected_z = self.primitive.z[::-1]  # type: ignore
        # pylint: disable=import-outside-toplevel,no-member
        sig_qubits = np.logical_or(corrected_x, corrected_z)
        if np.sum(sig_qubits) == 0:
            # e^I is just a global phase, but we can keep track of it! Should we?
            # For now, just return identity
            return PauliOp(self.primitive)
        if np.sum(sig_qubits) == 1:
            sig_qubit_index = sig_qubits.tolist().index(True)
            coeff = np.real(self.coeff) \
                if not isinstance(self.coeff, ParameterExpression) \
                else self.coeff
            # Y rotation
            if corrected_x[sig_qubit_index] and corrected_z[sig_qubit_index]:
                rot_op = PrimitiveOp(RYGate(coeff))
            # Z rotation
            elif corrected_z[sig_qubit_index]:
                rot_op = PrimitiveOp(RZGate(coeff))
            # X rotation
            elif corrected_x[sig_qubit_index]:
                rot_op = PrimitiveOp(RXGate(coeff))

            from ..operator_globals import I
            left_pad = I.tensorpower(sig_qubit_index)
            right_pad = I.tensorpower(self.num_qubits - sig_qubit_index - 1)
            # Need to use overloaded operators here in case left_pad == I^0
            return left_pad ^ rot_op ^ right_pad
        else:
            from ..evolutions.evolved_op import EvolvedOp
            return EvolvedOp(self)
예제 #10
0
    def test_commutative_circuit2(self):
        """
        A simple circuit where three CNOTs commute, the first and the last cancel,
        also two X gates cancel and two Rz gates combine.

        qr0:----.---------------.--------     qr0:-------------
                |               |
        qr1:---(+)---(+)--[X]--(+)--[X]--  =  qr1:--------(+)--
                      |                                    |
        qr2:---[Rz]---.---[Rz]-[T]--[S]--     qr2:--[U1]---.---
        """

        qr = QuantumRegister(3, "qr")
        circuit = QuantumCircuit(qr)
        circuit.cx(qr[0], qr[1])
        circuit.rz(np.pi / 3, qr[2])
        circuit.cx(qr[2], qr[1])
        circuit.rz(np.pi / 3, qr[2])
        circuit.t(qr[2])
        circuit.s(qr[2])
        circuit.x(qr[1])
        circuit.cx(qr[0], qr[1])
        circuit.x(qr[1])

        passmanager = PassManager()
        passmanager.append(CommutativeCancellation())
        new_circuit = passmanager.run(circuit)
        expected = QuantumCircuit(qr)
        expected.append(RZGate(np.pi * 17 / 12), [qr[2]])
        expected.cx(qr[2], qr[1])
        expected.global_phase = (np.pi * 17 / 12 - (2 * np.pi / 3)) / 2
        self.assertEqual(expected, new_circuit)
예제 #11
0
 def test_operations(self):
     self.assertEqual(self.empty_target.operations, [])
     ibm_expected = [
         RZGate(self.theta),
         IGate(),
         SXGate(),
         XGate(),
         CXGate(),
         Measure()
     ]
     for gate in ibm_expected:
         self.assertIn(gate, self.ibm_target.operations)
     aqt_expected = [
         RZGate(self.theta),
         RXGate(self.theta),
         RYGate(self.theta),
         RGate(self.theta, self.phi),
         RXXGate(self.theta),
     ]
     for gate in aqt_expected:
         self.assertIn(gate, self.aqt_target.operations)
     fake_expected = [
         UGate(self.fake_backend._theta, self.fake_backend._phi,
               self.fake_backend._lam),
         CXGate(),
         Measure(),
         ECRGate(),
         RXGate(math.pi / 6),
         RXGate(self.fake_backend._theta),
     ]
     for gate in fake_expected:
         self.assertIn(gate, self.fake_backend_target.operations)
     ideal_sim_expected = [
         UGate(self.theta, self.phi, self.lam),
         RXGate(self.theta),
         RYGate(self.theta),
         RZGate(self.theta),
         CXGate(),
         ECRGate(),
         CCXGate(),
         Measure(),
     ]
     for gate in ideal_sim_expected:
         self.assertIn(gate, self.ideal_sim_target.operations)
예제 #12
0
 def gate_to_qiskit(gate2):
     if gate2.name == 'X':
         return XGate()
     elif gate2.name == 'Ry':
         return RYGate(-gate2.arg)
     elif gate2.name == 'Rz':
         return RZGate(-gate2.arg)
     elif gate2.name == 'R1':
         return U1Gate(gate2.arg)
     else:
         raise RuntimeError("Can't implement: %s" % gate2)
예제 #13
0
def rz_matrix(phi: float) -> np.ndarray:
    """
    Computes an RZ rotation by the angle of ``phi``.

    Args:
        phi: rotation angle.

    Returns:
        an RZ rotation matrix.
    """
    return RZGate(phi).to_matrix()
예제 #14
0
    def test_decompose_xxyy(self):
        """
        Test that decompose_xxyy_into_xxyy_xx correctly recovers decompositions.
        """

        for _ in range(100):
            source_coordinate, interaction, target_coordinate = self._generate_xxyy_test_case(
            )

            r, s, u, v, x, y = decompose_xxyy_into_xxyy_xx(
                target_coordinate[0],
                target_coordinate[1],
                source_coordinate[0],
                source_coordinate[1],
                interaction[0],
            )

            prod = (
                np.kron(RZGate(2 * r).to_matrix(),
                        RZGate(2 * s).to_matrix())
                @ canonical_matrix(*source_coordinate) @ np.kron(
                    RZGate(2 * u).to_matrix(),
                    RZGate(2 * v).to_matrix()) @ canonical_matrix(
                        interaction[0], 0.0, 0.0) @ np.kron(
                            RZGate(2 * x).to_matrix(),
                            RZGate(2 * y).to_matrix()))
            expected = canonical_matrix(*target_coordinate)
            self.assertTrue(np.all(np.abs(prod - expected) < EPSILON))
    def test_parameterized_gates(self):
        """Check commutativity between parameterized gates, both with free and with
        bound parameters."""

        comm_checker = CommutationChecker()

        # gate that has parameters but is not considered parameterized
        rz_gate = RZGate(np.pi / 2)
        self.assertEqual(len(rz_gate.params), 1)
        self.assertFalse(rz_gate.is_parameterized())

        # gate that has parameters and is considered parameterized
        rz_gate_theta = RZGate(Parameter("Theta"))
        rz_gate_phi = RZGate(Parameter("Phi"))
        self.assertEqual(len(rz_gate_theta.params), 1)
        self.assertTrue(rz_gate_theta.is_parameterized())

        # gate that has no parameters and is not considered parameterized
        cx_gate = CXGate()
        self.assertEqual(len(cx_gate.params), 0)
        self.assertFalse(cx_gate.is_parameterized())

        # We should detect that these gates commute
        res = comm_checker.commute(rz_gate, [0], [], cx_gate, [0, 1], [])
        self.assertTrue(res)

        # We should detect that these gates commute
        res = comm_checker.commute(rz_gate, [0], [], rz_gate, [0], [])
        self.assertTrue(res)

        # We should detect that parameterized gates over disjoint qubit subsets commute
        res = comm_checker.commute(rz_gate_theta, [0], [], rz_gate_theta, [1],
                                   [])
        self.assertTrue(res)

        # We should detect that parameterized gates over disjoint qubit subsets commute
        res = comm_checker.commute(rz_gate_theta, [0], [], rz_gate_phi, [1],
                                   [])
        self.assertTrue(res)

        # We should detect that parameterized gates over disjoint qubit subsets commute
        res = comm_checker.commute(rz_gate_theta, [2], [], cx_gate, [1, 3], [])
        self.assertTrue(res)

        # However, for now commutativity checker should return False when checking
        # commutativity between a parameterized gate and some other gate, when
        # the two gates are over intersecting qubit subsets.
        # This check should be changed if commutativity checker is extended to
        # handle parameterized gates better.
        res = comm_checker.commute(rz_gate_theta, [0], [], cx_gate, [0, 1], [])
        self.assertFalse(res)

        res = comm_checker.commute(rz_gate_theta, [0], [], rz_gate, [0], [])
        self.assertFalse(res)
class TestParameterCtrlState(QiskitTestCase):
    """Test gate equality with ctrl_state parameter."""
    @data((RXGate(0.5), CRXGate(0.5)), (RYGate(0.5), CRYGate(0.5)),
          (RZGate(0.5), CRZGate(0.5)), (XGate(), CXGate()),
          (YGate(), CYGate()), (ZGate(), CZGate()),
          (U1Gate(0.5), CU1Gate(0.5)), (SwapGate(), CSwapGate()),
          (HGate(), CHGate()), (U3Gate(0.1, 0.2, 0.3), CU3Gate(0.1, 0.2, 0.3)))
    @unpack
    def test_ctrl_state_one(self, gate, controlled_gate):
        """Test controlled gates with ctrl_state
        See https://github.com/Qiskit/qiskit-terra/pull/4025
        """
        self.assertEqual(gate.control(1, ctrl_state='1'), controlled_gate)
예제 #17
0
    def test_parameterized_circuit_for_simulator(self):
        """Verify that a parameterized circuit can be transpiled for a simulator backend."""
        qr = QuantumRegister(2, name='qr')
        qc = QuantumCircuit(qr)

        theta = Parameter('theta')
        qc.rz(theta, qr[0])

        transpiled_qc = transpile(qc, backend=BasicAer.get_backend('qasm_simulator'))

        expected_qc = QuantumCircuit(qr)
        expected_qc.append(RZGate(theta), [qr[0]])
        self.assertEqual(expected_qc, transpiled_qc)
예제 #18
0
    def test_parameter_expression_circuit_for_simulator(self):
        """Verify that a circuit including expressions of parameters can be
        transpiled for a simulator backend."""
        qr = QuantumRegister(2, name='qr')
        qc = QuantumCircuit(qr)

        theta = Parameter('theta')
        square = theta * theta
        qc.rz(square, qr[0])

        transpiled_qc = transpile(qc, backend=BasicAer.get_backend('qasm_simulator'))

        expected_qc = QuantumCircuit(qr)
        expected_qc.append(RZGate(square), [qr[0]])
        self.assertEqual(expected_qc, transpiled_qc)
예제 #19
0
    def test_compose_one_liner(self):
        """Test building a circuit in one line, for fun.
        """
        circ = QuantumCircuit(3)
        h = HGate()
        rz = RZGate(0.1)
        cx = CXGate()
        ccx = CCXGate()
        circ = circ.compose(h, [0]).compose(cx, [0, 2]).compose(ccx, [2, 1, 0]).compose(rz, [1])

        expected = QuantumCircuit(3)
        expected.h(0)
        expected.cx(0, 2)
        expected.ccx(2, 1, 0)
        expected.rz(0.1, 1)

        self.assertEqual(circ, expected)
예제 #20
0
 def test_instruction_schedule_map_ideal_sim_backend(self):
     ideal_sim_target = Target(num_qubits=3)
     theta = Parameter("theta")
     phi = Parameter("phi")
     lam = Parameter("lambda")
     for inst in [
             UGate(theta, phi, lam),
             RXGate(theta),
             RYGate(theta),
             RZGate(theta),
             CXGate(),
             ECRGate(),
             CCXGate(),
             Measure(),
     ]:
         ideal_sim_target.add_instruction(inst, {None: None})
     inst_map = ideal_sim_target.instruction_schedule_map()
     self.assertEqual(InstructionScheduleMap(), inst_map)
    def _define(self):
        # The y angle matrix stands for the absolute value, while the z angles stand for phases
        # The difficulty lies in the "global" phases that must be later accounted for in each subspace
        y_angle_matrix = self._to_angle_matrix_y()
        z_angle_matrix, global_phases = self._to_angle_matrix_z()

        # As the subspace phase correction is a very expensive module, we only want to do it if the
        # z rotation matrix is non-zero!
        no_z_rotations = abs(sparse.linalg.norm(z_angle_matrix)) < 1e-6

        control = QuantumRegister(self.num_controls_qb, "q^c")
        target = QuantumRegister(self.num_targets_qb, "q^t")
        qc_y = QuantumCircuit(control, target, name=self.name)
        qc_z = QuantumCircuit(control, target, name=self.name)

        for l in range(1, self.num_targets_qb + 1):
            qargs = list(control) + target[0:l]

            # If there are no z-rotations we save a lot of gates, so we want to rule that out
            if not no_z_rotations:
                angles_z: sparse.spmatrix = z_angle_matrix[range(ControlledStatePreparationGate._chi(l) - 1, ControlledStatePreparationGate._chi(l + 1) - 1), :]
                angles_z = angles_z.reshape(-1, 1)
                gate_z = UniformRotationGate(gate=lambda a: RZGate(a), alpha=angles_z.todok())
                qc_z.append(gate_z, qargs)
                qc_z.barrier()

            # The uniform rotation for the y rotation will take care of the absolute value
            angles_y: sparse.spmatrix = y_angle_matrix[range(ControlledStatePreparationGate._chi(l) - 1, ControlledStatePreparationGate._chi(l + 1) - 1), :]
            angles_y = angles_y.reshape(-1, 1)
            gate_y = UniformRotationGate(gate=lambda a: RYGate(a), alpha=angles_y.todok())
            qc_y.append(gate_y, qargs)
            qc_y.barrier()

        if not no_z_rotations:
            # A relative phase correction is pretty intensive: a state preparation on the control
            global_phase_correction = MöttönenStatePreparationGate(
                sparse.dok_matrix(np.exp(1.0j * global_phases.T.toarray())),
                neglect_absolute_value=True
            )
            qc_z.append(global_phase_correction, qargs=control)
            qc_z.barrier()

        qc = qc_y + qc_z
        self._definition = qc
예제 #22
0
    def test_all_gates(self):
        """Test all gates on 1 and 2 qubits

        q0:-[H]-[H]--[x]-[x]--[y]-[y]--[rz]-[rz]--[u1]-[u1]-[rx]-[rx]---.--.--.--.--.--.-
                                                                        |  |  |  |  |  |
        q1:-------------------------------------------------------------X--X--Y--Y--.--.-

        =

        qr0:---[u1]---

        qr1:----------
        """
        qr = QuantumRegister(2, "q")
        circuit = QuantumCircuit(qr)
        circuit.h(qr[0])
        circuit.h(qr[0])
        circuit.x(qr[0])
        circuit.x(qr[0])
        circuit.y(qr[0])
        circuit.y(qr[0])
        circuit.rz(0.5, qr[0])
        circuit.rz(0.5, qr[0])
        circuit.append(U1Gate(0.5),
                       [qr[0]])  # TODO this should work with Phase gates too
        circuit.append(U1Gate(0.5), [qr[0]])
        circuit.rx(0.5, qr[0])
        circuit.rx(0.5, qr[0])
        circuit.cx(qr[0], qr[1])
        circuit.cx(qr[0], qr[1])
        circuit.cy(qr[0], qr[1])
        circuit.cy(qr[0], qr[1])
        circuit.cz(qr[0], qr[1])
        circuit.cz(qr[0], qr[1])

        passmanager = PassManager()
        passmanager.append(CommutativeCancellation())
        new_circuit = passmanager.run(circuit)

        expected = QuantumCircuit(qr)
        expected.append(RZGate(2.0), [qr[0]])
        expected.rx(1.0, qr[0])

        self.assertEqual(expected, new_circuit)
예제 #23
0
 def test_instructions(self):
     self.assertEqual(self.empty_target.instructions, [])
     ibm_expected = [
         (IGate(), (0, )),
         (IGate(), (1, )),
         (IGate(), (2, )),
         (IGate(), (3, )),
         (IGate(), (4, )),
         (RZGate(self.theta), (0, )),
         (RZGate(self.theta), (1, )),
         (RZGate(self.theta), (2, )),
         (RZGate(self.theta), (3, )),
         (RZGate(self.theta), (4, )),
         (SXGate(), (0, )),
         (SXGate(), (1, )),
         (SXGate(), (2, )),
         (SXGate(), (3, )),
         (SXGate(), (4, )),
         (XGate(), (0, )),
         (XGate(), (1, )),
         (XGate(), (2, )),
         (XGate(), (3, )),
         (XGate(), (4, )),
         (CXGate(), (3, 4)),
         (CXGate(), (4, 3)),
         (CXGate(), (3, 1)),
         (CXGate(), (1, 3)),
         (CXGate(), (1, 2)),
         (CXGate(), (2, 1)),
         (CXGate(), (0, 1)),
         (CXGate(), (1, 0)),
         (Measure(), (0, )),
         (Measure(), (1, )),
         (Measure(), (2, )),
         (Measure(), (3, )),
         (Measure(), (4, )),
     ]
     self.assertEqual(ibm_expected, self.ibm_target.instructions)
     ideal_sim_expected = [
         (UGate(self.theta, self.phi, self.lam), None),
         (RXGate(self.theta), None),
         (RYGate(self.theta), None),
         (RZGate(self.theta), None),
         (CXGate(), None),
         (ECRGate(), None),
         (CCXGate(), None),
         (Measure(), None),
     ]
     self.assertEqual(ideal_sim_expected,
                      self.ideal_sim_target.instructions)
예제 #24
0
    def setUp(self):
        super().setUp()
        self.target = Target()

        # U gate in qubit 0.
        self.theta = Parameter("theta")
        self.phi = Parameter("phi")
        self.lam = Parameter("lambda")
        u_props = {
            (0, ): InstructionProperties(duration=5.23e-8, error=0.00038115),
        }
        self.target.add_instruction(UGate(self.theta, self.phi, self.lam),
                                    u_props)

        # Rz gate in qubit 1.
        rz_props = {
            (1, ): InstructionProperties(duration=0.0, error=0),
        }
        self.target.add_instruction(RZGate(self.phi), rz_props)

        # X gate in qubit 1.
        x_props = {
            (1, ):
            InstructionProperties(duration=3.5555555555555554e-08,
                                  error=0.00020056469709026198),
        }
        self.target.add_instruction(XGate(), x_props)

        # SX gate in qubit 1.
        sx_props = {
            (1, ):
            InstructionProperties(duration=3.5555555555555554e-08,
                                  error=0.00020056469709026198),
        }
        self.target.add_instruction(SXGate(), sx_props)

        cx_props = {
            (0, 1): InstructionProperties(duration=5.23e-7, error=0.00098115),
            (1, 0): InstructionProperties(duration=4.52e-7, error=0.00132115),
        }
        self.target.add_instruction(CXGate(), cx_props)
예제 #25
0
 def test_multiple_gate_error_matrix(self):
     """Test error matrix ona small target with multiple gets on each qubit generates"""
     target = Target(num_qubits=3)
     phi = Parameter("phi")
     lam = Parameter("lam")
     theta = Parameter("theta")
     target.add_instruction(
         RZGate(phi), {(i, ): InstructionProperties(duration=0, error=0)
                       for i in range(3)})
     target.add_instruction(
         UGate(theta, phi, lam),
         {(i, ): InstructionProperties(duration=1e-7, error=1e-2)
          for i in range(3)},
     )
     cx_props = {
         (0, 1): InstructionProperties(error=1e-3),
         (0, 2): InstructionProperties(error=1e-3),
         (1, 0): InstructionProperties(error=1e-3),
         (1, 2): InstructionProperties(error=1e-3),
         (2, 0): InstructionProperties(error=1e-3),
         (2, 1): InstructionProperties(error=1e-3),
     }
     target.add_instruction(CXGate(), cx_props)
     ecr_props = {
         (0, 1): InstructionProperties(error=2e-2),
         (1, 2): InstructionProperties(error=2e-2),
         (2, 0): InstructionProperties(error=2e-2),
     }
     target.add_instruction(ECRGate(), ecr_props)
     expected_error_matrix = np.array([
         [1e-2, 2e-2, 1e-3],
         [1e-3, 1e-2, 2e-2],
         [2e-2, 1e-3, 1e-2],
     ])
     np.testing.assert_array_equal(expected_error_matrix,
                                   DenseLayout(target=target).error_mat)
예제 #26
0
class TestCollect2qBlocks(QiskitTestCase):
    """
    Tests to verify that blocks of 2q interactions are found correctly.
    """
    def test_blocks_in_topological_order(self):
        """the pass returns blocks in correct topological order
                                                    ______
        q0:--[p]-------.----      q0:-------------|      |--
                       |                 ______   |  U2  |
        q1:--[u]--(+)-(+)---   =  q1:---|      |--|______|--
                   |                    |  U1  |
        q2:--------.--------      q2:---|______|------------
        """
        qr = QuantumRegister(3, "qr")
        qc = QuantumCircuit(qr)
        qc.p(0.5, qr[0])
        qc.u(0.0, 0.2, 0.6, qr[1])
        qc.cx(qr[2], qr[1])
        qc.cx(qr[0], qr[1])
        dag = circuit_to_dag(qc)

        topo_ops = list(dag.topological_op_nodes())
        block_1 = [topo_ops[1], topo_ops[2]]
        block_2 = [topo_ops[0], topo_ops[3]]

        pass_ = Collect2qBlocks()
        pass_.run(dag)
        self.assertTrue(pass_.property_set["block_list"], [block_1, block_2])

    def test_block_interrupted_by_gate(self):
        """Test that blocks interrupted by a gate that can't be added
        to the block can be collected correctly

        This was raised in #2775 where a measure in the middle of a block
        stopped the block collection from working properly. This was because
        the pass didn't expect to have measures in the middle of the circuit.

        blocks : [['cx', 'id', 'id', 'id'], ['id', 'cx']]

                ┌───┐┌───┐┌─┐     ┌───┐┌───┐
        q_0: |0>┤ X ├┤ I ├┤M├─────┤ I ├┤ X ├
                └─┬─┘├───┤└╥┘┌───┐└───┘└─┬─┘
        q_1: |0>──■──┤ I ├─╫─┤ I ├───────■──
                     └───┘ ║ └───┘
         c_0: 0 ═══════════╩════════════════

        """
        qc = QuantumCircuit(2, 1)
        qc.cx(1, 0)
        qc.i(0)
        qc.i(1)
        qc.measure(0, 0)
        qc.i(0)
        qc.i(1)
        qc.cx(1, 0)

        dag = circuit_to_dag(qc)
        pass_ = Collect2qBlocks()
        pass_.run(dag)

        # list from Collect2QBlocks of nodes that it should have put into blocks
        good_names = ["cx", "u1", "u2", "u3", "id"]
        dag_nodes = [
            node for node in dag.topological_op_nodes()
            if node.name in good_names
        ]

        # we have to convert them to sets as the ordering can be different
        # but equivalent between python 3.5 and 3.7
        # there is no implied topology in a block, so this isn't an issue
        dag_nodes = [set(dag_nodes[:4]), set(dag_nodes[4:])]
        pass_nodes = [set(bl) for bl in pass_.property_set["block_list"]]

        self.assertEqual(dag_nodes, pass_nodes)

    def test_block_with_classical_register(self):
        """Test that only blocks that share quantum wires are added to the block.
        It was the case that gates which shared a classical wire could be added to
        the same block, despite not sharing the same qubits. This was fixed in #2956.

                                    ┌─────────────────────┐
        q_0: |0>────────────────────┤ U2(0.25*pi,0.25*pi) ├
                     ┌─────────────┐└──────────┬──────────┘
        q_1: |0>──■──┤ U1(0.25*pi) ├───────────┼───────────
                ┌─┴─┐└──────┬──────┘           │
        q_2: |0>┤ X ├───────┼──────────────────┼───────────
                └───┘    ┌──┴──┐            ┌──┴──┐
        c0_0: 0 ═════════╡ = 0 ╞════════════╡ = 0 ╞════════
                         └─────┘            └─────┘

        Previously the blocks collected were : [['cx', 'u1', 'u2']]
        This is now corrected to : [['cx', 'u1']]
        """

        qasmstr = """
        OPENQASM 2.0;
        include "qelib1.inc";
        qreg q[3];
        creg c0[1];

        cx q[1],q[2];
        if(c0==0) u1(0.25*pi) q[1];
        if(c0==0) u2(0.25*pi, 0.25*pi) q[0];
        """
        qc = QuantumCircuit.from_qasm_str(qasmstr)

        pass_manager = PassManager()
        pass_manager.append(Collect2qBlocks())

        pass_manager.run(qc)

        self.assertEqual(
            [["cx"]], [[n.name for n in block]
                       for block in pass_manager.property_set["block_list"]])

    def test_do_not_merge_conditioned_gates(self):
        """Validate that classically conditioned gates are never considered for
        inclusion in a block. Note that there are cases where gates conditioned
        on the same (register, value) pair could be correctly merged, but this is
        not yet implemented.

                 ┌────────┐┌────────┐┌────────┐      ┌───┐
        qr_0: |0>┤ P(0.1) ├┤ P(0.2) ├┤ P(0.3) ├──■───┤ X ├────■───
                 └────────┘└───┬────┘└───┬────┘┌─┴─┐ └─┬─┘  ┌─┴─┐
        qr_1: |0>──────────────┼─────────┼─────┤ X ├───■────┤ X ├─
                               │         │     └───┘   │    └─┬─┘
        qr_2: |0>──────────────┼─────────┼─────────────┼──────┼───
                            ┌──┴──┐   ┌──┴──┐       ┌──┴──┐┌──┴──┐
         cr_0: 0 ═══════════╡     ╞═══╡     ╞═══════╡     ╞╡     ╞
                            │ = 0 │   │ = 0 │       │ = 0 ││ = 1 │
         cr_1: 0 ═══════════╡     ╞═══╡     ╞═══════╡     ╞╡     ╞
                            └─────┘   └─────┘       └─────┘└─────┘

        Previously the blocks collected were : [['p', 'p', 'p', 'cx', 'cx', 'cx']]
        This is now corrected to : [['cx']]
        """
        # ref: https://github.com/Qiskit/qiskit-terra/issues/3215

        qr = QuantumRegister(3, "qr")
        cr = ClassicalRegister(2, "cr")

        qc = QuantumCircuit(qr, cr)
        qc.p(0.1, 0)
        qc.p(0.2, 0).c_if(cr, 0)
        qc.p(0.3, 0).c_if(cr, 0)
        qc.cx(0, 1)
        qc.cx(1, 0).c_if(cr, 0)
        qc.cx(0, 1).c_if(cr, 1)

        pass_manager = PassManager()
        pass_manager.append(Collect2qBlocks())

        pass_manager.run(qc)
        self.assertEqual(
            [["cx"]], [[n.name for n in block]
                       for block in pass_manager.property_set["block_list"]])

    @unpack
    @data(
        (CXGate(), U1Gate(0.1), U2Gate(0.2, 0.3)),
        (RXXGate(pi / 2), RZGate(0.1), RXGate(pi / 2)),
        (
            Gate("custom2qgate", 2, []),
            Gate("custom1qgate1", 1, []),
            Gate("custom1qgate2", 1, []),
        ),
    )
    def test_collect_arbitrary_gates(self, twoQ_gate, oneQ_gate1, oneQ_gate2):
        """Validate we can collect blocks irrespective of gate types in the circuit."""

        qc = QuantumCircuit(3)

        # Block 1 - q[0] and q[1]
        qc.append(oneQ_gate1, [0])
        qc.append(oneQ_gate2, [1])
        qc.append(twoQ_gate, [0, 1])
        qc.append(oneQ_gate1, [0])
        qc.append(oneQ_gate2, [1])

        # Block 2 - q[1] and q[2]
        qc.append(oneQ_gate1, [1])
        qc.append(oneQ_gate2, [2])
        qc.append(twoQ_gate, [1, 2])
        qc.append(oneQ_gate1, [1])
        qc.append(oneQ_gate2, [2])

        # Block 3 - q[0] and q[1]
        qc.append(oneQ_gate1, [0])
        qc.append(oneQ_gate2, [1])
        qc.append(twoQ_gate, [0, 1])
        qc.append(oneQ_gate1, [0])
        qc.append(oneQ_gate2, [1])

        pass_manager = PassManager()
        pass_manager.append(Collect2qBlocks())

        pass_manager.run(qc)
        self.assertEqual(len(pass_manager.property_set["block_list"]), 3)
예제 #27
0
    def setUp(self):
        super().setUp()
        self.fake_backend = FakeBackendV2()
        self.fake_backend_target = self.fake_backend.target
        self.theta = Parameter("theta")
        self.phi = Parameter("phi")
        self.ibm_target = Target()
        i_props = {
            (0, ): InstructionProperties(duration=35.5e-9, error=0.000413),
            (1, ): InstructionProperties(duration=35.5e-9, error=0.000502),
            (2, ): InstructionProperties(duration=35.5e-9, error=0.0004003),
            (3, ): InstructionProperties(duration=35.5e-9, error=0.000614),
            (4, ): InstructionProperties(duration=35.5e-9, error=0.006149),
        }
        self.ibm_target.add_instruction(IGate(), i_props)
        rz_props = {
            (0, ): InstructionProperties(duration=0, error=0),
            (1, ): InstructionProperties(duration=0, error=0),
            (2, ): InstructionProperties(duration=0, error=0),
            (3, ): InstructionProperties(duration=0, error=0),
            (4, ): InstructionProperties(duration=0, error=0),
        }
        self.ibm_target.add_instruction(RZGate(self.theta), rz_props)
        sx_props = {
            (0, ): InstructionProperties(duration=35.5e-9, error=0.000413),
            (1, ): InstructionProperties(duration=35.5e-9, error=0.000502),
            (2, ): InstructionProperties(duration=35.5e-9, error=0.0004003),
            (3, ): InstructionProperties(duration=35.5e-9, error=0.000614),
            (4, ): InstructionProperties(duration=35.5e-9, error=0.006149),
        }
        self.ibm_target.add_instruction(SXGate(), sx_props)
        x_props = {
            (0, ): InstructionProperties(duration=35.5e-9, error=0.000413),
            (1, ): InstructionProperties(duration=35.5e-9, error=0.000502),
            (2, ): InstructionProperties(duration=35.5e-9, error=0.0004003),
            (3, ): InstructionProperties(duration=35.5e-9, error=0.000614),
            (4, ): InstructionProperties(duration=35.5e-9, error=0.006149),
        }
        self.ibm_target.add_instruction(XGate(), x_props)
        cx_props = {
            (3, 4): InstructionProperties(duration=270.22e-9, error=0.00713),
            (4, 3): InstructionProperties(duration=305.77e-9, error=0.00713),
            (3, 1): InstructionProperties(duration=462.22e-9, error=0.00929),
            (1, 3): InstructionProperties(duration=497.77e-9, error=0.00929),
            (1, 2): InstructionProperties(duration=227.55e-9, error=0.00659),
            (2, 1): InstructionProperties(duration=263.11e-9, error=0.00659),
            (0, 1): InstructionProperties(duration=519.11e-9, error=0.01201),
            (1, 0): InstructionProperties(duration=554.66e-9, error=0.01201),
        }
        self.ibm_target.add_instruction(CXGate(), cx_props)
        measure_props = {
            (0, ): InstructionProperties(duration=5.813e-6, error=0.0751),
            (1, ): InstructionProperties(duration=5.813e-6, error=0.0225),
            (2, ): InstructionProperties(duration=5.813e-6, error=0.0146),
            (3, ): InstructionProperties(duration=5.813e-6, error=0.0215),
            (4, ): InstructionProperties(duration=5.813e-6, error=0.0333),
        }
        self.ibm_target.add_instruction(Measure(), measure_props)

        self.aqt_target = Target(description="AQT Target")
        rx_props = {
            (0, ): None,
            (1, ): None,
            (2, ): None,
            (3, ): None,
            (4, ): None,
        }
        self.aqt_target.add_instruction(RXGate(self.theta), rx_props)
        ry_props = {
            (0, ): None,
            (1, ): None,
            (2, ): None,
            (3, ): None,
            (4, ): None,
        }
        self.aqt_target.add_instruction(RYGate(self.theta), ry_props)
        rz_props = {
            (0, ): None,
            (1, ): None,
            (2, ): None,
            (3, ): None,
            (4, ): None,
        }
        self.aqt_target.add_instruction(RZGate(self.theta), rz_props)
        r_props = {
            (0, ): None,
            (1, ): None,
            (2, ): None,
            (3, ): None,
            (4, ): None,
        }
        self.aqt_target.add_instruction(RGate(self.theta, self.phi), r_props)
        rxx_props = {
            (0, 1): None,
            (0, 2): None,
            (0, 3): None,
            (0, 4): None,
            (1, 0): None,
            (2, 0): None,
            (3, 0): None,
            (4, 0): None,
            (1, 2): None,
            (1, 3): None,
            (1, 4): None,
            (2, 1): None,
            (3, 1): None,
            (4, 1): None,
            (2, 3): None,
            (2, 4): None,
            (3, 2): None,
            (4, 2): None,
            (3, 4): None,
            (4, 3): None,
        }
        self.aqt_target.add_instruction(RXXGate(self.theta), rxx_props)
        measure_props = {
            (0, ): None,
            (1, ): None,
            (2, ): None,
            (3, ): None,
            (4, ): None,
        }
        self.aqt_target.add_instruction(Measure(), measure_props)
        self.empty_target = Target()
        self.ideal_sim_target = Target(num_qubits=3,
                                       description="Ideal Simulator")
        self.lam = Parameter("lam")
        for inst in [
                UGate(self.theta, self.phi, self.lam),
                RXGate(self.theta),
                RYGate(self.theta),
                RZGate(self.theta),
                CXGate(),
                ECRGate(),
                CCXGate(),
                Measure(),
        ]:
            self.ideal_sim_target.add_instruction(inst, {None: None})
    def convert(
        self,
        operator: CircuitStateFn,
        params: Optional[Union[ParameterExpression, ParameterVector,
                               List[ParameterExpression]]] = None,
    ) -> 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:
            AquaError: If one of the circuits could not be constructed.
            TypeError: If ``operator`` is an unsupported type.
        """

        # QFI & phase fix observable
        qfi_observable = ~StateFn(4 * Z ^ (I ^ operator.num_qubits))
        phase_fix_observable = ~StateFn((X + 1j * Y)
                                        ^ (I ^ operator.num_qubits))
        # see https://arxiv.org/pdf/quant-ph/0108146.pdf

        # 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 CircuitStateFn'
            )

        # If a single parameter is given wrap it into a list.
        if not isinstance(params, (list, np.ndarray)):
            params = [params]
        state_qc = operator.primitive

        # First, the operators are computed which can compensate for a potential phase-mismatch
        # between target and trained state, i.e.〈ψ|∂lψ〉
        phase_fix_states = None
        # Add working qubit
        qr_work = QuantumRegister(1, 'work_qubit')
        work_q = qr_work[0]
        additional_qubits: Tuple[List[Qubit], List[Qubit]] = ([work_q], [])
        for param in params:
            # Get the gates of the given quantum state which are parameterized by param
            param_gates = state_qc._parameter_table[param]
            # Loop through the occurrences of param in the quantum state
            for m, param_occurence in enumerate(param_gates):
                # Get the coefficients and gates for the linear combination gradient for each
                # occurrence, see e.g. https://arxiv.org/abs/2006.06004
                coeffs_i, gates_i = LinComb._gate_gradient_dict(
                    param_occurence[0])[param_occurence[1]]
                # Construct the quantum states which are then evaluated for the respective QFI
                # element.
                for k, gate_to_insert_i in enumerate(gates_i):
                    grad_state = state_qc.copy()
                    grad_state.add_register(qr_work)

                    # apply Hadamard on work_q
                    LinComb.insert_gate(grad_state,
                                        param_occurence[0],
                                        HGate(),
                                        qubits=[work_q])
                    # Fix work_q phase such that the gradient is correct.
                    coeff_i = coeffs_i[k]
                    sign = np.sign(coeff_i)
                    is_complex = np.iscomplex(coeff_i)
                    if sign == -1:
                        if is_complex:
                            LinComb.insert_gate(grad_state,
                                                param_occurence[0],
                                                SdgGate(),
                                                qubits=[work_q])
                        else:
                            LinComb.insert_gate(grad_state,
                                                param_occurence[0],
                                                ZGate(),
                                                qubits=[work_q])
                    else:
                        if is_complex:
                            LinComb.insert_gate(grad_state,
                                                param_occurence[0],
                                                SGate(),
                                                qubits=[work_q])

                    # Insert controlled, intercepting gate - controlled by |0>

                    if isinstance(param_occurence[0], UGate):
                        if param_occurence[1] == 0:
                            LinComb.insert_gate(
                                grad_state, param_occurence[0],
                                RZGate(param_occurence[0].params[2]))
                            LinComb.insert_gate(grad_state, param_occurence[0],
                                                RXGate(np.pi / 2))
                            LinComb.insert_gate(
                                grad_state,
                                param_occurence[0],
                                gate_to_insert_i,
                                additional_qubits=additional_qubits)
                            LinComb.insert_gate(grad_state, param_occurence[0],
                                                RXGate(-np.pi / 2))
                            LinComb.insert_gate(
                                grad_state, param_occurence[0],
                                RZGate(-param_occurence[0].params[2]))

                        elif param_occurence[1] == 1:
                            LinComb.insert_gate(
                                grad_state,
                                param_occurence[0],
                                gate_to_insert_i,
                                after=True,
                                additional_qubits=additional_qubits)
                        else:
                            LinComb.insert_gate(
                                grad_state,
                                param_occurence[0],
                                gate_to_insert_i,
                                additional_qubits=additional_qubits)
                    else:
                        LinComb.insert_gate(
                            grad_state,
                            param_occurence[0],
                            gate_to_insert_i,
                            additional_qubits=additional_qubits)

                    # Remove unnecessary gates.
                    grad_state = self.trim_circuit(grad_state,
                                                   param_occurence[0])
                    # Apply the final Hadamard on the working qubit.
                    grad_state.h(work_q)
                    # Add the coefficient needed for the gradient as well as the original
                    # coefficient of the given quantum state.
                    state = np.sqrt(np.abs(
                        coeff_i)) * operator.coeff * CircuitStateFn(grad_state)

                    # Check if the gate parameter corresponding to param is a parameter expression
                    gate_param = param_occurence[0].params[param_occurence[1]]
                    if gate_param == param:
                        state = phase_fix_observable @ state
                    else:
                        # If the gate parameter is a parameter expressions the chain rule needs
                        # to be taken into account.
                        if isinstance(gate_param, ParameterExpression):
                            expr_grad = DerivativeBase.parameter_expression_grad(
                                gate_param, param)
                            state = (expr_grad * phase_fix_observable) @ state
                        else:
                            state *= 0

                    if m == 0 and k == 0:
                        phase_fix_state = state
                    else:
                        # Take the product rule into account
                        phase_fix_state += state
            # Create a list for the phase fix states
            if not phase_fix_states:
                phase_fix_states = [phase_fix_state]
            else:
                phase_fix_states += [phase_fix_state]

        # Get  4 * Re[〈∂kψ|∂lψ]
        qfi_operators = []
        # Add a working qubit
        qr_work_qubit = QuantumRegister(1, 'work_qubit')
        work_qubit = qr_work_qubit[0]
        additional_qubits = ([work_qubit], [])
        # create a copy of the original circuit with an additional work_qubit register
        circuit = state_qc.copy()
        circuit.add_register(qr_work_qubit)
        # Apply a Hadamard on the working qubit
        LinComb.insert_gate(circuit,
                            state_qc._parameter_table[params[0]][0][0],
                            HGate(),
                            qubits=[work_qubit])

        # Get the circuits needed to compute〈∂iψ|∂jψ〉
        for i, param_i in enumerate(params):  # loop over parameters
            qfi_ops = None
            for j, param_j in enumerate(params):
                # Get the gates of the quantum state which are parameterized by param_i
                param_gates_i = state_qc._parameter_table[param_i]
                for m_i, param_occurence_i in enumerate(param_gates_i):
                    coeffs_i, gates_i = LinComb._gate_gradient_dict(
                        param_occurence_i[0])[param_occurence_i[1]]

                    for k_i, gate_to_insert_i in enumerate(gates_i):
                        coeff_i = coeffs_i[k_i]
                        # Get the gates of the quantum state which are parameterized by param_j
                        param_gates_j = state_qc._parameter_table[param_j]
                        for m_j, param_occurence_j in enumerate(param_gates_j):
                            coeffs_j, gates_j = \
                                LinComb._gate_gradient_dict(param_occurence_j[0])[
                                    param_occurence_j[1]]
                            for k_j, gate_to_insert_j in enumerate(gates_j):
                                coeff_j = coeffs_j[k_j]

                                # create a copy of the original circuit with the same registers
                                qfi_circuit = QuantumCircuit(*circuit.qregs)
                                qfi_circuit.data = circuit.data

                                # Correct the phase of the working qubit according to coefficient i
                                # and coefficient j
                                sign = np.sign(np.conj(coeff_i) * coeff_j)
                                is_complex = np.iscomplex(
                                    np.conj(coeff_i) * coeff_j)
                                if sign == -1:
                                    if is_complex:
                                        LinComb.insert_gate(
                                            qfi_circuit,
                                            param_occurence_i[0],
                                            SdgGate(),
                                            qubits=[work_qubit])
                                    else:
                                        LinComb.insert_gate(
                                            qfi_circuit,
                                            param_occurence_i[0],
                                            ZGate(),
                                            qubits=[work_qubit])
                                else:
                                    if is_complex:
                                        LinComb.insert_gate(
                                            qfi_circuit,
                                            param_occurence_i[0],
                                            SGate(),
                                            qubits=[work_qubit])

                                LinComb.insert_gate(qfi_circuit,
                                                    param_occurence_i[0],
                                                    XGate(),
                                                    qubits=[work_qubit])

                                # Insert controlled, intercepting gate i - controlled by |1>
                                if isinstance(param_occurence_i[0], UGate):
                                    if param_occurence_i[1] == 0:
                                        LinComb.insert_gate(
                                            qfi_circuit, param_occurence_i[0],
                                            RZGate(param_occurence_i[0].
                                                   params[2]))
                                        LinComb.insert_gate(
                                            qfi_circuit, param_occurence_i[0],
                                            RXGate(np.pi / 2))
                                        LinComb.insert_gate(
                                            qfi_circuit,
                                            param_occurence_i[0],
                                            gate_to_insert_i,
                                            additional_qubits=additional_qubits
                                        )
                                        LinComb.insert_gate(
                                            qfi_circuit, param_occurence_i[0],
                                            RXGate(-np.pi / 2))
                                        LinComb.insert_gate(
                                            qfi_circuit, param_occurence_i[0],
                                            RZGate(-param_occurence_i[0].
                                                   params[2]))

                                    elif param_occurence_i[1] == 1:
                                        LinComb.insert_gate(
                                            qfi_circuit,
                                            param_occurence_i[0],
                                            gate_to_insert_i,
                                            after=True,
                                            additional_qubits=additional_qubits
                                        )
                                    else:
                                        LinComb.insert_gate(
                                            qfi_circuit,
                                            param_occurence_i[0],
                                            gate_to_insert_i,
                                            additional_qubits=additional_qubits
                                        )
                                else:
                                    LinComb.insert_gate(
                                        qfi_circuit,
                                        param_occurence_i[0],
                                        gate_to_insert_i,
                                        additional_qubits=additional_qubits)

                                LinComb.insert_gate(qfi_circuit,
                                                    gate_to_insert_i,
                                                    XGate(),
                                                    qubits=[work_qubit],
                                                    after=True)

                                # Insert controlled, intercepting gate j - controlled by |0>
                                if isinstance(param_occurence_j[0], UGate):
                                    if param_occurence_j[1] == 0:
                                        LinComb.insert_gate(
                                            qfi_circuit, param_occurence_j[0],
                                            RZGate(param_occurence_j[0].
                                                   params[2]))
                                        LinComb.insert_gate(
                                            qfi_circuit, param_occurence_j[0],
                                            RXGate(np.pi / 2))
                                        LinComb.insert_gate(
                                            qfi_circuit,
                                            param_occurence_j[0],
                                            gate_to_insert_j,
                                            additional_qubits=additional_qubits
                                        )
                                        LinComb.insert_gate(
                                            qfi_circuit, param_occurence_j[0],
                                            RXGate(-np.pi / 2))
                                        LinComb.insert_gate(
                                            qfi_circuit, param_occurence_j[0],
                                            RZGate(-param_occurence_j[0].
                                                   params[2]))

                                    elif param_occurence_j[1] == 1:
                                        LinComb.insert_gate(
                                            qfi_circuit,
                                            param_occurence_j[0],
                                            gate_to_insert_j,
                                            after=True,
                                            additional_qubits=additional_qubits
                                        )
                                    else:
                                        LinComb.insert_gate(
                                            qfi_circuit,
                                            param_occurence_j[0],
                                            gate_to_insert_j,
                                            additional_qubits=additional_qubits
                                        )
                                else:
                                    LinComb.insert_gate(
                                        qfi_circuit,
                                        param_occurence_j[0],
                                        gate_to_insert_j,
                                        additional_qubits=additional_qubits)

                                # Remove redundant gates

                                if j <= i:
                                    qfi_circuit = self.trim_circuit(
                                        qfi_circuit, param_occurence_i[0])
                                else:
                                    qfi_circuit = self.trim_circuit(
                                        qfi_circuit, param_occurence_j[0])
                                # Apply final Hadamard gate
                                qfi_circuit.h(work_qubit)
                                # Convert the quantum circuit into a CircuitStateFn and add the
                                # coefficients i, j and the original operator coefficient
                                term = np.sqrt(
                                    np.abs(coeff_i) *
                                    np.abs(coeff_j)) * operator.coeff
                                term = term * CircuitStateFn(qfi_circuit)

                                # Check if the gate parameters i and j are parameter expressions
                                gate_param_i = param_occurence_i[0].params[
                                    param_occurence_i[1]]
                                gate_param_j = param_occurence_j[0].params[
                                    param_occurence_j[1]]

                                meas = deepcopy(qfi_observable)
                                # If the gate parameter i is a parameter expression use the chain
                                # rule.
                                if isinstance(gate_param_i,
                                              ParameterExpression):
                                    expr_grad = DerivativeBase.parameter_expression_grad(
                                        gate_param_i, param_i)
                                    meas *= expr_grad
                                # If the gate parameter j is a parameter expression use the chain
                                # rule.
                                if isinstance(gate_param_j,
                                              ParameterExpression):
                                    expr_grad = DerivativeBase.parameter_expression_grad(
                                        gate_param_j, param_j)
                                    meas *= expr_grad
                                term = meas @ term

                                if m_i == 0 and k_i == 0 and m_j == 0 and k_j == 0:
                                    qfi_op = term
                                else:
                                    # Product Rule
                                    qfi_op += term

                # Compute −4 * Re(〈∂kψ|ψ〉〈ψ|∂lψ〉)
                def phase_fix_combo_fn(x):
                    return 4 * (-0.5) * (x[0] * np.conjugate(x[1]) +
                                         x[1] * np.conjugate(x[0]))

                phase_fix = 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ψ〉]
                if not qfi_ops:
                    qfi_ops = [qfi_op + phase_fix]
                else:
                    qfi_ops += [qfi_op + phase_fix]
            qfi_operators.append(ListOp(qfi_ops))
        # Return the full QFI
        return ListOp(qfi_operators)
def _bogoliubov_transform_num_conserving_jw(  # pylint: disable=invalid-name
    register: QuantumRegister, transformation_matrix: np.ndarray) -> Iterator[
        tuple[Gate, tuple[Qubit, ...]]]:
    n, _ = transformation_matrix.shape
    current_matrix = transformation_matrix
    left_rotations = []
    right_rotations = []

    # compute left and right Givens rotations
    for i in range(n - 1):
        if i % 2 == 0:
            # rotate columns by right multiplication
            for j in range(i + 1):
                target_index = i - j
                row = n - j - 1
                if not np.isclose(current_matrix[row, target_index], 0.0):
                    # zero out element at target index in given row
                    givens_mat = givens_matrix(
                        current_matrix[row, target_index + 1],
                        current_matrix[row, target_index],
                    )
                    right_rotations.append(
                        (givens_mat, (target_index + 1, target_index)))
                    current_matrix = apply_matrix_to_slices(
                        current_matrix,
                        givens_mat,
                        [(Ellipsis, target_index + 1),
                         (Ellipsis, target_index)],
                    )
        else:
            # rotate rows by left multiplication
            for j in range(i + 1):
                target_index = n - i + j - 1
                col = j
                if not np.isclose(current_matrix[target_index, col], 0.0):
                    # zero out element at target index in given column
                    givens_mat = givens_matrix(
                        current_matrix[target_index - 1, col],
                        current_matrix[target_index, col],
                    )
                    left_rotations.append(
                        (givens_mat, (target_index - 1, target_index)))
                    current_matrix = apply_matrix_to_slices(
                        current_matrix, givens_mat,
                        [target_index - 1, target_index])

    # convert left rotations to right rotations
    for givens_mat, (i, j) in reversed(left_rotations):
        givens_mat = givens_mat.T.conj()
        givens_mat[:, 0] *= current_matrix[i, i]
        givens_mat[:, 1] *= current_matrix[j, j]
        new_givens_mat = givens_matrix(givens_mat[1, 1], givens_mat[1, 0])
        right_rotations.append((new_givens_mat.T, (i, j)))
        phase_matrix = givens_mat @ new_givens_mat
        current_matrix[i, i] = phase_matrix[0, 0]
        current_matrix[j, j] = phase_matrix[1, 1]

    # yield operations
    for i in range(n):
        phi = np.angle(current_matrix[i, i])
        yield RZGate(phi), (register[i], )
    for givens_mat, (i, j) in reversed(right_rotations):
        theta = np.arccos(np.real(givens_mat[0, 0]))
        phi = np.angle(givens_mat[0, 1])
        yield XXPlusYYGate(2 * theta,
                           phi - np.pi / 2), (register[j], register[i])
 def test_controlled_rz(self):
     """Test the creation of a controlled RZ gate."""
     theta = 0.5
     self.assertEqual(RZGate(theta).control(), CRZGate(theta))