Esempio n. 1
0
def left_multiply(state: State, apply_to: Union[int, list], op):
    """
    Apply a multi-qubit operator on several qubits (indexed in apply_to) of the input codes.
    :param state: input wavefunction or density matrix
    :type state: np.ndarray
    :param apply_to: zero-based indices of qudit locations to apply the operator
    :type apply_to: list of int
    :param op: Operator to act with.
    :type op: np.ndarray (2-dimensional)
    """
    if isinstance(apply_to, int):
        apply_to = [apply_to]
    if not isinstance(op, list):
        op = [op]
    # Type handling: to determine if Pauli, check if a list of strings
    pauli = False
    if isinstance(op, list):
        if all(isinstance(elem, str) for elem in op):
            pauli = True
        else:
            op = tools.tensor_product(op)
    n_op = len(apply_to)
    if not pauli:
        if tools.is_sorted(apply_to):
            # Generate all shapes for left multiplication
            preshape = (d**n) * np.ones((2, n_op), dtype=int)
            preshape[1, 0] = int(state.dimension /
                                 ((d**n)**(1 + apply_to[n_op - 1])))
            if n_op > 1:
                preshape[1, 1:] = np.flip((d**n)**np.diff(apply_to)) / (d**n)

            shape1 = np.zeros(2 * n_op + 1, dtype=int)
            shape2 = np.zeros(2 * n_op + 1, dtype=int)
            order1 = np.zeros(2 * n_op + 1, dtype=int)
            order2 = np.zeros(2 * n_op + 1, dtype=int)

            shape1[:-1] = np.flip(preshape, axis=0).reshape((2 * n_op),
                                                            order='F')
            shape1[-1] = -1
            shape2[:-1] = preshape.reshape((-1), order='C')
            shape2[-1] = -1

            preorder = np.arange(2 * n_op)
            order1[:-1] = np.flip(preorder.reshape((-1, 2), order='C'),
                                  axis=1).reshape((-1), order='F')
            order2[:-1] = np.flip(preorder.reshape((2, -1), order='C'),
                                  axis=0).reshape((-1), order='F')
            order1[-1] = 2 * n_op
            order2[-1] = 2 * n_op

            # Now left multiply
            out = state.reshape(shape1, order='F').transpose(order1)
            out = np.dot(op, out.reshape(((d**n)**n_op, -1), order='F'))
            out = out.reshape(shape2, order='F').transpose(order2)
            out = out.reshape(state.shape, order='F')
            return State(out,
                         is_ket=state.is_ket,
                         IS_subspace=state.IS_subspace,
                         code=state.code)
        else:
            # Need to reshape the operator given
            new_shape = (d**n) * np.ones(2 * n_op, dtype=int)
            permut = np.argsort(apply_to)
            transpose_ord = np.zeros(2 * n_op, dtype=int)
            transpose_ord[:n_op] = permut
            transpose_ord[n_op:] = n_op * np.ones(n_op, dtype=int) + permut

            sorted_op = np.reshape(np.transpose(np.reshape(op,
                                                           new_shape,
                                                           order='F'),
                                                axes=transpose_ord),
                                   ((d**n)**n_op, (d**n)**n_op),
                                   order='F')
            sorted_apply_to = apply_to[permut]

            return left_multiply(state, sorted_apply_to, sorted_op)
    else:
        # op should be a list of Pauli operators, or
        out = state.copy()
        for i in range(len(apply_to)):
            if op[i] == 'X':  # Sigma_X
                out = qubit.left_multiply(out, [n * apply_to[i]], ['X'])
            elif op[i] == 'Y':  # Sigma_Y
                out = qubit.left_multiply(
                    out, [n * apply_to[i], n * apply_to[i] + 1], ['Y', 'Z'])
            elif op[i] == 'Z':  # Sigma_Z
                out = qubit.left_multiply(
                    out, [n * apply_to[i], n * apply_to[i] + 1], ['Z', 'Z'])
        return State(out,
                     is_ket=state.is_ket,
                     IS_subspace=state.IS_subspace,
                     code=state.code)
Esempio n. 2
0
def right_multiply(state: State, apply_to: Union[int, list], op):
    """
    Apply a multi-qubit operator on several qubits (indexed in apply_to) of the input codes.
    :param state: input wavefunction or density matrix
    :type state: np.ndarray
    :param apply_to: zero-based indices of qudit locations to apply the operator
    :type apply_to: list of int
    :param op: Operator to act with.
    :type op: np.ndarray (2-dimensional)
    """
    if isinstance(apply_to, int):
        apply_to = [apply_to]
    if not isinstance(op, list):
        op = [op]
    # Type handling: to determine if Pauli, check if a list of strings
    pauli = False
    if isinstance(op, list):
        if all(isinstance(elem, str) for elem in op):
            pauli = True
        else:
            op = tools.tensor_product(op)
    if state.is_ket:
        print(
            'Warning: right multiply functionality currently applies the operator and daggers the s.'
        )
    n_op = len(apply_to)
    if not pauli:
        if tools.is_sorted(apply_to):
            # generate necessary shapes
            preshape = (d**n) * np.ones((2, n_op), dtype=int)
            preshape[0, 0] = int(state.dimension /
                                 ((d**n)**(1 + apply_to[n_op - 1])))
            if n_op > 1:
                preshape[0, 1:] = np.flip((d**n)**np.diff(apply_to)) / (d**n)

            shape3 = np.zeros(2 * n_op + 2, dtype=int)
            shape3[0] = state.dimension
            shape3[1:-1] = np.reshape(preshape, (2 * n_op), order='F')
            shape3[-1] = -1

            shape4 = np.zeros(2 * n_op + 2, dtype=int)
            shape4[0] = state.dimension
            shape4[1:n_op + 1] = preshape[0]
            shape4[n_op + 1] = -1
            shape4[n_op + 2:] = preshape[1]

            order3 = np.zeros(2 * n_op + 2, dtype=int)
            order3[0] = 0
            order3[1:n_op + 2] = 2 * np.arange(n_op + 1) + np.ones(n_op + 1)
            order3[n_op + 2:] = 2 * np.arange(1, n_op + 1)

            order4 = np.zeros(2 * n_op + 2, dtype=int)
            order4[0] = 0
            order4[1] = 1
            order4[2:] = np.flip(np.arange(2, 2 * n_op + 2).reshape((2, -1),
                                                                    order='C'),
                                 axis=0).reshape((-1), order='F')

            # right multiply
            out = state.reshape(shape3, order='F').transpose(order3)
            out = np.dot(out.reshape((-1, (d**n)**n_op), order='F'),
                         op.conj().T)
            out = out.reshape(shape4, order='F').transpose(order4)
            out = out.reshape(state.shape, order='F')
            return State(out,
                         is_ket=state.is_ket,
                         IS_subspace=state.IS_subspace,
                         code=state.code)
        else:
            new_shape = 2 * np.ones(2 * n_op, dtype=int)
            permut = np.argsort(apply_to)
            transpose_ord = np.zeros(2 * n_op, dtype=int)
            transpose_ord[:n_op] = permut
            transpose_ord[n_op:] = n_op * np.ones(n_op, dtype=int) + permut

            sorted_op = np.reshape(np.transpose(np.reshape(op,
                                                           new_shape,
                                                           order='F'),
                                                axes=transpose_ord),
                                   ((d**n)**n_op, (d**n)**n_op),
                                   order='F')
            sorted_apply_to = apply_to[permut]

            return right_multiply(state, sorted_apply_to, sorted_op)
    else:
        out = state.copy()
        for i in range(len(apply_to)):
            # Note index start from the right (sN,...,s3,s2,s1)
            if op[i] == 'X':  # Sigma_X
                out = qubit.left_multiply(out, [n * apply_to[i]], ['X'])
            elif op[i] == 'Y':  # Sigma_Y
                out = qubit.left_multiply(
                    out, [n * apply_to[i], n * apply_to[i] + 1], ['Y', 'Z'])
            elif op[i] == 'Z':  # Sigma_Z
                out = qubit.left_multiply(
                    out, [n * apply_to[i], n * apply_to[i] + 1], ['Z', 'Z'])
        return State(out,
                     is_ket=state.is_ket,
                     IS_subspace=state.IS_subspace,
                     code=state.code)