コード例 #1
0
  def test_can_optimize_single_qubit_group_debatable(self, *gates):
    # compare the TODO at the beginning of class XmonArchitecture

    gates = (
        circuit.PhasedXGate(0.4711, 0.42),
        circuit.PhasedXGate(0.815, 0.137)
    )

    # check type and value for xmon_arch.can_optimize_single_qubit_group(...)
    _check_boolean(
        self,
        self.xmon_arch.can_optimize_single_qubit_group(gates),
        True
    )

    # construct the pauli_transform for the full gate sequence (note that the
    # order for the factors needs to be reversed here)
    pauli_transform = np.eye(3)
    for gate in gates:
      pauli_transform = np.dot(gate.get_pauli_transform(), pauli_transform)

    # check that xmon_arch.decompose_single_qubit_gate(...) gives a gate
    # sequence of equal length (the result of that function will not match the
    # input sequence because it decomposes it into a PhasedX and a RotZ gate)
    self.assertLen(
        self.xmon_arch.decompose_single_qubit_gate(pauli_transform),
        2
    )
コード例 #2
0
  def test_circuit(self):
    # construct the original circuit
    circ_orig = circuit.Circuit(2, [
        circuit.Operation(circuit.PhasedXGate(0.47, 0.11), [0]),
        circuit.Operation(circuit.ControlledZGate(), [0, 1]),
        circuit.Operation(circuit.RotZGate(0.42), [1]),
    ])

    # export the circuit to Cirq
    circ_exported = cirq_converter.export_to_cirq(circ_orig)

    # check the type of circ_exported
    self.assertIsInstance(circ_exported, cirq.Circuit)

    # TODO(tfoesel):
    # a direct comparison between circ_orig and circ_exported would be better

    # reimport the circuit from Cirq
    circ_reimported = cirq_converter.import_from_cirq(circ_exported)

    # check that the number of operations and the gate types are conserved
    self.assertEqual(len(circ_orig), len(circ_reimported))
    self.assertEqual(
        [type(operation.get_gate()) for operation in circ_orig],
        [type(operation.get_gate()) for operation in circ_orig]
    )
コード例 #3
0
def import_from_cirq(obj):
    """Imports a gate, operation or circuit from Cirq.

  Args:
      obj: the Cirq object to be imported.

  Returns:
      the imported object (an instance of circuit.Circuit, circuit.Operation, or
      a subclass of circuit.Gate).

  Raises:
      TypeError: if import is not supported for the given type.
      ValueError: if the object cannot be imported successfully.
  """

    if isinstance(obj, cirq.PhasedXPowGate):
        return circuit.PhasedXGate(obj.exponent * np.pi,
                                   obj.phase_exponent * np.pi)
    elif isinstance(obj, cirq.ZPowGate):
        return circuit.RotZGate(obj.exponent * np.pi)
    elif isinstance(obj, cirq.CZPowGate):
        if not np.isclose(np.mod(obj.exponent, 2.0), 1.0):
            raise ValueError('partial ControlledZ gates are not supported')
        return circuit.ControlledZGate()
    elif isinstance(obj,
                    (cirq.SingleQubitMatrixGate, cirq.TwoQubitMatrixGate)):
        return circuit.MatrixGate(cirq.unitary(obj))
    elif isinstance(obj, cirq.GateOperation):
        return circuit.Operation(import_from_cirq(obj.gate),
                                 [qubit.x for qubit in obj.qubits])
    elif isinstance(obj, cirq.Circuit):
        qubits = obj.all_qubits()
        if not all(isinstance(qubit, cirq.LineQubit) for qubit in qubits):
            qubit_types = set(type(qubit) for qubit in qubits)
            qubit_types = sorted(qubit_type.__name__
                                 for qubit_type in qubit_types)
            raise ValueError(
                'import is supported for circuits on LineQubits only'
                ' [found qubit type(s): %s]' % ', '.join(qubit_types))
        return circuit.Circuit(
            max(qubit.x for qubit in qubits) + 1, [
                import_from_cirq(operation)
                for operation in itertools.chain.from_iterable(obj)
            ])
    else:
        raise TypeError('unknown type: %s' % type(obj).__name__)
コード例 #4
0
def main(argv):
  if len(argv) > 1:
    raise app.UsageError('Too many command-line arguments.')

  rule = rules.ExchangeCommutingOperations()
  circ = circuit.Circuit(7, [
      circuit.Operation(circuit.ControlledZGate(), [0, 1]),
      circuit.Operation(circuit.RotZGate(0.42), [0]),
      circuit.Operation(circuit.ControlledZGate(), [1, 2]),
      circuit.Operation(circuit.PhasedXGate(0.815, 0.4711), [1]),
      circuit.Operation(circuit.ControlledZGate(), [0, 1]),
      circuit.Operation(circuit.ControlledZGate(), [1, 2])
  ])

  transformations = tuple(rule.scan(circ))

  # Show 4 transformations.
  print(transformations)
コード例 #5
0
class XmonArchitectureTest(parameterized.TestCase):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.xmon_arch = architecture.XmonArchitecture()

    @parameterized.parameters(
        [[[]], [[circuit.RotZGate(0.42)]],
         [[circuit.PhasedXGate(0.4711, 0.137)]],
         [[circuit.RotZGate(0.42),
           circuit.PhasedXGate(0.4711, 0.137)]],
         [[circuit.PhasedXGate(0.4711, 0.137),
           circuit.RotZGate(0.42)]]])
    def test_can_optimize_single_qubit_group_negative(self, gates):
        # check type and value for xmon_arch.can_optimize_single_qubit_group(...)
        _check_boolean(self,
                       self.xmon_arch.can_optimize_single_qubit_group(gates),
                       False)

        # construct the pauli_transform for the full gate sequence (note that the
        # order for the factors needs to be reversed here)
        pauli_transform = np.eye(3)
        for gate in gates:
            pauli_transform = np.dot(gate.get_pauli_transform(),
                                     pauli_transform)

        # check that xmon_arch.decompose_single_qubit_gate(...) gives a gate
        # sequence of equal length (the result of that function does not need to
        # match the input sequence because the order of PhasedX and RotZ is
        # arbitrary, so this check would be too strict)
        self.assertEqual(
            len(self.xmon_arch.decompose_single_qubit_gate(pauli_transform)),
            len(gates))

    def test_can_optimize_single_qubit_group_debatable(self, *gates):
        # compare the TODO at the beginning of class XmonArchitecture

        gates = (circuit.PhasedXGate(0.4711,
                                     0.42), circuit.PhasedXGate(0.815, 0.137))

        # check type and value for xmon_arch.can_optimize_single_qubit_group(...)
        _check_boolean(self,
                       self.xmon_arch.can_optimize_single_qubit_group(gates),
                       True)

        # construct the pauli_transform for the full gate sequence (note that the
        # order for the factors needs to be reversed here)
        pauli_transform = np.eye(3)
        for gate in gates:
            pauli_transform = np.dot(gate.get_pauli_transform(),
                                     pauli_transform)

        # check that xmon_arch.decompose_single_qubit_gate(...) gives a gate
        # sequence of equal length (the result of that function will not match the
        # input sequence because it decomposes it into a PhasedX and a RotZ gate)
        self.assertLen(
            self.xmon_arch.decompose_single_qubit_gate(pauli_transform), 2)

    @parameterized.parameters(
        itertools.chain(
            [[circuit.RotZGate(0.0)]],
            [[circuit.PhasedXGate(0.0, 0.0)]],
            [[circuit.PhasedXGate(0.0, 0.42)]],
            [[circuit.PhasedXGate(0.0, 0.5 * np.pi)]],
            itertools.product([circuit.RotZGate(0.0)], [
                circuit.PhasedXGate(0.0, 0.0),
                circuit.PhasedXGate(0.0, 0.42),
                circuit.PhasedXGate(0.0, 0.5 * np.pi)
            ]),
            itertools.product([
                circuit.PhasedXGate(0.0, 0.0),
                circuit.PhasedXGate(0.0, 0.42),
                circuit.PhasedXGate(0.0, 0.5 * np.pi)
            ], [circuit.RotZGate(0.0)]),
            itertools.product([circuit.RotZGate(0.137)], [
                circuit.PhasedXGate(0.0, 0.0),
                circuit.PhasedXGate(0.0, 0.42),
                circuit.PhasedXGate(0.0, 0.5 * np.pi)
            ]),
            itertools.product([
                circuit.PhasedXGate(0.0, 0.0),
                circuit.PhasedXGate(0.0, 0.42),
                circuit.PhasedXGate(0.0, 0.5 * np.pi)
            ], [circuit.RotZGate(0.137)]),
            itertools.product([circuit.RotZGate(0.0)], [
                circuit.PhasedXGate(0.4711, 0.0),
                circuit.PhasedXGate(0.4711, 0.42),
                circuit.PhasedXGate(0.4711, 0.5 * np.pi)
            ]),
            itertools.product([
                circuit.PhasedXGate(0.4711, 0.0),
                circuit.PhasedXGate(0.4711, 0.42),
                circuit.PhasedXGate(0.4711, 0.5 * np.pi)
            ], [circuit.RotZGate(0.0)]),
            [[circuit.RotZGate(0.137),
              circuit.RotZGate(0.137)]],
            [[circuit.RotZGate(0.137),
              circuit.RotZGate(0.42)]],
            [
                [  # pylint: disable=g-complex-comprehension
                    circuit.PhasedXGate(0.4711, phase_angle),
                    circuit.PhasedXGate(0.42, phase_angle)
                ] for phase_angle in (0.0, 0.42, 0.5 * np.pi)
            ],
            [[circuit.RotZGate(0.137),
              circuit.PhasedXGate(np.pi, 0.42)]],
            [[circuit.PhasedXGate(np.pi, 0.42),
              circuit.RotZGate(0.137)]],
            [[
                circuit.RotZGate(0.815),
                circuit.PhasedXGate(np.pi, 0.42),
                circuit.RotZGate(0.137)
            ]]))
    def test_can_optimize_single_qubit_group_positive(self, *gates):
        gates = tuple(gates)

        # check type and value for xmon_arch.can_optimize_single_qubit_group(...)
        _check_boolean(self,
                       self.xmon_arch.can_optimize_single_qubit_group(gates),
                       True)

        # construct the pauli_transform for the full gate sequence (note that the
        # order for the factors needs to be reversed here)
        pauli_transform = np.eye(3)
        for gate in gates:
            pauli_transform = np.dot(gate.get_pauli_transform(),
                                     pauli_transform)

        # check that xmon_arch.decompose_single_qubit_gate(...) can construct a
        # sequence with *less* gates
        self.assertLess(
            len(self.xmon_arch.decompose_single_qubit_gate(pauli_transform)),
            len(gates))

    def test_can_optimize_single_qubit_group_error_not_iterable(self):
        with self.assertRaisesRegex(TypeError,
                                    r'\'int\' object is not iterable'):
            self.xmon_arch.can_optimize_single_qubit_group(42)

    def test_can_optimize_single_qubit_group_error_not_gate(self):
        with self.assertRaisesRegex(
                TypeError,
                r'illegal types found in gates: range \(should be subtypes of Gate\)'
        ):
            self.xmon_arch.can_optimize_single_qubit_group([range(42)])

    @parameterized.parameters([
        (np.eye(3), []),
        (transform.Rotation.from_euler('z',
                                       0.42).as_dcm(), [circuit.RotZGate]),
        (transform.Rotation.from_euler('zxz', [0.42, 0.4711, -0.42]).as_dcm(),
         [circuit.PhasedXGate]),
        (transform.Rotation.from_euler('zxz', [0.42, np.pi, -0.42]).as_dcm(),
         [circuit.PhasedXGate]),
        (transform.Rotation.from_euler('zxz', [0.42, 0.4711, 0.137]).as_dcm(),
         [circuit.PhasedXGate, circuit.RotZGate])
    ])
    def test_decompose_single_qubit_gate(self, pauli_transform, gate_types):
        # call the method to be tested
        gates = self.xmon_arch.decompose_single_qubit_gate(
            pauli_transform.copy())

        # check that gates is a list of Gate instances
        self.assertIsInstance(gates, list)
        self.assertTrue(all(isinstance(gate, circuit.Gate) for gate in gates))

        # check that the gates reconstruct the correct target operation
        pauli_reconstr = np.eye(3)
        for gate in gates:
            pauli_reconstr = np.dot(gate.get_pauli_transform(), pauli_reconstr)
        np.testing.assert_allclose(pauli_transform,
                                   pauli_reconstr,
                                   rtol=1e-5,
                                   atol=1e-8)

        # check that gates has the expected length
        self.assertEqual(len(gates), len(gate_types))

        # check that the gates match the expected types
        self.assertTrue(
            all(
                isinstance(gate, gate_type)
                for gate, gate_type in zip(gates, gate_types)))

    def test_decompose_single_qubit_gate_error_no_array(self):
        with self.assertRaisesRegex(TypeError,
                                    r'slice cannot be converted to np.array'):
            self.xmon_arch.decompose_single_qubit_gate(slice(42))

    def test_decompose_single_qubit_gate_error_illegal_dtype(self):
        with self.assertRaisesRegex(
                TypeError,
                r'illegal dtype for pauli_transform: complex128 \(must be safely'
                r' castable to float\)'):
            self.xmon_arch.decompose_single_qubit_gate(np.eye(3,
                                                              dtype=complex))

    def test_decompose_single_qubit_gate_error_illegal_shape(self):
        with self.assertRaisesRegex(
                ValueError,
                r'illegal shape for pauli_transform: \(3, 5\) \[expected: \(3, 3\)\]'
        ):
            self.xmon_arch.decompose_single_qubit_gate(np.random.randn(3, 5))

    def test_decompose_single_qubit_gate_error_not_orthogonal(self):
        with self.assertRaisesRegex(
                ValueError, r'pauli_transform is not an orthogonal matrix'):
            self.xmon_arch.decompose_single_qubit_gate(np.ones([3, 3]))
コード例 #6
0
class TestExportAndImport(parameterized.TestCase):

  @parameterized.parameters([
      circuit.PhasedXGate(0.47, 0.11),
      circuit.RotZGate(0.42),
      circuit.ControlledZGate(),
      circuit.MatrixGate(stats.unitary_group.rvs(2)),  # random 1-qubit unitary
      circuit.MatrixGate(stats.unitary_group.rvs(4)),  # random 2-qubit unitary
      circuit.MatrixGate(stats.unitary_group.rvs(8)),  # random 3-qubit unitary
      circuit.MatrixGate(stats.unitary_group.rvs(16))  # random 4-qubit unitary
  ])
  def test_gates(self, gate_orig):
    # export the gate to Cirq
    gate_exported = cirq_converter.export_to_cirq(gate_orig)

    # check that the gates are equivalent
    self.assertIsInstance(gate_exported, cirq.Gate)
    np.testing.assert_allclose(
        gate_orig.get_pauli_transform(),
        circuit.compute_pauli_transform(cirq.unitary(gate_exported)),
        rtol=1e-5, atol=1e-8
    )

    # reimport the gate from Cirq
    gate_reimported = cirq_converter.import_from_cirq(gate_exported)

    # check that the original and the reimported gate are equivalent
    self.assertIs(type(gate_reimported), type(gate_orig))
    self.assertEqual(
        gate_reimported.get_num_qubits(),
        gate_orig.get_num_qubits()
    )
    np.testing.assert_allclose(
        gate_orig.get_operator(),
        gate_reimported.get_operator(),
        rtol=1e-5, atol=1e-8
    )

  @parameterized.parameters([1, 2])
  def test_operations(self, num_qubits):
    # construct the original operation
    op_orig = circuit.Operation(
        circuit.MatrixGate(stats.unitary_group.rvs(2 ** num_qubits)),
        np.random.permutation(10)[:num_qubits]
    )

    # export the operation to Cirq
    op_exported = cirq_converter.export_to_cirq(op_orig)

    # check that the operations are equivalent
    self.assertIsInstance(op_exported, cirq.GateOperation)
    np.testing.assert_allclose(
        op_orig.get_gate().get_operator(),
        cirq.unitary(op_exported.gate),
        rtol=1e-5, atol=1e-8
    )
    self.assertTupleEqual(
        op_orig.get_qubits(),
        tuple(qubit.x for qubit in op_exported.qubits)
    )

    # reimport the operation from Cirq
    op_reimported = cirq_converter.import_from_cirq(op_exported)

    # check that the original and the reimported operation are equivalent
    self.assertIs(type(op_reimported), circuit.Operation)
    self.assertEqual(op_reimported.get_num_qubits(), op_orig.get_num_qubits())
    np.testing.assert_allclose(
        op_orig.get_gate().get_operator(),
        op_reimported.get_gate().get_operator()
    )
    self.assertTupleEqual(op_orig.get_qubits(), op_reimported.get_qubits())

  def test_circuit(self):
    # construct the original circuit
    circ_orig = circuit.Circuit(2, [
        circuit.Operation(circuit.PhasedXGate(0.47, 0.11), [0]),
        circuit.Operation(circuit.ControlledZGate(), [0, 1]),
        circuit.Operation(circuit.RotZGate(0.42), [1]),
    ])

    # export the circuit to Cirq
    circ_exported = cirq_converter.export_to_cirq(circ_orig)

    # check the type of circ_exported
    self.assertIsInstance(circ_exported, cirq.Circuit)

    # TODO(tfoesel):
    # a direct comparison between circ_orig and circ_exported would be better

    # reimport the circuit from Cirq
    circ_reimported = cirq_converter.import_from_cirq(circ_exported)

    # check that the number of operations and the gate types are conserved
    self.assertEqual(len(circ_orig), len(circ_reimported))
    self.assertEqual(
        [type(operation.get_gate()) for operation in circ_orig],
        [type(operation.get_gate()) for operation in circ_orig]
    )

  def test_export_unknown_type_error(self):
    with self.assertRaisesRegex(TypeError, r'unknown type: range'):
      cirq_converter.export_to_cirq(range(42))

  def test_import_unknown_type_error(self):
    with self.assertRaisesRegex(TypeError, r'unknown type: range'):
      cirq_converter.import_from_cirq(range(42))

  def test_import_partial_cz_error(self):
    partial_cz = cirq.CZPowGate(exponent=0.37)

    with self.assertRaisesRegex(
        ValueError,
        r'partial ControlledZ gates are not supported'):
      cirq_converter.import_from_cirq(partial_cz)

  def test_import_non_line_qubits_error(self):
    qubit_a = cirq.GridQubit(0, 1)
    qubit_b = cirq.GridQubit(0, 2)

    circ = cirq.Circuit(
        cirq.PhasedXPowGate(exponent=0.47, phase_exponent=0.11).on(qubit_a),
        cirq.CZPowGate(exponent=1.0).on(qubit_a, qubit_b),
        cirq.ZPowGate(exponent=0.42).on(qubit_b)
    )

    with self.assertRaisesRegex(
        ValueError,
        r'import is supported for circuits on LineQubits only \[found qubit'
        r' type\(s\): GridQubit\]'):
      cirq_converter.import_from_cirq(circ)
コード例 #7
0
  def decompose_single_qubit_gate(self,
                                  pauli_transform
                                 ):
    """Decomposes an arbitrary single-qubit gate into native Xmon gates.

    Args:
        pauli_transform: np.ndarray with shape (3, 3) and dtype float.
            The pauli_transform representing the (single-qubit) gate to be
            decomposed.

    Returns:
        a sequence of native Xmon gates that are equivalent to the input,
        consisting of a PhasedX and/or a RotZ gate

    Raises:
        TypeError: if pauli_transform is not a np.array with real dtype, and
            cannot be casted to one.
        ValueError: if pauli_transform does not have the correct shape or if
            pauli_transform is not an orthogonal matrix
    """

    if not hasattr(pauli_transform, '__getitem__'):
      raise TypeError('%s cannot be converted to np.array'
                      %type(pauli_transform).__name__)

    pauli_transform = np.array(pauli_transform)

    try:
      pauli_transform = pauli_transform.astype(float, casting='safe')
    except TypeError:
      raise TypeError('illegal dtype for pauli_transform: %s (must be safely'
                      ' castable to float)'%pauli_transform.dtype)

    if not np.array_equal(pauli_transform.shape, [3, 3]):
      raise ValueError(
          'illegal shape for pauli_transform: %s [expected: (3, 3)]'
          %str(pauli_transform.shape)
      )
    if not np.allclose(np.dot(pauli_transform, pauli_transform.T), np.eye(3)):
      raise ValueError('pauli_transform is not an orthogonal matrix')

    if np.allclose(pauli_transform, np.eye(3)):
      return []

    # We want to decompose a given single-qubit gate U into PhasedX and RotZ
    # gates. For this, we make the following ansatz:
    #
    #     ───U───   ≡   ───PhasedX(beta, -alpha)───RotZ(phi)───
    #
    # To obtain the parameters of the PhasedX and RotZ gate, we perform an Euler
    # angle decomposition for the pauli_transform of U.
    #
    # This can be motivated as follows. We have
    #
    #     PhasedX(beta, -alpha).pauli_transform ==
    #         == dot(Rz(-alpha), Rx(beta), Rz(alpha))
    #     RotZ(phi).pauli_transform == Rz(phi)
    #
    # where Rx and Rz are 3D rotations around the x and z axis, respectively.
    # From this, we get:
    #
    #     dot(RotZ(phi), PhasedX(alpha, beta)).pauli_transform ==
    #         == dot(Rz(phi-alpha), Rx(beta), Rz(alpha))
    #
    # The right-hand side is exactly the Euler angle decomposition with
    # signature 'zxz' and angles [alpha, beta, phi-alpha], which gives us a
    # direct way to extract the parameters for the two gates. Below, the angle
    # gamma will substitute phi-alpha.
    #
    # Note 1: The fact that this Euler angle decomposition is possible for every
    # SO(3) rotation, i.e. every possible Pauli transform, is one way to prove
    # universality for the combination of the PhasedX and RotZ gate.
    #
    # Note 2: In the calculation above, we have reversed two times the order of
    # the operations. This is because in circuit diagrams and for the Euler
    # angle decomposition (following the convention used in the corresponding
    # Scipy function), the first operation is left, whereas when multiplying the
    # associated operators, the first operation is right such that this is the
    # one applied first to the ket state (a vector multiplied from the right).

    # The two if-branches are special cases where the Euler decomposition into
    # 'zxz' is not anymore unique; here, we fix a choice that corresponds to
    # either just a PhasedX gate or just a RotZ gate, and thus minimizes the
    # total number of gates.
    #
    # In addition, the algorithm for Euler decomposition used in the else
    # branch seems to run into numerical precision issues for these cases; as
    # a positive side effect, these issues are circumvented.
    if np.isclose(pauli_transform[2, 2], 1.0):
      gamma = 0.5 * np.arctan2(pauli_transform[1, 0], pauli_transform[0, 0])
      beta = 0.0
      alpha = gamma
    elif np.isclose(pauli_transform[2, 2], -1.0):
      gamma = 0.5 * np.arctan2(pauli_transform[1, 0], pauli_transform[0, 0])
      beta = np.pi
      alpha = -gamma
    else:
      rot = scipy.spatial.transform.Rotation.from_dcm(pauli_transform)
      alpha, beta, gamma = rot.as_euler('zxz')

    gates = []
    if not np.isclose(np.cos(beta), 1.0):
      gates.append(circuit.PhasedXGate(beta, -alpha))
    if not np.isclose(np.cos(alpha + gamma), 1.0):
      gates.append(circuit.RotZGate(alpha + gamma))
    return gates
コード例 #8
0
def parse_gates(gates, *gate_types):
    """Parses gates into expected gate types."""

    if len(gates) != len(gate_types):
        raise ValueError(
            'inconsistent length of gates and gate_types (%d vs %d)' %
            (len(gates), len(gate_types)))

    parsed_gates = []

    for gate_in, gate_type in zip(gates, gate_types):
        if not isinstance(gate_in, circuit.Gate):
            raise TypeError('%s is not a Gate' % type(gate_in).__name__)
        if not isinstance(gate_type, type):
            raise TypeError('%s instance is not a type' %
                            type(gate_type).__name__)
        if not issubclass(gate_type, circuit.Gate):
            raise TypeError('%s is not a Gate type' % gate_type.__name__)

        if gate_type == circuit.PhasedXGate:
            if gate_in.get_num_qubits() != 1:
                return None
            elif isinstance(gate_in, circuit.PhasedXGate):
                gate_out = gate_in
            elif isinstance(gate_in, circuit.RotZGate):
                if gate_in.is_identity(phase_invariant=True):
                    gate_out = circuit.PhasedXGate(0.0, 0.0)
                else:
                    return None
            else:
                pauli_transform = gate_in.get_pauli_transform()
                if np.isclose(pauli_transform[2, 2], -1.0):
                    gate_out = circuit.PhasedXGate(
                        np.pi, 0.5 * np.arctan2(pauli_transform[0, 1],
                                                pauli_transform[0, 0]))
                else:
                    rotation = scipy.spatial.transform.Rotation.from_dcm(
                        pauli_transform)
                    alpha, beta, gamma = rotation.as_euler('zxz')
                    if np.isclose(alpha, -gamma):
                        gate_out = circuit.PhasedXGate(beta, -alpha)
                    else:
                        return None
        elif gate_type == circuit.RotZGate:
            if gate_in.get_num_qubits() != 1:
                return None
            elif isinstance(gate_in, circuit.RotZGate):
                gate_out = gate_in
            elif isinstance(gate_in, circuit.PhasedXGate):
                if gate_in.is_identity(phase_invariant=True):
                    gate_out = circuit.RotZGate(0.0)
                else:
                    return None
            else:
                pauli_transform = gate_in.get_pauli_transform()
                if np.isclose(pauli_transform[2, 2], 1.0):
                    gate_out = circuit.RotZGate(
                        np.arctan2(pauli_transform[1, 0], pauli_transform[0,
                                                                          0]))
                else:
                    return None
        elif gate_type == circuit.ControlledZGate:
            if gate_in.get_num_qubits() != 2:
                return None
            elif isinstance(gate_in, circuit.ControlledZGate):
                gate_out = gate_in
            elif gate_in == circuit.ControlledZGate():
                gate_out = circuit.ControlledZGate()
            else:
                return None
        else:
            raise ValueError('unknown Gate type: %s' % gate_type.__name__)

        assert isinstance(gate_out, gate_type)

        parsed_gates.append(gate_out)

    assert len(parsed_gates) == len(gates)

    return parsed_gates