Example #1
0
 def _decompose_(self, qubits):
     if not self._has_unitary_():
         return NotImplemented
     result = [PAULI_GATES[p].on(q) for p, q in zip(self.pauli_mask, qubits) if p]
     if self.coefficient != 1:
         result.append(global_phase_op.global_phase_operation(self.coefficient))
     return result
Example #2
0
    def _decompose_(self, qubits):
        """An adjacency-respecting decomposition.

        0: ───p───@──────────────@───────@──────────@──────────
                  │              │       │          │
        1: ───p───X───@───p^-1───X───@───X──────@───X──────@───
                      │              │          │          │
        2: ───p───────X───p──────────X───p^-1───X───p^-1───X───

        where p = T**self._exponent
        """
        a, b, c = qubits

        # Hacky magic: avoid the non-adjacent edge.
        if hasattr(b, 'is_adjacent'):
            if not b.is_adjacent(a):
                b, c = c, b
            elif not b.is_adjacent(c):
                a, b = b, a

        p = common_gates.T**self._exponent
        sweep_abc = [common_gates.CNOT(a, b), common_gates.CNOT(b, c)]
        global_phase = 1j ** (2 * self.global_shift * self._exponent)
        global_phase = (
            complex(global_phase)
            if protocols.is_parameterized(global_phase) and global_phase.is_complex
            else global_phase
        )
        global_phase_operation = (
            [global_phase_op.global_phase_operation(global_phase)]
            if protocols.is_parameterized(global_phase) or abs(global_phase - 1.0) > 0
            else []
        )
        return global_phase_operation + [
            p(a),
            p(b),
            p(c),
            sweep_abc,
            p(b) ** -1,
            p(c),
            sweep_abc,
            p(c) ** -1,
            sweep_abc,
            p(c) ** -1,
            sweep_abc,
        ]
Example #3
0
    def _decompose_(self, qubits: Sequence['cirq.Qid']) -> 'cirq.OP_TREE':
        """Decompose the n-qubit diagonal gates into CNOT and Rz gates.

        A 3 qubits decomposition looks like
        0: ───────────────────────────────────X───Rz(6)───X───Rz(7)───X───Rz(5)───X───Rz(4)───
                                              │           │           │           │
        1: ───────────X───Rz(3)───X───Rz(2)───@───────────┼───────────@───────────┼───────────
                      │           │                       │                       │
        2: ───Rz(1)───@───────────@───────────────────────@───────────────────────@───────────

        where the angles in Rz gates are corresponding to the fast-walsh-Hadamard transform
        of diagonal_angles in the Gray Code order.

        For n qubits decomposition looks similar but with 2^n-1 Rz gates and 2^n-2 CNOT gates.

        The algorithm is implemented according to the paper:
            Welch, Jonathan, et al. "Efficient quantum circuits for diagonal unitaries without
            ancillas." New Journal of Physics 16.3 (2014): 033040.
            https://iopscience.iop.org/article/10.1088/1367-2630/16/3/033040/meta
        """
        n = self._num_qubits_()
        hat_angles = _fast_walsh_hadamard_transform(
            self._diag_angles_radians) / (2**n)

        # There is one global phase shift between unitary matrix of the diagonal gate and the
        # decomposed gates. On its own it is not physically observable. However, if using this
        # diagonal gate for sub-system like controlled gate, it is no longer equivalent. Hence,
        # we add global phase.
        # Global phase is ignored for parameterized gates as `cirq.GlobalPhaseGate` expects a
        # scalar value.
        decomposed_circ: List[Any] = ([
            global_phase_op.global_phase_operation(np.exp(1j * hat_angles[0]))
        ] if not protocols.is_parameterized(hat_angles[0]) else [])
        for i, bit_flip in _gen_gray_code(n):
            decomposed_circ.extend(
                self._decompose_for_basis(i, bit_flip, -hat_angles[i], qubits))
        return decomposed_circ
Example #4
0
    def _decompose_(self, qubits):
        """An adjacency-respecting decomposition.

        0: ───p_0───@──────────────@───────@──────────@──────────
                    │              │       │          │
        1: ───p_1───X───@───p_3────X───@───X──────@───X──────@───
                        │              │          │          │
        2: ───p_2───────X───p_4────────X───p_5────X───p_6────X───

        where p_i = T**(4*x_i) and x_i solve the system of equations
                    [0, 0, 1, 0, 1, 1, 1][x_0]   [r_1]
                    [0, 1, 0, 1, 1, 0, 1][x_1]   [r_2]
                    [0, 1, 1, 1, 0, 1, 0][x_2]   [r_3]
                    [1, 0, 0, 1, 1, 1, 0][x_3] = [r_4]
                    [1, 0, 1, 1, 0, 0, 1][x_4]   [r_5]
                    [1, 1, 0, 0, 0, 1, 1][x_5]   [r_6]
                    [1, 1, 1, 0, 1, 0, 0][x_6]   [r_7]
        where r_i is self._diag_angles_radians[i].

        The above system was created by equating the composition of the gates
        in the circuit diagram to np.diag(self._diag_angles) (shifted by a
        global phase of np.exp(-1j * self._diag_angles[0])).
        """

        a, b, c = qubits
        if hasattr(b, 'is_adjacent'):
            if not b.is_adjacent(a):
                b, c = c, b
            elif not b.is_adjacent(c):
                a, b = b, a
        sweep_abc = [common_gates.CNOT(a, b), common_gates.CNOT(b, c)]
        phase_matrix_inverse = 0.25 * np.array([
            [-1, -1, -1, 1, 1, 1, 1],
            [-1, 1, 1, -1, -1, 1, 1],
            [1, -1, 1, -1, 1, -1, 1],
            [-1, 1, 1, 1, 1, -1, -1],
            [1, 1, -1, 1, -1, -1, 1],
            [1, -1, 1, 1, -1, 1, -1],
            [1, 1, -1, -1, 1, 1, -1],
        ])
        shifted_angles_tail = [
            angle - self._diag_angles_radians[0]
            for angle in self._diag_angles_radians[1:]
        ]
        phase_solutions = phase_matrix_inverse.dot(shifted_angles_tail)
        p_gates = [
            pauli_gates.Z**(solution / np.pi) for solution in phase_solutions
        ]
        global_phase = 1j**(2 * self._diag_angles_radians[0] / np.pi)
        global_phase_operation = ([
            global_phase_op.global_phase_operation(global_phase)
        ] if protocols.is_parameterized(global_phase)
                                  or abs(global_phase - 1.0) > 0 else [])
        return global_phase_operation + [
            p_gates[0](a),
            p_gates[1](b),
            p_gates[2](c),
            sweep_abc,
            p_gates[3](b),
            p_gates[4](c),
            sweep_abc,
            p_gates[5](c),
            sweep_abc,
            p_gates[6](c),
            sweep_abc,
        ]