Ejemplo n.º 1
0
def test_is_unitary_tolerance():
    atol = 0.5

    # Pays attention to specified tolerance.
    assert cirq.is_unitary(np.array([[1, 0], [-0.5, 1]]), atol=atol)
    assert not cirq.is_unitary(np.array([[1, 0], [-0.6, 1]]), atol=atol)

    # Error isn't accumulated across entries.
    assert cirq.is_unitary(np.array([[1.2, 0, 0], [0, 1.2, 0], [0, 0, 1.2]]), atol=atol)
    assert not cirq.is_unitary(np.array([[1.2, 0, 0], [0, 1.3, 0], [0, 0, 1.2]]), atol=atol)
Ejemplo n.º 2
0
def two_level_decompose_gray(m):
    """Decomposes a unitary matrix into two level operations, if possible and returns list matrices which multiply to
    A with indexing.

    Args:
        m: Matrix to be decomposed

    Returns:
        result: Returns list of decomposed matrices (single bit acting)
        idx: Returns the matrix indexes

    Raises:
        AssertionError:
            Matrix m must be a power of 2
            Matrix m must be a square matrix
            Matrix m is not a unitary matrix

    """

    n = m.shape[0]
    assert is_power_of_two(n)
    assert m.shape == (n, n), "Matrix must be square."
    assert cirq.is_unitary(m)

    perm = [x ^ (x // 2) for x in range(n)]  # Gray code.
    result, idxs = two_level_decompose(permute_matrix(m, perm))
    for i in range(len(idxs)):
        index1, index2 = idxs[i]
        idxs[i] = perm[index1], perm[index2]

    return result, idxs
Ejemplo n.º 3
0
def three_qubit_matrix_to_operations(
    q0: ops.Qid, q1: ops.Qid, q2: ops.Qid, u: np.ndarray, atol: float = 1e-8
) -> Sequence[ops.Operation]:
    """Returns operations for a 3 qubit unitary.

    The algorithm is described in Shende et al.:
    Synthesis of Quantum Logic Circuits. Tech. rep. 2006,
    https://arxiv.org/abs/quant-ph/0406176

    Args:
        q0: first qubit
        q1: second qubit
        q2: third qubit
        u: unitary matrix
        atol: A limit on the amount of absolute error introduced by the
            construction.

    Returns:
        The resulting operations will have only known two-qubit and one-qubit
        gates based operations, namely CZ, CNOT and rx, ry, PhasedXPow gates.

    Raises:
        ValueError: If the u matrix is non-unitary or not of shape (8,8).
    """
    if np.shape(u) != (8, 8):
        raise ValueError(f"Expected unitary matrix with shape (8,8) got {np.shape(u)}")
    if not cirq.is_unitary(u, atol=atol):
        raise ValueError(f"Matrix is not unitary: {u}")

    try:
        from scipy.linalg import cossin
    except ImportError:  # coverage: ignore
        # coverage: ignore
        raise ImportError(
            "cirq.three_qubit_unitary_to_operations requires "
            "SciPy 1.5.0+, as it uses the cossin function. Please"
            " upgrade scipy in your environment to use this "
            "function!"
        )
    (u1, u2), theta, (v1h, v2h) = cossin(u, 4, 4, separate=True)

    cs_ops = _cs_to_ops(q0, q1, q2, theta)
    if len(cs_ops) > 0 and cs_ops[-1] == cirq.CZ(q2, q0):
        # optimization A.1 - merging the last CZ from the end of CS into UD
        # cz = cirq.Circuit([cs_ops[-1]]).unitary()
        # CZ(c,a) = CZ(a,c) as CZ is symmetric
        # for the u1⊕u2 multiplexor operator:
        # as u1(b,c) is the operator in case a = \0>,
        # and u2(b,c) is the operator for (b,c) in case a = |1>
        # we can represent the merge by phasing u2 with I ⊗ Z
        u2 = u2 @ np.diag([1, -1, 1, -1])
        cs_ops = cs_ops[:-1]

    d_ud, ud_ops = _two_qubit_multiplexor_to_ops(q0, q1, q2, u1, u2, shift_left=True, atol=atol)

    _, vdh_ops = _two_qubit_multiplexor_to_ops(
        q0, q1, q2, v1h, v2h, shift_left=False, diagonal=d_ud, atol=atol
    )

    return list(cirq.Circuit(vdh_ops + cs_ops + ud_ops).all_operations())
Ejemplo n.º 4
0
def unitary2x2_to_gates(m):
    """Decomposes a two level unitary to Ry, Rz and R1 gates

    Args:
        m: Matrix to be converted into gates

    Returns:
        result: Returns result (list of gates to be applied from function su_to_gates) with additional R1 gates

    Raises:
        AssertionError:
            Matrix m is not a unitary matrix

    """

    assert cirq.is_unitary(m)
    phi = np.angle(np.linalg.det(m))
    if np.abs(phi) < 1e-9:
        return su_to_gates(m)
    elif np.allclose(m, np.array([[0, 1], [1, 0]], dtype=np.complex128)):
        return [('X', 'n/a')]
    else:
        m = np.diag([1.0, np.exp(-1j * phi)]) @ m
        return su_to_gates(m) + [('R1', phi)]
Ejemplo n.º 5
0
def test_is_unitary():
    assert cirq.is_unitary(np.empty((0, 0)))
    assert not cirq.is_unitary(np.empty((1, 0)))
    assert not cirq.is_unitary(np.empty((0, 1)))

    assert cirq.is_unitary(np.array([[1]]))
    assert cirq.is_unitary(np.array([[-1]]))
    assert cirq.is_unitary(np.array([[1j]]))
    assert not cirq.is_unitary(np.array([[5]]))
    assert not cirq.is_unitary(np.array([[3j]]))

    assert not cirq.is_unitary(np.array([[1, 0]]))
    assert not cirq.is_unitary(np.array([[1], [0]]))

    assert not cirq.is_unitary(np.array([[1, 0], [0, -2]]))
    assert cirq.is_unitary(np.array([[1, 0], [0, -1]]))
    assert cirq.is_unitary(np.array([[1j, 0], [0, 1]]))
    assert not cirq.is_unitary(np.array([[1, 0], [1, 1]]))
    assert not cirq.is_unitary(np.array([[1, 1], [0, 1]]))
    assert not cirq.is_unitary(np.array([[1, 1], [1, 1]]))
    assert not cirq.is_unitary(np.array([[1, -1], [1, 1]]))
    assert cirq.is_unitary(np.array([[1, -1], [1, 1]]) * np.sqrt(0.5))
    assert cirq.is_unitary(np.array([[1, 1j], [1j, 1]]) * np.sqrt(0.5))
    assert not cirq.is_unitary(np.array([[1, -1j], [1j, 1]]) * np.sqrt(0.5))

    assert cirq.is_unitary(
        np.array([[1, 1j + 1e-11], [1j, 1 + 1j * 1e-9]]) * np.sqrt(0.5))
Ejemplo n.º 6
0
def matrix_to_sycamore_operations(
        target_qubits: List[cirq.GridQubit],
        matrix: np.ndarray) -> Tuple[cirq.OP_TREE, List[cirq.GridQubit]]:
    """A method to convert a unitary matrix to a list of Sycamore operations.

    This method will return a list of `cirq.Operation`s using the qubits and (optionally) ancilla
    qubits to implement the unitary matrix `matrix` on the target qubits `qubits`.
    The operations are also supported by `cirq.google.gate_sets.SYC_GATESET`.

    Args:
        target_qubits: list of qubits the returned operations will act on. The qubit order defined by the list
            is assumed to be used by the operations to implement `matrix`.
        matrix: a matrix that is guaranteed to be unitary and of size (2**len(qs), 2**len(qs)).
    Returns:
        A tuple of operations and ancilla qubits allocated.
            Operations: In case the matrix is supported, a list of operations `ops` is returned.
                `ops` acts on `qs` qubits and for which `cirq.unitary(ops)` is equal to `matrix` up
                 to certain tolerance. In case the matrix is not supported, it might return NotImplemented to
                 reduce the noise in the judge output.
            Ancilla qubits: In case ancilla qubits are allocated a list of ancilla qubits. Otherwise
                an empty list.
        .
    """
    #number of target qubits
    l = target_qubits
    if isinstance(target_qubits, list):
        qs = len(target_qubits)
    else:
        qs = 1

    #check for the input matrix to be unitary
    unitary = cirq.is_unitary(matrix)
    if not (unitary):
        print("Error: Non unitary matrix")
        return NotImplemented, []

    #class for the input unitary gate
    class unigate(cirq.Gate):
        def __init__(self, qs, matrix):
            super(unigate, self)

        def _num_qubits_(self):
            return qs

        def _unitary_(self):
            return matrix

        def _circuit_diagram_info_(self, args):
            return "Uni"

    #gate with the unitary input matrix
    ug = unigate(qs, matrix)

    #we make use of the converter for sycamore gates
    converter = cirq.google.ConvertToSycamoreGates()

    #we create the input qubits
    if qs == 1:
        converted = converter.convert(ug.on(l[0]))
    elif qs == 2:
        converted = converter.convert(ug.on(l[0], l[1]))
    elif qs == 3:
        converted = converter.convert(ug.on(l[0], l[1], l[2]))
    elif qs == 4:
        converted = converter.convert(ug.on(l[0], l[1], l[2], l[3]))
    elif qs == 5:
        converted = converter.convert(ug.on(l[0], l[1], l[2], l[3], l[4]))
    elif qs == 6:
        converted = converter.convert(ug.on(l[0], l[1], l[2], l[3], l[4],
                                            l[5]))
    elif qs == 7:
        converted = converter.convert(
            ug.on(l[0], l[1], l[2], l[3], l[4], l[5], l[6]))
    elif qs == 8:
        converted = converter.convert(
            ug.on(l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7]))
    else:
        print("Error in conversion")
        return NotImplemented, []

    return converted, []
Ejemplo n.º 7
0
    def translate_cirq_to_qtrajectory(
        self,
        qubit_order: cirq.ops.QubitOrderOrList = cirq.ops.QubitOrder.DEFAULT
    ) -> qsim.NoisyCircuit:
        """
        Translates this noisy Cirq circuit to the qsim representation.
        :qubit_order: Ordering of qubits
        :return: a C++ qsim NoisyCircuit object
        """
        qsim_ncircuit = qsim.NoisyCircuit()
        ordered_qubits = cirq.ops.QubitOrder.as_qubit_order(
            qubit_order).order_for(self.all_qubits())

        # qsim numbers qubits in reverse order from cirq
        ordered_qubits = list(reversed(ordered_qubits))

        qsim_ncircuit.num_qubits = len(ordered_qubits)

        def has_qsim_kind(op: cirq.ops.GateOperation):
            return _cirq_gate_kind(op.gate) != None

        def to_matrix(op: cirq.ops.GateOperation):
            mat = cirq.unitary(op.gate, None)
            if mat is None:
                return NotImplemented

            return cirq.ops.MatrixGate(mat).on(*op.qubits)

        qubit_to_index_dict = {q: i for i, q in enumerate(ordered_qubits)}
        time_offset = 0
        for moment in self:
            moment_length = 0
            ops_by_gate = []
            ops_by_mix = []
            ops_by_channel = []
            # Capture ops of each type in the appropriate list.
            for qsim_op in moment:
                if cirq.has_unitary(qsim_op) or cirq.is_measurement(qsim_op):
                    oplist = cirq.decompose(qsim_op,
                                            fallback_decomposer=to_matrix,
                                            keep=has_qsim_kind)
                    ops_by_gate.append(oplist)
                    moment_length = max(moment_length, len(oplist))
                    pass
                elif cirq.has_mixture(qsim_op):
                    ops_by_mix.append(qsim_op)
                    moment_length = max(moment_length, 1)
                    pass
                elif cirq.has_channel(qsim_op):
                    ops_by_channel.append(qsim_op)
                    moment_length = max(moment_length, 1)
                    pass
                else:
                    raise ValueError(f'Encountered unparseable op: {qsim_op}')

            # Gates must be added in time order.
            for gi in range(moment_length):
                # Handle gate output.
                for gate_ops in ops_by_gate:
                    if gi >= len(gate_ops):
                        continue
                    qsim_op = gate_ops[gi]
                    time = time_offset + gi
                    gate_kind = _cirq_gate_kind(qsim_op.gate)
                    add_op_to_circuit(qsim_op, time, qubit_to_index_dict,
                                      qsim_ncircuit)
                # Handle mixture output.
                for mixture in ops_by_mix:
                    mixdata = []
                    for prob, mat in cirq.mixture(mixture):
                        square_mat = np.reshape(mat,
                                                (int(np.sqrt(mat.size)), -1))
                        unitary = cirq.is_unitary(square_mat)
                        # Package matrix into a qsim-friendly format.
                        mat = np.reshape(mat, (-1, )).astype(np.complex64,
                                                             copy=False)
                        mixdata.append((prob, mat.view(np.float32), unitary))
                    qubits = [qubit_to_index_dict[q] for q in mixture.qubits]
                    qsim.add_channel(time_offset, qubits, mixdata,
                                     qsim_ncircuit)
                # Handle channel output.
                for channel in ops_by_channel:
                    chdata = []
                    for i, mat in enumerate(cirq.channel(channel)):
                        square_mat = np.reshape(mat,
                                                (int(np.sqrt(mat.size)), -1))
                        unitary = cirq.is_unitary(square_mat)
                        singular_vals = np.linalg.svd(square_mat)[1]
                        lower_bound_prob = min(singular_vals)**2
                        # Package matrix into a qsim-friendly format.
                        mat = np.reshape(mat, (-1, )).astype(np.complex64,
                                                             copy=False)
                        chdata.append(
                            (lower_bound_prob, mat.view(np.float32), unitary))
                    qubits = [qubit_to_index_dict[q] for q in channel.qubits]
                    qsim.add_channel(time_offset, qubits, chdata,
                                     qsim_ncircuit)
            time_offset += moment_length

        return qsim_ncircuit
def _unitary_power(U, p):
    """Raises unitary matrix U to power p."""
    assert cirq.is_unitary(U)
    eig_vals, eig_vectors = np.linalg.eig(U)
    Q = np.array(eig_vectors)
    return Q @ np.diag(np.exp(p * 1j * np.angle(eig_vals))) @ Q.conj().T
    def decompose(self, matrix, controls, target, free_qubits=[], power=1.0):
        """Implements action of multi-controlled unitary gate.

        Returns sequence of operations, which, when applied in sequence, gives
        result equivalent to applying single-qubit gate with matrix
        `matrix^power` on `target`, controlled by `controls`.

        Result is guaranteed to consist exclusively of 1-qubit, CNOT and CCNOT
        gates.

        If matrix is special unitary, result has length `O(len(controls))`.
        Otherwise result has length `O(len(controls)**2)`.

        Args:
            matrix - 2x2 numpy unitary matrix (of real or complex dtype).
            controls - control qubits.
            targets - target qubits.
            free_qubits - qubits which are neither controlled nor target. Can
                be modified by algorithm but will end up in inital state.
            power - power in which `unitary` should be raised. Deafults to 1.0.
        """
        assert cirq.is_unitary(matrix)
        assert matrix.shape == (2, 2)
        u = _unitary_power(matrix, power)

        # Matrix parameters, see definitions in [1], chapter 4.
        delta = np.angle(np.linalg.det(u)) * 0.5
        alpha = np.angle(u[0, 0]) + np.angle(u[0, 1]) - 2 * delta
        beta = np.angle(u[0, 0]) - np.angle(u[0, 1])
        theta = 2 * np.arccos(np.minimum(1.0, np.abs(u[0, 0])))

        # Decomposing matrix into three matrices - see [1], lemma 4.3.
        A = _Rz(alpha) @ _Ry(theta / 2)
        B = _Ry(-theta / 2) @ _Rz(-(alpha + beta) / 2)
        C = _Rz((beta - alpha) / 2)
        X = np.array([[0, 1], [1, 0]])
        if not np.allclose(A @ B @ C, np.eye(2), atol=1e-4):
            print('alpha', alpha)
            print('beta', beta)
            print('theta', theta)
            print('delta', delta)
            print('A', A)
            print('B', B)
            print('C', C)
            print(np.abs(A @ B @ C))
        assert np.allclose(A @ B @ C, np.eye(2), atol=1e-2)
        assert np.allclose(A @ X @ B @ X @ C,
                           u / np.exp(1j * delta),
                           atol=1e-2)

        m = len(controls)
        ctrl = controls
        assert m > 0, "No control qubits."

        if m == 1:
            # See [1], chapter 5.1.
            result = [
                cirq.ZPowGate(exponent=delta / np.pi).on(ctrl[0]),
                cirq.rz(-0.5 * (beta - alpha)).on(target),
                cirq.CNOT.on(ctrl[0], target),
                cirq.rz(0.5 * (beta + alpha)).on(target),
                cirq.ry(0.5 * theta).on(target),
                cirq.CNOT.on(ctrl[0], target),
                cirq.ry(-0.5 * theta).on(target),
                cirq.rz(-alpha).on(target),
            ]

            # Remove no-ops.
            result = [g for g in result if not _is_identity(g._unitary_())]

            return result
        else:
            gate_is_special_unitary = np.allclose(delta, 0)

            if gate_is_special_unitary:
                # O(n) decomposition of SU matrix - [1], lemma 7.9.
                cnot_seq = self.multi_ctrl_x(ctrl[:-1],
                                             target,
                                             free_qubits=[ctrl[-1]])
                result = []
                result += self.decompose(C, [ctrl[-1]], target)
                result += cnot_seq
                result += self.decompose(B, [ctrl[-1]], target)
                result += cnot_seq
                result += self.decompose(A, [ctrl[-1]], target)
                return result
            else:
                # O(n^2) decomposition - [1], lemma 7.5.
                cnot_seq = self.multi_ctrl_x(ctrl[:-1],
                                             ctrl[-1],
                                             free_qubits=free_qubits +
                                             [target])
                part1 = self.decompose(matrix, [ctrl[-1]],
                                       target,
                                       power=0.5 * power)
                part2 = self.decompose(matrix, [ctrl[-1]],
                                       target,
                                       power=-0.5 * power)
                part3 = self.decompose(matrix,
                                       ctrl[:-1],
                                       target,
                                       power=0.5 * power,
                                       free_qubits=free_qubits + [ctrl[-1]])
                return part1 + cnot_seq + part2 + cnot_seq + part3

        return circuit
Ejemplo n.º 10
0
    def trotter_step(
            self,
            qubits: Sequence[cirq.QubitId],
            time: float,
            control_qubit: Optional[cirq.QubitId]=None
            ) -> cirq.OP_TREE:

        n_qubits = len(qubits)

        # Simulate the off-diagonal one-body terms.
        yield swap_network(
                qubits,
                lambda p, q, a, b: ControlledXXYYGate(duration=
                    self.one_body_coefficients[p, q].real * time).on(
                        control_qubit, a, b),
                fermionic=True)
        qubits = qubits[::-1]

        # Simulate the diagonal one-body terms.
        yield (cirq.Rot11Gate(rads=
                   -self.one_body_coefficients[j, j].real * time).on(
                       control_qubit, qubits[j])
               for j in range(n_qubits))

        # Simulate each singular vector of the two-body terms.
        prior_basis_matrix = numpy.identity(n_qubits, complex)
        for j in range(len(self.eigenvalues)):

            # Get the basis change matrix and its inverse.
            two_body_coefficients = self.scaled_density_density_matrices[j]
            basis_change_matrix = self.basis_change_matrices[j]

            # If the basis change matrix is unitary, merge rotations.
            # Otherwise, you must simulate two basis rotations.
            is_unitary = cirq.is_unitary(basis_change_matrix)
            if is_unitary:
                inverse_basis_matrix = numpy.conjugate(
                    numpy.transpose(basis_change_matrix))
                merged_basis_matrix = numpy.dot(prior_basis_matrix,
                                                inverse_basis_matrix)
                yield bogoliubov_transform(qubits, merged_basis_matrix)
            else:
                # TODO add LiH test to cover this.
                # coverage: ignore
                if j:
                    yield bogoliubov_transform(qubits, prior_basis_matrix)
                yield cirq.inverse(
                    bogoliubov_transform(qubits, basis_change_matrix))

            # Simulate the off-diagonal two-body terms.
            yield swap_network(
                    qubits,
                    lambda p, q, a, b: Rot111Gate(rads=
                        -2 * two_body_coefficients[p, q] * time).on(
                            control_qubit, a, b))
            qubits = qubits[::-1]

            # Simulate the diagonal two-body terms.
            yield (cirq.Rot11Gate(rads=
                       -two_body_coefficients[k, k] * time).on(
                           control_qubit, qubits[k])
                   for k in range(n_qubits))

            # Undo basis transformation non-unitary case.
            # Else, set basis change matrix to prior matrix for merging.
            if is_unitary:
                prior_basis_matrix = basis_change_matrix
            else:
                # TODO add LiH test to cover this.
                # coverage: ignore
                yield bogoliubov_transform(qubits, basis_change_matrix)
                prior_basis_matrix = numpy.identity(n_qubits, complex)

        # Undo final basis transformation in unitary case.
        if is_unitary:
            yield bogoliubov_transform(qubits, prior_basis_matrix)

        # Apply phase from constant term
        yield cirq.RotZGate(rads=-self.constant * time).on(control_qubit)
Ejemplo n.º 11
0
def two_level_decompose(m):
    """Decomposes a unitary matrix into two level operations, if possible and returns list of decomposed unitary
    matrices with indexing.

    Args:
        m: Matrix to be decomposed

    Returns:
        result: Returns list of decomposed unitary matrices
        idx: Returns the matrix indexes

     Raises:
        AssertionError:
            Matrix m is not a unitary matrix

    """

    assert cirq.is_unitary(m)
    n = m.shape[0]
    A = np.array(m, dtype=np.complex128)

    result = []
    idxs = []

    for i in range(n - 2):
        for j in range(n - 1, i, -1):
            a = A[i, j - 1]
            b = A[i, j]
            if abs(A[i, j]) < 1e-9:
                u_2x2 = np.eye(2, dtype=np.complex128)
                if j == i + 1:
                    u_2x2 = np.array([[1 / a, 0], [0, a]], dtype=np.complex128)
            elif abs(A[i, j - 1]) < 1e-9:
                u_2x2 = np.array([[0, 1], [1, 0]], dtype=np.complex128)
                if j == i + 1:
                    u_2x2 = np.array([[0, b], [1 / b, 0]], dtype=np.complex128)
            else:
                theta = np.arctan(np.abs(b / a))
                lmbda = -np.angle(a)
                mu = np.pi + np.angle(b) - np.angle(a) - lmbda
                u_2x2 = np.array([[
                    np.cos(theta) * np.exp(1j * lmbda),
                    np.sin(theta) * np.exp(1j * mu)
                ],
                                  [
                                      -np.sin(theta) * np.exp(-1j * mu),
                                      np.cos(theta) * np.exp(-1j * lmbda)
                                  ]],
                                 dtype=np.complex128)

            A[:, (j - 1, j)] = A[:, (j - 1, j)] @ u_2x2
            if not np.allclose(u_2x2, np.eye(2, dtype=np.complex128)):
                result.append(u_2x2.conj().T)
                idxs.append((j - 1, j))

    last_matrix = A[n - 2:n, n - 2:n]
    if not np.allclose(last_matrix, np.eye(2, dtype=np.complex128)):
        result.append(last_matrix)
        idxs.append((n - 2, n - 1))

    return result, idxs