Esempio n. 1
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], 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)
Esempio n. 2
0
    def test_hamiltonian_driver(self):
        N = 6

        hl_qubit = hamiltonian.HamiltonianDriver(
            graph=tools_test.sample_graph())

        psi0 = State(np.zeros((2**N, 1)))
        psi0[0, 0] = 1
        psi1 = State(tools.outer_product(psi0, psi0))

        # Evolve by e^{-i (\pi/2) \sum_i X_i}
        psi0 = hl_qubit.evolve(psi0, np.pi / 2)

        # Should get (-1j)^N |111111>
        self.assertTrue(np.vdot(psi0, psi0) == 1)
        self.assertTrue(psi0[-1, 0] == (-1j)**N)

        # Evolve by e^{-i (\pi/2) \sum_i X_i}
        psi1 = hl_qubit.evolve(psi1, np.pi / 2)

        # Should get (-1j)^N |111111>
        self.assertTrue(tools.is_valid_state(psi1))
        self.assertAlmostEqual(psi1[-1, -1], 1)

        psi0 = State(np.zeros((2**N, 1)))
        psi0[0, 0] = 1
        psi0 = hl_qubit.left_multiply(psi0)
        psi1 = np.zeros((2**N, 1), dtype=np.complex128)
        for i in range(N):
            psi1[2**i, 0] = 1
        self.assertTrue(np.allclose(psi0, psi1))
        psi2 = State(np.zeros((2**N, 1)))
        psi2[0, 0] = 1
        psi2 = hl_qubit.hamiltonian @ psi2
        self.assertTrue(np.allclose(psi0, psi2))

        N = 3
        hl = hamiltonian.HamiltonianDriver(transition=(0, 1), code=rydberg)
        psi0 = State(np.zeros((rydberg.d**N, 1)), code=rydberg)
        psi0[5, 0] = 1
        psi1 = State(tools.outer_product(psi0, psi0), code=rydberg)
        psi0 = hl.left_multiply(psi0)

        self.assertTrue(psi0[2, 0] == 1)
        self.assertTrue(psi0[14, 0] == 1)
        psi1 = hl.left_multiply(psi1)
        self.assertTrue(psi1[2, 5] == 1)
        self.assertTrue(psi1[14, 5] == 1)

        psi0 = State(np.zeros((rydberg.d**N, 1)), code=rydberg)
        psi0[5, 0] = 1
        psi0 = hl.evolve(psi0, np.pi / 2)
        self.assertTrue(np.isclose(psi0[11, 0], -1))

        # IS subspace
        hl = hamiltonian.HamiltonianDriver(transition=(0, 2),
                                           code=rydberg,
                                           IS_subspace=True,
                                           graph=line_graph(2))
        psi0 = State(np.zeros((8, 1)), code=rydberg)
        psi0[-1, 0] = 1
        psi0 = State(tools.outer_product(psi0, psi0), code=rydberg)
        self.assertTrue(tools.is_hermitian(hl.evolve(psi0, 1)))
Esempio n. 3
0
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()
print('scipy expm_multiply with sparse matrix:', t3 - t2)
for j in range(n):
    # Do single qubit rotations
    psi0 = qubit.rotation(psi0, 1, j, qubit.X, is_idempotent=True)
print('qsim qubit operations:', time.perf_counter() - t3)
Esempio n. 4
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], 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)
Esempio n. 5
0
 def f(flattened):
     s = State(flattened.reshape(state_shape))
     res = self.evolution_generator(s)
     return res.reshape(flattened.shape)
Esempio n. 6
0
hb = hamiltonian.HamiltonianDriver()
hamiltonians = [hc, hb]
ring_hamiltonians = [hc_ring, hb]

sim = qaoa.SimulateQAOA(g, cost_hamiltonian=hc, hamiltonian=hamiltonians)
sim_ring = qaoa.SimulateQAOA(ring,
                             cost_hamiltonian=hc_ring,
                             hamiltonian=ring_hamiltonians)
sim_ket = qaoa.SimulateQAOA(g, cost_hamiltonian=hc, hamiltonian=hamiltonians)
sim_noisy = qaoa.SimulateQAOA(g,
                              cost_hamiltonian=hc,
                              hamiltonian=hamiltonians,
                              noise_model='channel')

# Initialize in |000000>
psi0 = State(equal_superposition(N))
rho0 = State(outer_product(psi0, psi0))

noises = [quantum_channels.DepolarizingChannel(rates=(.001, ))]
sim_noisy.noise = noises * 2


class TestSimulateQAOA(unittest.TestCase):
    def test_variational_grad(self):
        # Test that the calculated objective function and gradients are correct
        # p = 1
        sim_ket.hamiltonian = hamiltonians
        F, Fgrad = sim_ket.variational_grad(np.array([1, 0.5]),
                                            initial_state=psi0)
        self.assertTrue(np.abs(F - 5.066062984904652) <= 1e-5)
        self.assertTrue(
Esempio n. 7
0
def imperfect_blockade_performance():
    graph = line_graph(n=5, return_mis=False)
    phi = .02
    laser = hamiltonian.HamiltonianDriver(pauli='X',
                                          energies=(np.cos(phi) *
                                                    np.sin(phi), ),
                                          graph=graph)
    energy_shift_r = hamiltonian.HamiltonianEnergyShift(
        index=0, energies=(np.sin(phi)**2, ), graph=graph)
    energy_shift_g = hamiltonian.HamiltonianEnergyShift(
        index=1, energies=(np.cos(phi)**2, ), graph=graph)
    dissipation = EffectiveOperatorDissipation(omega_g=np.cos(phi),
                                               omega_r=np.sin(phi),
                                               rates=(1, ),
                                               graph=graph,
                                               IS_subspace=False)

    rydberg_hamiltonian = hamiltonian.HamiltonianMIS(graph,
                                                     IS_subspace=False,
                                                     code=qubit,
                                                     energies=(
                                                         0,
                                                         4,
                                                     ))
    rydberg_hamiltonian_cost_IS = hamiltonian.HamiltonianMIS(graph,
                                                             IS_subspace=True,
                                                             code=qubit)
    rydberg_hamiltonian_cost = hamiltonian.HamiltonianMIS(graph,
                                                          IS_subspace=False,
                                                          code=qubit,
                                                          energies=(
                                                              1,
                                                              -4,
                                                          ))

    eq = LindbladMasterEquation(hamiltonians=[
        laser, energy_shift_g, energy_shift_r, rydberg_hamiltonian
    ],
                                jump_operators=[dissipation])
    state = np.zeros(rydberg_hamiltonian_cost.hamiltonian.shape)
    state[graph.independent_sets[np.argmax(rydberg_hamiltonian_cost_IS.
                                           hamiltonian)][0],
          graph.independent_sets[np.argmax(rydberg_hamiltonian_cost_IS.
                                           hamiltonian)][0]] = 1
    state = State(state, is_ket=False, graph=graph, IS_subspace=False)
    ss = eq.steady_state(state, k=80, use_initial_guess=True)
    print(ss[1].shape)
    ss = ss[1][0]
    print(np.diagonal(ss), rydberg_hamiltonian_cost.hamiltonian)
    state = State(ss, is_ket=False, graph=graph, IS_subspace=False)
    print(np.around(ss, decimals=3),
          rydberg_hamiltonian_cost.optimum_overlap(state))
    layout = np.zeros((2, 2))
    layout[0, 0] = 1
    layout[1, 1] = 1
    layout[0, 1] = 1
    adjacent_energy = 4
    diag_energy = adjacent_energy / 8
    second_nearest_energy = adjacent_energy / 64
    for i in range(layout.shape[0] - 1):
        for j in range(layout.shape[1] - 1):
            if layout[i, j] == 1:
                # There is a spin here
                pass
    plt.scatter(depths, [i / (i + 1) for i in depths], label='maxcut')
    plt.scatter(depths, mis, label='mis with $n=$' + str(n))
    plt.plot(depths, mis)

    plt.legend()
    if show:
        plt.show()


if __name__ == "__main__":
    i, j = 4, 4
    graph = node_defect_torus(i, j)
    #simulation = adiabatic_simulation(graph, IS_subspace=True)
    #t0 = time.time()

    #res = simulation.performance_vs_total_time(np.arange(0, 0), metric='optimum_overlap',
    #                                           schedule=lambda t, tf: experiment_rydberg_MIS_schedule(t, tf, simulation,
    #                                                                                                  coefficients=[10,
    #                                                                                                                10]),
    #                                           plot=True, verbose=True, method='trotterize')

    #print('results: ', res, flush=True)
    #simulation = mis_qaoa(4, IS_subspace=True, method='basinhopping')
    simulation = adiabatic_simulation(graph, IS_subspace=False)
    res = simulation.performance_vs_total_time(np.arange(40, 95, 5), metric='optimum_overlap',
                                               initial_state=State(equal_superposition(i * j)),
                                               schedule=lambda t, tf: simulation.linear_schedule(t, tf,
                                                                                                 coefficients=[1, 1]),
                                               plot=True, verbose=True, method='trotterize')
    print('results: ', res, flush=True)
Esempio n. 9
0
def domain_wall_dissipation(n:list, dt=0.001, trials = 30):
    tf = 3
    times = np.arange(0, tf, dt)
    pump_rate = 10
    for i in n:
        # Ensure an odd number of nodes
        assert i % 2 == 1
        def psi0():
            """Generates the initial domain wall codes"""
            s = np.zeros((1, 2**i))
            middle = i//2-1
            arr = np.ones(i)
            for j in range(i):
                if j <= middle and j % 2 == 0:
                    arr[j] = 0
                elif j > middle+1 and j % 2 == 1:
                    arr[j] = 0
            s[0,tools.nary_to_int(arr)] = 1
            return s.T
        def mis_state():
            """Generates the initial domain wall codes"""
            s = np.zeros((1, 2**i))
            arr = np.ones(i)
            for j in range(i):
                if j % 2 == 0:
                    arr[j] = 0
            s[0, tools.nary_to_int(arr)] = 1
            return s.T
        def spin_hamiltonian():
            s = np.zeros((1, 2 ** i))
            for j in range(2**i):
                s[0,j] = i-np.sum(np.array([tools.int_to_nary(j)]))
            return s.T
        g = chain_graph(i)
        graph = Graph(g)
        psi0 =  psi0()#np.zeros((2**i, 1)) #
        #psi0[-1,0] = 1
        s = State(psi0, graph.n, is_ket=True)
        greedy = GreedyNoise(graph, rate = pump_rate)
        heisenberg = HamiltonianHeisenberg(graph, k=100)
        sw = StochasticWavefunction(hamiltonians=[heisenberg, greedy], jumps=[greedy])
        quantum_outputs = np.zeros((trials, np.arange(0, tf, dt).shape[0], psi0.shape[0]), dtype=np.complex128)
        classical_outputs = np.zeros((trials, np.arange(0, tf, dt).shape[0], graph.n), dtype=np.complex128)
        for k in range(trials):
            quantum_output = sw.run(s.state.copy(), 0, tf, dt)
            quantum_outputs[k,...] = np.squeeze(quantum_output, axis=-1)
            classical_output = graph.run(0, tf, dt, rates=[1, pump_rate], config=np.array([1,0,0,1,0]))
            classical_outputs[k,...] = np.squeeze(classical_output, axis=-1)
        mis_size = i//2+1
        # Assume the amount in the ground codes is very small
        quantum_overlap_mis = np.abs(quantum_outputs@mis_state())**2
        quantum_spin = np.abs(quantum_outputs)**2 @ spin_hamiltonian()/mis_size
        classical_spin = np.sum(classical_outputs, axis=-1) / mis_size
        classical_overlap_mis = classical_spin.copy()
        classical_overlap_mis[classical_overlap_mis==1] =1
        classical_overlap_mis[classical_overlap_mis!=1] =0
        plt.plot(times, np.mean(np.squeeze(quantum_overlap_mis, axis=-1), axis=0), label='Quantum MIS overlap, n='+str(i))
        plt.plot(times, np.mean(np.squeeze(quantum_spin, axis=-1), axis=0), label='Quantum AR, n='+str(i))

        plt.plot(times, np.mean(classical_overlap_mis, axis=0), label='Classical MIS overlap, n='+str(i))
        plt.plot(times, np.mean(classical_spin, axis=0), label='Classical AR, n='+str(i))

    plt.legend(loc='upper left')
    plt.show()
Esempio n. 10
0
def dephasing_performance():
    # adiabatic = SimulateAdiabatic(hamiltonian=[laser], noise = [dephasing, dissipation], noise_model='continuous',
    #                              graph=graph, IS_subspace=True, cost_hamiltonian=rydberg_hamiltonian_cost)
    # def schedule(t, tf):
    #    phi = (tf-t)/tf*np.pi/2
    #    laser.omega_g = np.cos(phi)
    #    laser.omega_r = np.sin(phi)
    #    dissipation.omega_g = np.cos(phi)
    #    dissipation.omega_r = np.sin(phi)
    # adiabatic.performance_vs_total_time(np.arange(5, 100, 5), schedule=schedule, verbose=True, plot=True, method='odeint')
    dephasing_rates = 10.**(np.arange(-3, 0, .2))
    performance_3 = []
    performance_5 = []
    for j in [3, 5]:
        graph = line_graph(n=j)
        phi = .02
        laser = EffectiveOperatorHamiltonian(omega_g=np.cos(phi),
                                             omega_r=np.sin(phi),
                                             energies=(1, ),
                                             graph=graph)
        dissipation = EffectiveOperatorDissipation(omega_g=np.cos(phi),
                                                   omega_r=np.sin(phi),
                                                   rates=(1, ),
                                                   graph=graph)
        dephasing = lindblad_operators.LindbladPauliOperator(pauli='Z',
                                                             IS_subspace=True,
                                                             graph=graph,
                                                             rates=(.1, ))
        rydberg_hamiltonian_cost = hamiltonian.HamiltonianMIS(graph,
                                                              IS_subspace=True,
                                                              code=qubit)
        eq = LindbladMasterEquation(hamiltonians=[laser],
                                    jump_operators=[dissipation, dephasing])

        for i in range(len(dephasing_rates)):
            dissipation.rates = (1, )
            laser.energies = (1, )
            dephasing.rates = (dephasing_rates[i], )
            state = np.zeros(dissipation.nh_hamiltonian.shape)
            state[-1, -1] = 1
            state = State(state, is_ket=False, graph=graph, IS_subspace=True)
            ss = eq.steady_state(state)
            ss = ss[1][0]
            state = State(ss, is_ket=False, graph=graph, IS_subspace=True)
            print(rydberg_hamiltonian_cost.optimum_overlap(state))
            if j == 3:
                performance_3.append(
                    rydberg_hamiltonian_cost.optimum_overlap(state))
            else:
                performance_5.append(
                    rydberg_hamiltonian_cost.optimum_overlap(state))

    plt.scatter(dephasing_rates,
                performance_3,
                color='teal',
                label=r'$n=3$ line graph')
    plt.scatter(dephasing_rates,
                performance_5,
                color='purple',
                label=r'$n=5$ line graph')
    plt.ylabel(r'log(-log(optimum overlap))')
    plt.xlabel(r'$\log(\gamma\Omega^2/(\delta^2\Gamma_{\rm{dephasing}}))$')
    plt.legend()
    plt.show()
Esempio n. 11
0
 def matvec_disorder(x):
     if len(x.shape) == 2:
         return matvec_heisenberg(Heis, disorder, State(x))
     else:
         return matvec_heisenberg(Heis, disorder,
                                  State(np.expand_dims(x, axis=-1)))
Esempio n. 12
0
 def f(flattened):
     s = State(flattened.reshape(state_shape))
     res = P @ self.evolution_generator(P @ s @ P) @ P + Q @ self.evolution_generator(Q @ s @ P) @ P + \
           P @ self.evolution_generator(P @ s @ Q) @ Q
     return res.reshape(flattened.shape)
Esempio n. 13
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)
    """
    # Handle typing
    if isinstance(apply_to, int):
        apply_to = [apply_to]
    pauli = False
    if isinstance(op, list):
        if all(isinstance(elem, str) for elem in op):
            pauli = True
        else:
            op = tensor_product(op)
    n_op = len(apply_to)
    if not pauli:
        if is_sorted(apply_to):
            # Generate all shapes for left multiplication
            preshape = d * np.ones((2, n_op), dtype=int)
            preshape[1,
                     0] = int(state.dimension / (d**(1 + apply_to[n_op - 1])))
            if n_op > 1:
                preshape[1, 1:] = np.flip(d**np.diff(apply_to)) / 2

            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_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
            apply_to = np.asarray(apply_to, dtype=int)
            new_shape = d * 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] = (n_op - 1) * np.ones(
                n_op, dtype=int) - np.flip(permut, axis=0)
            transpose_ord[n_op:] = (2 * n_op - 1) * np.ones(
                n_op, dtype=int) - np.flip(permut, axis=0)

            sorted_op = np.reshape(np.transpose(np.reshape(op,
                                                           new_shape,
                                                           order='F'),
                                                axes=transpose_ord),
                                   (d**n_op, d**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
        out = state.copy()
        for i in range(len(apply_to)):
            ind = d**apply_to[i]
            if state.is_ket:
                # Note index start from the right (sN,...,s3,s2,s1)
                out = out.reshape((-1, d, ind), order='F')
                if op[i] == 'X':  # Sigma_X
                    out = np.flip(out, 1)
                elif op[i] == 'Y':  # Sigma_Y
                    out = np.flip(out, 1)
                    out[:, 0, :] = -1j * out[:, 0, :]
                    out[:, d - 1, :] = 1j * out[:, d - 1, :]
                elif op[i] == 'Z':  # Sigma_Z
                    out[:, d - 1, :] = -out[:, d - 1, :]

                out = out.reshape(state.shape, order='F')
            else:
                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, 1)
                elif op[i] == 'Y':  # Sigma_Y
                    out = np.flip(out, axis=1)
                    out[:, 0, :, :, :] = -1j * out[:, 0, :, :, :]
                    out[:, d - 1, :, :, :] = 1j * out[:, d - 1, :, :, :]
                elif op[i] == 'Z':  # Sigma_Z
                    out[:, d - 1, :, :, :] = -out[:, 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)
Esempio n. 14
0
    for i in range(layout.shape[0] - 1):
        for j in range(layout.shape[1] - 1):
            if layout[i, j] == 1:
                # There is a spin here
                pass


#imperfect_blockade_performance()


graph = line_graph(n=3, return_mis=False)
phi = np.pi/2
laser = EffectiveOperatorHamiltonian(omega_g=np.cos(phi), omega_r=np.sin(phi), graph=graph)
eq = SchrodingerEquation(hamiltonians=[laser])

state = State(np.ones((5, 1), dtype=np.complex128)/np.sqrt(5))
print(np.round(eq.eig(k='all')[1], decimals=3))

def performance_vs_alpha():
    # alpha = gamma * omega^2/(delta^2 T)
    graph = line_graph(n=3)
    gammas = np.array([1, 5, 10])
    times = np.array([1, 5, 10])
    deltas = np.array([20, 30])
    omegas = np.array([1, 5, 10])
    for (g, d, o) in zip(gammas, deltas, omegas):
        # Find the performance vs alpha
        laser = EffectiveOperatorHamiltonian(graph=graph)
        dissipation = EffectiveOperatorDissipation(graph=graph)
        rydberg_hamiltonian_cost = hamiltonian.HamiltonianMIS(graph, IS_subspace=True, code=qubit)
        adiabatic = SimulateAdiabatic(hamiltonian=[laser], noise = [dissipation], noise_model='continuous',
 def right_multiply(self, state: State):
     return State(state @ self.hamiltonian.T.conj(),
                  is_ket=state.is_ket,
                  IS_subspace=state.IS_subspace,
                  code=state.code,
                  graph=self.graph)
Esempio n. 16
0
where_nonzero = np.argwhere(SDP_output != 0)

SDP_output = np.zeros(2**8)
SDP_output[where_nonzero] = 1
SDP_output = SDP_output * np.random.uniform(-100, 100, size=len(SDP_output))
SDP_output = SDP_output / np.linalg.norm(SDP_output)

#SDP_output[23] = 1
np.set_printoptions(threshold=np.inf)
print(SDP_output)
#print(SDP_output)
#SDP_output1 = generate_SDP_output(4, 20)
#print(np.linalg.norm(SDP_output1-SDP_output2))
#print(len(SDP_output[np.nonzero(SDP_output)]))
#print(SDP_output[rows, cols])
SDP_output = State(SDP_output[np.newaxis, :].T)
graph = generate_SDP_graph(3, 1 / 3, visualize=False)
cost = hamiltonian.HamiltonianMaxCut(graph, cost_function=True)
driver = hamiltonian.HamiltonianDriver()
qa = qaoa.SimulateQAOA(graph=graph, hamiltonian=[], cost_hamiltonian=cost)

SDP_cf = cost.cost_function(SDP_output) / 8
print(SDP_output)
print(SDP_cf)
#plt.scatter(-1, cost.cost_function(SDP_output))
vanilla_cf = np.array([
    42.39230484541338, 48.26437904543421, 53.04760524625608,
    58.034293475214454, 61.43733741238711, 62.1439072862153
]) / 64
plt.scatter(np.arange(len(vanilla_cf)), 1 - vanilla_cf)
#plt.semilogy()
 def left_multiply(self, state: State):
     return State(self.energies[0] * self._csc_hamiltonian @ state,
                  is_ket=state.is_ket,
                  IS_subspace=state.IS_subspace,
                  code=state.code,
                  graph=self.graph)
Esempio n. 18
0
    def run_stochastic_wavefunction_solver(self,
                                           s,
                                           t0,
                                           tf,
                                           num=50,
                                           schedule=lambda t: None,
                                           times=None,
                                           full_output=True,
                                           method='trotterize',
                                           verbose=False,
                                           iterations=None):
        if iterations is None:
            iterations = 1
        # Compute probability that we have a jump
        # For the stochastic solver, we have to return a times dictionary
        assert s.is_ket
        # Save state properties
        is_ket = s.is_ket
        code = s.code
        IS_subspace = s.IS_subspace
        state_shape = s.shape
        graph = s.graph

        num_jumps = []
        jump_times = []
        jump_indices = []

        if times is None and (method == 'odeint' or method == 'trotterize'):
            times = np.linspace(0, 1, num=int(num)) * (tf - t0) + t0
        if not (method == 'odeint' or method == 'trotterize'):
            raise NotImplementedError

        schrodinger_equation = SchrodingerEquation(
            hamiltonians=self.hamiltonians + self.jump_operators)

        def f(t, state):
            if method == 'odeint':
                t, state = state, t
            if method != 'odeint':
                state = np.reshape(np.expand_dims(state, axis=0), state_shape)
            state = State(state,
                          is_ket=is_ket,
                          code=code,
                          IS_subspace=IS_subspace)
            return np.asarray(
                schrodinger_equation.evolution_generator(state)).flatten()

        assert len(times) > 1
        if full_output:
            outputs = np.zeros(
                (iterations, len(times), s.shape[0], s.shape[1]),
                dtype=np.complex128)
        else:
            outputs = np.zeros((iterations, s.shape[0], s.shape[1]),
                               dtype=np.complex128)
        dt = times[1] - times[0]
        for k in range(iterations):
            jump_time = []
            jump_indices_iter = []
            num_jump = 0
            out = s.copy()
            if verbose:
                print('Iteration', k)
            for (j, time) in zip(range(times.shape[0]), times):
                # Update energies
                schedule(time)
                for i in range(len(self.jump_operators)):
                    if i == 0:
                        jumped_states, jump_probabilities = self.jump_operators[
                            i].jump_rate(
                                out, list(range(out.number_physical_qudits)))
                        jump_probabilities = jump_probabilities * dt
                    elif i > 0:
                        js, jp = self.jump_operators[i].jump_rate(
                            out, list(range(out.number_physical_qudits)))
                        jump_probabilities = np.concatenate(
                            [jump_probabilities, jp * dt])
                        jumped_states = np.concatenate([jumped_states, js])
                if len(self.jump_operators) == 0:
                    jump_probability = 0
                else:
                    jump_probability = np.sum(jump_probabilities)
                if np.random.uniform() < jump_probability and len(
                        self.jump_operators) != 0:
                    # Then we should do a jump
                    num_jump += 1
                    jump_time.append(time)
                    if verbose:
                        print('Jumped with probability', jump_probability,
                              'at time', time)
                    jump_index = np.random.choice(
                        list(range(len(jump_probabilities))),
                        p=jump_probabilities / np.sum(jump_probabilities))
                    jump_indices_iter.append(jump_index)
                    out = State(jumped_states[jump_index, ...] *
                                np.sqrt(dt / jump_probabilities[jump_index]),
                                is_ket=is_ket,
                                code=code,
                                IS_subspace=IS_subspace,
                                graph=graph)
                    # Normalization factor
                else:
                    state_asarray = np.asarray(out)
                    if method == 'odeint':
                        z = odeintw(f,
                                    state_asarray, [0, dt],
                                    full_output=False)
                        out = State(z[-1],
                                    code=code,
                                    IS_subspace=IS_subspace,
                                    is_ket=is_ket,
                                    graph=graph)

                    else:
                        for hamiltonian in self.hamiltonians:
                            out = hamiltonian.evolve(out, dt)

                        for jump_operator in self.jump_operators:
                            if isinstance(jump_operator, LindbladJumpOperator):
                                # Non-hermitian evolve
                                out = jump_operator.nh_evolve(out, dt)
                            elif isinstance(jump_operator, QuantumChannel):
                                out = jump_operator.evolve(out, dt)
                        out = State(out,
                                    code=code,
                                    IS_subspace=IS_subspace,
                                    is_ket=is_ket,
                                    graph=graph)

                    # Normalize the output
                    out = out / np.linalg.norm(out)
                    # We don't do np.sqrt(1 - jump_probability) because it is only a first order expansion,
                    # and is often inaccurate. Things will quickly diverge if the state is not normalized
                if full_output:
                    outputs[k, j, ...] = out
            jump_times.append(jump_time)
            jump_indices.append(jump_indices_iter)
            num_jumps.append(num_jump)
            if not full_output:
                outputs[k, ...] = out

        return outputs, {
            't': times,
            'jump_times': jump_times,
            'num_jumps': num_jumps,
            'jump_indices': jump_indices
        }
Esempio n. 19
0
    def run(self, time, schedule, num=None, initial_state=None, full_output=True, method='RK45', verbose=False,
            iterations=None):
        if method == 'odeint' or method == 'trotterize' and num is None:
            num = self._num_from_time(time, method=method)

        if initial_state is None:
            # Begin with all qudits in the ground s
            initial_state = State(np.zeros((self.cost_hamiltonian.hamiltonian.shape[0], 1)), code=self.code,
                                  IS_subspace=self.IS_subspace, graph=self.graph)
            initial_state[-1, -1] = 1

        if self.noise_model is not None and self.noise_model != 'monte_carlo':
            initial_state = State(outer_product(initial_state, initial_state), IS_subspace=self.IS_subspace,
                                  code=self.code, graph=self.graph)

        if self.noise_model == 'continuous':
            # Initialize master equation
            if method == 'trotterize':
                master_equation = LindbladMasterEquation(hamiltonians=self.hamiltonian, jump_operators=self.noise)
                results, info = master_equation.run_trotterized_solver(initial_state, 0, time, num=num,
                                                                       schedule=lambda t: schedule(t, time),
                                                                       full_output=full_output, verbose=verbose)
            else:
                master_equation = LindbladMasterEquation(hamiltonians=self.hamiltonian, jump_operators=self.noise)
                results, info = master_equation.run_ode_solver(initial_state, 0, time, num=num,
                                                               schedule=lambda t: schedule(t, time), method=method,
                                                               full_output=full_output, verbose=verbose)
        elif self.noise_model is None:
            # Noise model is None
            # Initialize Schrodinger equation
            schrodinger_equation = SchrodingerEquation(hamiltonians=self.hamiltonian)
            if method == 'trotterize':
                results, info = schrodinger_equation.run_trotterized_solver(initial_state, 0, time, num=num,
                                                                            verbose=verbose, full_output=full_output,
                                                                            schedule=lambda t: schedule(t, time))
            else:
                results, info = schrodinger_equation.run_ode_solver(initial_state, 0, time, num=num, verbose=verbose,
                                                                    schedule=lambda t: schedule(t, time), method=method,
                                                                    full_output=full_output)

        else:
            assert self.noise_model == 'monte_carlo'
            # Initialize master equation
            master_equation = LindbladMasterEquation(hamiltonians=self.hamiltonian, jump_operators=self.noise)
            results, info = master_equation.run_stochastic_wavefunction_solver(initial_state, 0, time, num=num,
                                                                               full_output=full_output,
                                                                               schedule=lambda t: schedule(t, time),
                                                                               method=method, verbose=verbose,
                                                                               iterations=iterations)

        if len(results.shape) == 2:
            # The algorithm has output a single state
            out = [State(results, IS_subspace=self.IS_subspace, code=self.code, graph=self.graph)]
        elif len(results.shape) == 3:
            # The algorithm has output an array of states
            out = [State(res, IS_subspace=self.IS_subspace, code=self.code, graph=self.graph) for res in results]
        else:
            assert len(results.shape) == 4
            if self.noise_model != 'monte_carlo':
                raise Exception('Run output has more dimensions than expected')
            out = []
            for i in range(results.shape[0]):
                # For all iterations
                res = []
                for j in range(results.shape[1]):
                    # For all times
                    res.append(
                        State(results[i, j, ...], IS_subspace=self.IS_subspace, code=self.code, graph=self.graph))
                out.append(res)
        return out, info
Esempio n. 20
0
    def steady_state(self,
                     state: State,
                     k=6,
                     which='LR',
                     use_initial_guess=False,
                     plot=False,
                     tol=1e-8,
                     verbose=False):
        """Returns a list of the eigenvalues and the corresponding valid density matrix."""
        assert not state.is_ket
        state_shape = state.shape

        def f(flattened):
            s = State(flattened.reshape(state_shape))
            res = self.evolution_generator(s)
            return res.reshape(flattened.shape)

        state_flattened = state.flatten()
        lindbladian = LinearOperator(shape=(len(state_flattened),
                                            len(state_flattened)),
                                     dtype=np.complex128,
                                     matvec=f)
        if not use_initial_guess:
            v0 = None
        else:
            v0 = state_flattened
        try:
            eigvals, eigvecs = eigs(lindbladian, k=k, which=which, v0=v0)
        except ArpackNoConvergence as exception_info:
            eigvals = exception_info.eigenvalues
            eigvecs = exception_info.eigenvectors
        if eigvals.size != 0:
            # Do some basic reshaping to post process the results and select for only the steady states
            eigvecs = np.moveaxis(eigvecs, -1, 0)
            eigvecs = np.reshape(
                eigvecs, [eigvecs.shape[0], state_shape[0], state_shape[1]])
            steady_state_indices = np.argwhere(eigvals.real > -1 * tol).T[0]
            steady_state_eigvecs = eigvecs[steady_state_indices, :, :]
            if verbose:
                print('Number of steady states is ',
                      str(steady_state_eigvecs.shape[0]))
            # If there is one steady s, then normalize it because it is a valid density matrix
            if steady_state_eigvecs.shape[0] == 1:
                steady_state_eigvecs[0, :, :] = steady_state_eigvecs[
                    0, :, :] / np.trace(steady_state_eigvecs[0, :, :])
                if verbose:
                    print(
                        'Steady state is a valid density matrix:',
                        tools.is_valid_state(steady_state_eigvecs[0, :, :],
                                             verbose=False))
            steady_state_eigvals = eigvals[steady_state_indices]
            if plot:
                steady_state_eigvals_cc = steady_state_eigvals.conj()
                steady_state_eigvals_real = np.concatenate(
                    (steady_state_eigvals.real, steady_state_eigvals.real))
                steady_state_eigvals_complex = np.concatenate(
                    (steady_state_eigvals_cc.imag,
                     steady_state_eigvals_cc.imag))
                plt.hlines(0, xmin=-1, xmax=.1)
                plt.vlines(0, ymin=-100, ymax=100)
                plt.scatter(steady_state_eigvals_real,
                            steady_state_eigvals_complex,
                            c='m',
                            s=4)
                plt.show()
            return steady_state_eigvals, steady_state_eigvecs
        else:
            return None, None
Esempio n. 21
0
    def variational_grad(self, param, initial_state=None):
        """Calculate the objective function F and its gradient exactly
            Input:
                param = parameters of QAOA

            Output: (F, Fgrad)
               F = <HamC> for minimization
               Fgrad = gradient of F with respect to param
        """
        # TODO: make this work for continuous noise models
        if self.noise_model == 'continuous':
            raise NotImplementedError(
                'Variational gradient does not currently support continuous noise model'
            )
        param = np.asarray(param)
        # Preallocate space for storing copies of wavefunction - necessary for efficient computation of analytic
        # gradient
        if self.code.logical_code and initial_state is None:
            initial_state = State(tensor_product([self.code.logical_basis[1]] *
                                                 self.N),
                                  code=self.code)
        elif initial_state is None:
            initial_state = State(np.zeros(
                (self.cost_hamiltonian.hamiltonian.shape[0], 1)),
                                  code=self.code)
            initial_state[-1, -1] = 1
        if not (self.noise_model is None or self.noise_model == 'monte_carlo'):
            # Initial s should be a density matrix
            initial_state = State(outer_product(initial_state, initial_state),
                                  code=self.code)
        psi = initial_state
        if initial_state.is_ket:
            memo = np.zeros([psi.shape[0], 2 * self.depth + 2],
                            dtype=np.complex128)
            memo[:, 0] = np.squeeze(psi.T)
            tester = psi.copy()
        else:
            memo = np.zeros([psi.shape[0], psi.shape[0], self.depth + 1],
                            dtype=np.complex128)
            memo[..., 0] = np.squeeze(outer_product(psi, psi))
            tester = State(outer_product(psi, psi), code=self.code)
        # Evolving forward
        for j in range(self.depth):
            if initial_state.is_ket:
                tester = self.hamiltonian[j].evolve(tester, param[j])
                memo[:, j + 1] = np.squeeze(tester.T)
            else:
                self.hamiltonian[j].evolve(tester, param[j])
                # Goes through memo, evolves every density matrix in it, and adds one more in the j*m+i+1 position
                # corresponding to H_i*p
                s0_prenoise = memo[..., 0]
                for k in range(j + 1):
                    s = State(memo[..., k], code=self.code)
                    s = self.hamiltonian[j].evolve(s, param[j])
                    if k == 0:
                        s0_prenoise = s.copy()
                    if self.noise_model is not None:
                        if not (self.noise[j] is None):
                            s = self.noise[j].evolve(s, param[j])
                    memo[..., k] = s.copy()
                s0_prenoise = self.hamiltonian[j].left_multiply(s0_prenoise)
                if self.noise_model is not None:
                    if not (self.noise[j] is None):
                        s0_prenoise = self.noise[j].evolve(
                            s0_prenoise, param[j])
                memo[..., j + 1] = s0_prenoise.copy()

        # Multiply by cost_hamiltonian
        if initial_state.is_ket:
            memo[:, self.depth +
                 1] = self.cost_hamiltonian.hamiltonian @ memo[:, self.depth]

            s = State(np.array([memo[:, self.depth + 1]]).T, code=self.code)
        else:
            for k in range(self.depth + 1):
                s = memo[..., k]
                s = State(self.cost_hamiltonian.hamiltonian * s,
                          code=self.code)
                memo[..., k] = s

        # Evolving backwards, if ket:
        if initial_state.is_ket:
            for k in range(self.depth):
                s = self.hamiltonian[self.depth - k - 1].evolve(
                    s, -1 * param[self.depth - k - 1])
                memo[:, self.depth + k + 2] = np.squeeze(s.T)

        # Evaluating objective function
        if initial_state.is_ket:
            F = np.real(np.vdot(memo[:, self.depth], memo[:, self.depth + 1]))
        else:
            F = np.real(np.trace(memo[..., 0]))

        # Evaluating gradient analytically
        Fgrad = np.zeros(self.depth)
        for r in range(self.depth):
            if initial_state.is_ket:
                s = State(np.array([memo[:, 2 * self.depth + 1 - r]]).T,
                          code=self.code)
                s = self.hamiltonian[r].left_multiply(s)
                Fgrad[r] = -2 * np.imag(np.vdot(memo[:, r], np.squeeze(s.T)))
            else:
                Fgrad[r] = 2 * np.imag(np.trace(memo[..., r + 1]))
        return F, Fgrad