Beispiel #1
0
    def test_single_qubit_operation(self):
        N = 6
        # Test single qubit operation
        psi0 = State(np.zeros((rydberg.d**N, 1)), code=rydberg)
        psi0[0][0] = 1
        # Apply sigma_y on the second qubit to get 1j|020000>
        psi0 = rydberg.multiply(psi0, [1], rydberg.Y)
        self.assertTrue(psi0[(rydberg.d - 1) * rydberg.d**(N - 2), 0] == 1j)

        # Apply sigma_z on the second qubit, codes is -1j|010000>
        psi0 = rydberg.multiply(psi0, [1], rydberg.Z)
        self.assertTrue(psi0[(rydberg.d - 1) * rydberg.d**(N - 2), 0] == -1j)

        # Apply sigma_x on qubits
        psi0 = rydberg.multiply(psi0, [0, 2, 3, 4, 5],
                                tools.tensor_product([rydberg.X] * (N - 1)))

        # Vector is still normalized
        self.assertTrue(np.vdot(psi0, psi0) == 1)

        # Should be -1j|111111>
        self.assertTrue(psi0[-1, 0] == -1j)

        # Test rydberg operations with density matrices
        psi1 = np.array([
            tools.tensor_product([np.array([1, 0, 1]),
                                  np.array([1, 0, 0])]) / 2**(1 / 2)
        ]).T
        psi1 = State(tools.outer_product(psi1, psi1), code=rydberg)

        # Apply sigma_Y to first qubit
        psi2 = np.array([np.kron([1, 0, -1], [1, 0, 0]) * -1j / 2**(1 / 2)]).T
        rho2 = tools.outer_product(psi2, psi2)

        psi1 = rydberg.multiply(psi1, [0], ['Y'])
        self.assertTrue(np.linalg.norm(psi1 - rho2) <= 1e-10)

        psi0 = np.array([
            tools.tensor_product([np.array([1, 0, 1]),
                                  np.array([1, 0, 0])]) / 2**(1 / 2)
        ]).T
        psi0 = State(tools.outer_product(psi0, psi0), code=rydberg)
        psi1 = State(psi0.copy(), code=rydberg)

        # Apply sigma_Y to first qubit
        psi2 = np.array([np.kron([1, 0, -1], [1, 0, 0]) * -1j / 2**(1 / 2)]).T
        psi2 = tools.outer_product(psi2, psi2)

        # Apply single qubit operation to dmatrix
        psi0 = rydberg.multiply(psi0, [0], rydberg.Y)
        self.assertTrue(np.linalg.norm(psi0 - psi2) <= 1e-10)

        # Test on ket
        psi1 = rydberg.multiply(psi1, [0], rydberg.Y)
        self.assertTrue(np.linalg.norm(psi1 - psi2) <= 1e-10)
    def test_multi_qubit(self):
        n = 5
        psi0 = State(
            tools.tensor_product([jordan_farhi_shor.logical_basis[0]] * n))
        psi1 = psi0.copy()
        op = tools.tensor_product(
            [jordan_farhi_shor.X, jordan_farhi_shor.Y, jordan_farhi_shor.Z])
        psi0 = jordan_farhi_shor.multiply(psi0, [1, 3, 4], op)
        psi1 = jordan_farhi_shor.multiply(psi1, [1, 3, 4], ['X', 'Y', 'Z'])

        self.assertTrue(np.allclose(psi0, psi1))
Beispiel #3
0
def rotation(state: State,
             apply_to: Union[int, list],
             angle: float,
             op,
             is_involutary=False,
             is_idempotent=False):
    """
    Apply a single qubit rotation :math:`e^{-i \\alpha A}` to the input ``codes``.

    :param apply_to:
    :param is_idempotent:
    :param is_involutary:
    :param state: input wavefunction or density matrix
    :type state: np.ndarray
    :param angle: The angle :math:`\\alpha`` to rotate by.
    :type angle: float
    :param op: Operator to act with.
    :type op: np.ndarray
    """
    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 pauli:
        # Construct operator to use
        temp = []
        for i in range(len(op)):
            if op[i] == 'X':
                temp.append(X)
            elif op[i] == 'Y':
                temp.append(Y)
            elif op[i] == 'Z':
                temp.append(Z)
        temp = tools.tensor_product(temp)
        temp = np.cos(angle) * np.identity(
            temp.shape[0]) - temp * 1j * np.sin(angle)
        return multiply(state, apply_to, temp)
    else:
        if is_involutary:
            op = np.cos(angle) * np.identity(
                op.shape[0]) - op * 1j * np.sin(angle)
            return multiply(state, apply_to, op)
        elif is_idempotent:
            op = (np.exp(-1j * angle) - 1) * op + np.identity(op.shape[0])
            return multiply(state, apply_to, op)
        else:
            return multiply(state, apply_to, expm(-1j * angle * op))
Beispiel #4
0
    def test_multi_qubit(self):
        N = 5
        psi0 = State(
            tools.tensor_product([two_qubit_code.logical_basis[0]] * N))
        psi1 = psi0.copy()
        op = tools.tensor_product(
            [two_qubit_code.X, two_qubit_code.Y, two_qubit_code.Z])
        psi0 = two_qubit_code.multiply(psi0, [1, 3, 4], op)
        psi1 = two_qubit_code.multiply(psi1, [1, 3, 4], ['X', 'Y', 'Z'])

        self.assertTrue(np.allclose(psi0, psi1))
Beispiel #5
0
    def test_single_qubit_multiply_pauli(self):
        # Tests single qubit operation given a pauli index
        N = 6
        # Initialize in |000000>
        psi0 = State(np.zeros((2 ** N, 1)))
        psi0[0,0] = 1
        # Apply sigma_y on the second qubit to get 1j|010000>
        # Check Typing
        psi0 = qubit.multiply(psi0, 1, 'Y')
        self.assertTrue(psi0[2 ** (N - 2), 0] == 1j)

        # Apply sigma_z on the second qubit, codes is -1j|010000>
        psi0 = qubit.multiply(psi0, [1], 'Z')
        self.assertTrue(psi0[2 ** (N - 2), 0] == -1j)

        # Apply sigma_x on all qubits but 1
        psi0 = qubit.multiply(psi0, [0, 2, 3, 4, 5], ['X'] * (N - 1))

        # Vector is still normalized
        self.assertTrue(np.vdot(psi0, psi0) == 1)

        # Should be -1j|111111>
        self.assertTrue(psi0[-1, 0] == -1j)

        # Density matrix test
        psi1 = np.array([tools.tensor_product([np.array([1, 1]), np.array([1, 0])]) / 2 ** (1 / 2)]).T
        psi1 = State(tools.outer_product(psi1, psi1))

        # Apply sigma_Y to first qubit
        psi2 = np.array([np.kron([1j, -1j], [1, 0]) / 2 ** (1 / 2)]).T
        rho2 = tools.outer_product(psi2, psi2)

        psi1 = qubit.multiply(psi1, [0], 'Y')
        self.assertTrue(np.linalg.norm(psi1 - rho2) <= 1e-10)
Beispiel #6
0
def cost_function(G, penalty):
    global N
    sigma_plus = np.array([1, 0])
    rr = np.array([[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]])
    C = np.zeros([2**N])
    myeye = lambda n: np.ones(np.asarray(sigma_plus.shape[0])**n)

    for i, j in G.edges:
        temp = tools.tensor_product([rr, tools.identity(N - 2)])
        temp = np.reshape(temp, 2 * np.ones(2 * N, dtype=int))
        temp = np.moveaxis(temp, [0, 1, N, N + 1], [i, j, N + i, N + j])
        temp = np.reshape(temp, (2**N, 2**N))
        C = C + np.diagonal(temp) * penalty * -1
    for c in G.nodes:
        C = C + tools.tensor_product([myeye(c), sigma_plus, myeye(N - c - 1)])
    return C
Beispiel #7
0
    def test_single_qubit(self):
        psi0 = State(
            tools.tensor_product([
                three_qubit_code.logical_basis[0],
                three_qubit_code.logical_basis[0]
            ]))
        psi1 = psi0.copy()
        # Density matrix test
        psi2 = State(tools.outer_product(psi1, psi1))
        psi0 = three_qubit_code.multiply(psi0, [1], ['Y'])
        # Test non-pauli operation
        psi1 = three_qubit_code.multiply(psi1, [1], three_qubit_code.Y)
        psi2 = three_qubit_code.multiply(psi2, [1], three_qubit_code.Y)
        res = 1j * tools.tensor_product([
            three_qubit_code.logical_basis[0],
            three_qubit_code.logical_basis[1]
        ])
        # Should get out 1j|0L>|1L>
        self.assertTrue(np.allclose(psi0, res))
        self.assertTrue(np.allclose(psi1, res))
        self.assertTrue(np.allclose(psi2, tools.outer_product(res, res)))

        self.assertTrue(
            np.allclose(
                three_qubit_code.multiply(psi0, [1], ['Z']),
                -1j * tools.tensor_product([
                    three_qubit_code.logical_basis[0],
                    three_qubit_code.logical_basis[1]
                ])))
        psi0 = three_qubit_code.multiply(psi0, [0], ['X'])
        # Should get out -1j|1L>|1L>
        self.assertTrue(
            np.allclose(
                psi0, 1j * tools.tensor_product([
                    three_qubit_code.logical_basis[1],
                    three_qubit_code.logical_basis[1]
                ])))
        # Rotate all qubits
        for i in range(2):
            psi0 = three_qubit_code.rotation(psi0, [i], np.pi / 2,
                                             three_qubit_code.X)
        self.assertTrue(
            np.allclose(
                psi0, -1j * tools.tensor_product([
                    three_qubit_code.logical_basis[0],
                    three_qubit_code.logical_basis[0]
                ])))
 def __init__(self, graph: nx.Graph, IS_penalty=1, code=None):
     self.graph = graph
     if code is None:
         code = qubit
     self.code = code
     self.N = self.graph.number_of_nodes()
     self.IS_penalty = IS_penalty
     # Construct jump operators for edges and nodes
     # node =  (np.identity(self.code.d * self.code.n) - self.code.Z) / 2
     # edge = self.IS_penalty * tools.tensor_product([self.code.U, self.code.U])
     node = self.code.X @ (np.identity(self.code.d * self.code.n) -
                           self.code.Z) / 2
     edge = self.IS_penalty * (tools.tensor_product([
         self.code.X, np.identity(self.code.d * self.code.n)
     ]) + tools.tensor_product([
         np.identity(self.code.d * self.code.n), self.code.X
     ])) @ tools.tensor_product([self.code.U, self.code.U]) / 2
     self.jump_operators = [node, edge]
Beispiel #9
0
 def test_multi_qubit_operation(self):
     N = 6
     psi0 = State(np.zeros((rydberg.d**N, 1)), code=rydberg)
     psi0[0, 0] = 1
     psi1 = psi0.copy()
     op = tools.tensor_product([rydberg.X, rydberg.Y, rydberg.Z])
     psi0 = rydberg.multiply(psi0, [1, 3, 4], op)
     psi1 = rydberg.multiply(psi1, [1], 'X')
     psi1 = rydberg.multiply(psi1, [3], 'Y')
     psi1 = rydberg.multiply(psi1, [4], 'Z')
     self.assertTrue(np.allclose(psi0, psi1))
Beispiel #10
0
def IS_projector(G):
    global N
    rr = np.array([[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]])
    C = np.identity(2**N)
    for i, j in G.edges:
        temp = tools.tensor_product([rr, tools.identity(N - 2)])
        temp = np.reshape(temp, 2 * np.ones(2 * N, dtype=int))
        temp = np.moveaxis(temp, [0, 1, N, N + 1], [i, j, N + i, N + j])
        temp = np.reshape(temp, (2**N, 2**N))
        C = C @ (np.identity(2**N) - temp)
    return C
Beispiel #11
0
 def test_multi_qubit_pauli(self):
     N = 6
     psi0 = np.zeros((2 ** N, 1), dtype=np.complex128)
     psi0[0, 0] = 1
     psi1 = State(psi0)
     psi2 = State(psi0)
     psi3 = State(psi0)
     psi4 = State(psi0)
     psi0 = State(psi0)
     psi0 = qubit.multiply(psi0, [1, 3, 4], ['X', 'Y', 'Z'])
     psi2 = qubit.multiply(psi2, [4, 1, 3], ['Z', 'X', 'Y'])
     psi3 = qubit.multiply(psi3, [1, 3, 4], tools.tensor_product([qubit.X, qubit.Y, qubit.Z]))
     psi4 = qubit.multiply(psi4, [4, 1, 3], tools.tensor_product([qubit.Z, qubit.X, qubit.Y]))
     psi1 = qubit.multiply(psi1, [1], 'X')
     psi1 = qubit.multiply(psi1, [3], 'Y')
     psi1 = qubit.multiply(psi1, [4], 'Z')
     self.assertTrue(np.allclose(psi0, psi1))
     self.assertTrue(np.allclose(psi1, psi2))
     self.assertTrue(np.allclose(psi2, psi3))
     self.assertTrue(np.allclose(psi3, psi4))
Beispiel #12
0
 def test_multi_qubit_multiply(self):
     N = 6
     psi0 = np.zeros((2 ** N, 1), dtype=np.complex128)
     psi0[0, 0] = 1
     psi1 = State(psi0)
     psi0 = State(psi0)
     op = tools.tensor_product([qubit.X, qubit.Y, qubit.Z])
     psi0 = qubit.multiply(psi0, [1, 3, 4], op)
     psi1 = qubit.multiply(psi1, [1], 'X')
     psi1 = qubit.multiply(psi1, [3], 'Y')
     psi1 = qubit.multiply(psi1, [4], 'Z')
     self.assertTrue(np.allclose(psi0, psi1))
def ham_term(*argv):
    def decode(code):
        if code == 'i':
            return np.identity(2)
        if code == 'x':
            return tools.X()
        if code == 'y':
            return tools.Y()
        if code == 'z':
            return tools.Z()

    return tools.tensor_product([decode(arg) for arg in argv])
Beispiel #14
0
 def adiabatic_hamiltonian(t, tf):
     graph, mis = line_graph(n=n)
     ham = np.zeros((2**n, 2**n))
     coefficients = adiabatic_schedule(t, tf)
     rydberg_energy = 50
     rydberg_hamiltonian = hamiltonian.HamiltonianMIS(graph,
                                                      energy=rydberg_energy,
                                                      code=qubit)
     # TODO: generalize this!
     if n == 3:
         ham = ham + coefficients[1] * tools.tensor_product(
             [qubit.Z, np.identity(2),
              np.identity(2)])
         ham = ham + coefficients[1] * tools.tensor_product(
             [np.identity(2), qubit.Z,
              np.identity(2)])
         ham = ham + coefficients[1] * tools.tensor_product(
             [np.identity(2), np.identity(2), qubit.Z])
         ham = ham + coefficients[0] * tools.tensor_product(
             [qubit.X, np.identity(2),
              np.identity(2)])
         ham = ham + coefficients[0] * tools.tensor_product(
             [np.identity(2), qubit.X,
              np.identity(2)])
         ham = ham + coefficients[0] * tools.tensor_product(
             [np.identity(2), np.identity(2), qubit.X])
         ham = ham + np.diag(rydberg_hamiltonian.hamiltonian.T[0])
     elif n == 2:
         ham = ham + coefficients[1] * tools.tensor_product(
             [qubit.Z, np.identity(2)])
         ham = ham + coefficients[1] * tools.tensor_product(
             [np.identity(2), qubit.Z])
         ham = ham + coefficients[0] * tools.tensor_product(
             [qubit.X, np.identity(2)])
         ham = ham + coefficients[0] * tools.tensor_product(
             [np.identity(2), qubit.X])
         ham = ham + np.diag(rydberg_hamiltonian.hamiltonian.T[0])
     return np.linalg.eig(ham)
Beispiel #15
0
 def test_multi_qubit_pauli(self):
     N = 6
     psi0 = State(np.zeros((rydberg.d**N, 1)), code=rydberg)
     psi0[0, 0] = 1
     psi1 = psi0.copy()
     psi2 = psi0.copy()
     psi3 = psi0.copy()
     psi4 = psi0.copy()
     psi0 = rydberg.multiply(psi0, [1, 3, 4], ['X', 'Y', 'Z'])
     psi1 = rydberg.multiply(psi1, [1], 'X')
     psi1 = rydberg.multiply(psi1, [3], 'Y')
     psi1 = rydberg.multiply(psi1, [4], 'Z')
     psi2 = rydberg.multiply(psi2, [4, 1, 3], ['Z', 'X', 'Y'])
     psi3 = rydberg.multiply(
         psi3, [1, 3, 4],
         tools.tensor_product([rydberg.X, rydberg.Y, rydberg.Z]))
     psi4 = rydberg.multiply(
         psi4, [4, 1, 3],
         tools.tensor_product([rydberg.Z, rydberg.X, rydberg.Y]))
     self.assertTrue(np.allclose(psi0, psi1))
     self.assertTrue(np.allclose(psi1, psi2))
     self.assertTrue(np.allclose(psi2, psi3))
     self.assertTrue(np.allclose(psi3, psi4))
Beispiel #16
0
def 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 not state.is_ket:
        if pauli:
            out = state.copy()
            for i in range(len(apply_to)):
                # Note index start from the right (sN,...,s3,s2,s1)
                # 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], n * apply_to[i] + 2],
                        ['Y', 'Y'])
                elif op[i] == 'Y':  # Sigma_Y
                    out = -1 * qubit.left_multiply(
                        out, [n * apply_to[i] + 1, n * apply_to[i] + 2],
                        ['X', 'X'])
                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)
        else:
            return right_multiply(left_multiply(state, apply_to, op), apply_to,
                                  op)
    else:
        return left_multiply(state, apply_to, op)
Beispiel #17
0
def 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 not state.is_ket:
        if pauli:
            out = state.copy()
            for i in range(len(apply_to)):
                ind = d ** apply_to[i]
                out = out.reshape((-1, d, d ** (state.number_physical_qudits - 1), d, ind), order='F')
                if op[i] == 'X':  # Sigma_X
                    out = np.flip(out, axis=(1, 3))
                elif op[i] == 'Y':  # Sigma_Y
                    out = np.flip(out, axis=(1, 3))
                    out[:, d - 1, :, 0, :] = -out[:, d - 1, :, 0, :]
                    out[:, 0, :, d - 1, :] = -out[:, 0, :, d - 1, :]
                elif op[i] == 'Z':  # Sigma_Z
                    out[:, d - 1, :, 0, :] = -out[:, d - 1, :, 0, :]
                    out[:, 0, :, d - 1, :] = -out[:, 0, :, d - 1, :]

            out = out.reshape(state.shape, order='F')

            return State(out, is_ket=state.is_ket, IS_subspace=state.IS_subspace, code=state.code)
        else:
            return right_multiply(left_multiply(state, apply_to, op), apply_to, op)
    else:
        return left_multiply(state, apply_to, op)
Beispiel #18
0
    def __init__(self, graph: nx.Graph, rate):
        super().__init__(jump_operators=[
            np.array([[0, 0, 0, 0], [1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]),
            np.array([[0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 0], [0, 0, 0, 0]]),
            np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 0]])
        ],
                         weights=rate)
        # Construct the right jump_operators operators
        self.code = qubit
        self.graph = graph
        self.N = self.graph.number_of_nodes()
        jump_operators = []

        for (i, j) in graph.edges:
            for p in range(len(self.jump_operators)):
                temp = tools.tensor_product(
                    [self.jump_operators[p],
                     tools.identity(self.N - 2)])
                temp = np.reshape(temp, 2 * np.ones(2 * self.N, dtype=int))
                temp = np.moveaxis(temp, [0, 1, self.N, self.N + 1],
                                   [i, j, self.N + i, self.N + j])
                temp = np.reshape(temp, (2**self.N, 2**self.N))
                jump_operators.append(temp)
        self.jump_operators = jump_operators
Beispiel #19
0
import numpy as np
from qsim.codes import qubit
from qsim.codes.quantum_state import State
from qsim import tools
import time
from scipy.linalg import expm
from scipy.sparse.linalg import expm_multiply
from scipy.sparse import csr_matrix

n = 10
print('Timer test with', n, 'qubits')

Hb = np.zeros((2**n, 2**n), dtype=int)
for i in range(n):
    Hb = Hb + tools.tensor_product(
        [tools.identity(i), qubit.X,
         tools.identity(n - i - 1)])

# Make sparse matrix for Hb
Hb_sparse = csr_matrix(Hb)
psi0 = State(np.zeros((2**n, 1)))
psi0[-1, -1] = 1
t0 = time.perf_counter()
res = expm_multiply(Hb, psi0)
t1 = time.perf_counter()
print('scipy expm_multiply with non-sparse matrix: ', t1 - t0)
res = expm(Hb) @ psi0
t2 = time.perf_counter()
print('numpy expm with non-spares matrix: ', t2 - t1)
res = expm_multiply(Hb_sparse, psi0)
t3 = time.perf_counter()
Beispiel #20
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)
Beispiel #21
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)
Beispiel #22
0
import numpy as np
from qsim import tools
from . import qubit
from scipy.linalg import expm
from qsim.codes.quantum_state import State
from typing import Union

__all__ = ['multiply', 'right_multiply', 'left_multiply', 'rotation']

logical_code = True

X = tools.tensor_product([tools.X(), tools.identity()])
Y = tools.tensor_product([tools.Y(), tools.Z()])
Z = tools.tensor_product([tools.Z(), tools.Z()])
n = 2
d = 2
logical_basis = np.array([[[1], [0], [0], [1]], [[0], [1], [1], [0]]]).astype(
    np.complex128) / np.sqrt(2)
Q = 1 / 2 * (np.identity(d**n) + Z)
P = 1 / 2 * (np.identity(d**n) - Z)

code_space_projector = tools.outer_product(
    logical_basis[0], logical_basis[0]) + tools.outer_product(
        logical_basis[1], logical_basis[1])


def rotation(state: State,
             apply_to: Union[int, list],
             angle: float,
             op,
             is_involutary=False,
 def ground_overlap(state):
     g = np.array([[0, 0], [0, 1]])
     op = tools.tensor_product([g] * graph.number_of_nodes())
     return np.real(np.trace(op @ state))
Beispiel #24
0
import numpy as np
from qsim import tools
from . import qubit
from scipy.linalg import expm
from qsim.codes.quantum_state import State
from typing import Union

"""
:class:`ThreeQubitCode` is an error correcting code which can correct bit flip (X-type) errors.
"""
__all__ = ['multiply', 'right_multiply', 'left_multiply', 'rotation']

logical_code = True

X = tools.tensor_product([tools.X(), tools.X(), tools.X()])
Y = -1 * tools.tensor_product([tools.Y(), tools.Y(), tools.Y()])
Z = tools.tensor_product([tools.Z(), tools.Z(), tools.Z()])
n = 3
d = 2
logical_basis = np.array([[[1], [0], [0], [0], [0], [0], [0], [0]],
                          [[0], [0], [0], [0], [0], [0], [0], [1]]], dtype=np.complex128)
Q = 1/2*(np.identity(d**n)+Z)
P = 1/2*(np.identity(d**n)-Z)
code_space_projector = tools.outer_product(logical_basis[0], logical_basis[0]) + tools.outer_product(logical_basis[1],
                                                                                                     logical_basis[1])
stabilizers = np.array(
    [tools.tensor_product([tools.Z(2), tools.identity()]), tools.tensor_product([tools.identity(), tools.Z(2)])])


def rotation(state: State, apply_to: Union[int, list], angle: float, op, is_involutary=False, is_idempotent=False):
    """