Пример #1
0
def find_gap(graph, use_Z2_symmetry=True):
    # Compute the number of ground states and first excited states
    cost = HamiltonianMaxCut(graph, cost_function=False, use_Z2_symmetry=use_Z2_symmetry)
    # Generate a dummy graph with one fewer nodes
    # Cut cost and driver hamiltonian in half to account for Z2 symmetry
    if use_Z2_symmetry:
        driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n - 1)))
        driver.hamiltonian
        row = list(range(2 ** (graph.n - 1)))
        column = list(range(2 ** (graph.n - 1)))
        column.reverse()
        driver._hamiltonian = driver._hamiltonian + (-1) ** graph.n * sparse.csr_matrix(
            (np.ones(2 ** (graph.n - 1)), (row, column)))
    else:
        driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n)))
    n_ground = len(ground_states(cost.hamiltonian))
    times = np.arange(0, 1, .01)

    def schedule(t):
        cost.energies = (t,)
        driver.energies = (t - 1,)

    min_gap = np.inf
    for i in range(len(times)):
        schedule(times[i])
        eigvals = SchrodingerEquation(hamiltonians=[cost, driver]).eig(k=n_ground + 1, return_eigenvectors=False)
        eigvals = np.flip(eigvals)
        if eigvals[-1] - eigvals[0] < min_gap:
            min_gap = eigvals[-1] - eigvals[0]
    return min_gap, n_ground
Пример #2
0
def node_removed_torus(y, x, return_mis=False):
    graph = nx.grid_2d_graph(x, y, periodic=True)
    # Remove 3 of four corners
    graph.remove_node((0, 0))
    graph.add_edge((1, 0), (y - 1, 0), weight=1)
    graph.add_edge((1, 0), (1, x - 1), weight=1)
    graph.add_edge((0, 1), (0, x - 1), weight=1)
    graph.add_edge((0, 1), (y - 1, 1), weight=1)
    nodes = graph.nodes
    new_nodes = list(range(len(nodes)))
    mapping = dict(zip(nodes, new_nodes))
    nx.relabel_nodes(graph, mapping, copy=False)
    if return_mis:
        return Graph(graph), x * y / 2 - 1
    else:
        return Graph(graph)
Пример #3
0
def generate_bipartite_graph(n, m, p, visualize=False):
    graph = nx.algorithms.bipartite.generators.random_graph(n, m, p)
    print(graph.edges)
    if visualize:
        nx.draw(graph, pos=nx.bipartite_layout(graph, list(graph.nodes)[:n]))
        plt.show()
    return Graph(graph)
Пример #4
0
def node_defect_torus(y, x, return_mis=False):
    graph = nx.grid_2d_graph(x, y, periodic=True)
    # Remove 3 of four corners
    for node in graph.nodes:
        graph.nodes[node]['weight'] = 1
    graph.nodes[(0, 0)]['weight'] = 2
    #print(graph.nodes)
    # graph.remove_node((0, 0))
    nodes = graph.nodes
    new_nodes = list(range(len(nodes)))
    mapping = dict(zip(nodes, new_nodes))
    nx.relabel_nodes(graph, mapping, copy=False)
    if return_mis:
        return Graph(graph), x * y / 2 - 1
    else:
        return Graph(graph)
Пример #5
0
def generate_regular_graph(d, n, visualize=False):
    graph = nx.random_regular_graph(d, n)
    print(graph.edges)
    if visualize:
        nx.draw(graph)
        plt.show()
    return Graph(graph)
Пример #6
0
def effective_markov_chain(graph=None, n=3, initial_state=None, omega_g=1, omega_r=1, gamma=1, tf=1):
    def schedule(t, tf):
        return [4 * (tf - t) / tf * omega_r ** 2 / gamma, 4 * t / tf * omega_g ** 2 / gamma]

    # Generate a graph
    if graph is None:
        graph, mis = line(n)
    graph = Graph(graph)
    # Generate the transition matrix
    # For each IS, look at spin flips generated by the laser
    # Over-allocate space
    g_to_r = transition_matrix(graph, (1, 0))
    r_to_g = transition_matrix(graph, (0, 1))
    if initial_state is None:
        initial_state = np.zeros((g_to_r.shape[0], 1), dtype=np.float64)
        initial_state[-1, -1] = 1
    t0 = 0
    num = tf * 5
    state = initial_state
    times = np.linspace(t0, tf, num=num)
    dt = times[1] - times[0]
    cost = np.zeros(len(times))
    for i in range(len(times)):
        state_transitions = np.sum(dt * r_to_g * schedule(times[i], tf)[0] + dt * g_to_r * schedule(times[i], tf)[1],
                                   axis=0)
        state = np.multiply((1 - state_transitions.T), state) + (
                    dt * r_to_g * schedule(times[i], tf)[0] + dt * g_to_r * schedule(times[i], tf)[1]) @ state
        cost[i] = state[0]
        # Normalize s
        # print((np.sum(np.abs(s))))
        # s = s/(np.sum(np.abs(s)))
    plt.plot(times, cost)
    plt.show()
    print(state)
Пример #7
0
def ARvstime_EIT(tf=10, graph=None, mis=None, show_graph=False, n=3):
    schedule = lambda t, tf: [[t / tf, (tf - t) / tf, 1], [1]]
    if graph is None:
        graph, mis = line_graph(n=n)
    graph = Graph(graph)
    if show_graph:
        nx.draw(graph)
        plt.show()
    rabi1 = 3
    rabi2 = 3
    # Generate the driving
    laser1 = hamiltonian.HamiltonianDriver(transition=(0, 1),
                                           energy=rabi1,
                                           code=rydberg,
                                           IS_subspace=True,
                                           graph=graph)
    laser2 = hamiltonian.HamiltonianDriver(transition=(1, 2),
                                           energy=rabi2,
                                           code=rydberg,
                                           IS_subspace=True,
                                           graph=graph)
    rydberg_hamiltonian_cost = hamiltonian.HamiltonianMIS(graph,
                                                          code=rydberg,
                                                          detuning=1,
                                                          energy=0,
                                                          IS_subspace=True)
    # Initialize spontaneous emission
    spontaneous_emission_rate = 1
    spontaneous_emission = lindblad_operators.SpontaneousEmission(
        transition=(1, 2),
        rate=spontaneous_emission_rate,
        code=rydberg,
        IS_subspace=True,
        graph=graph)

    # Initialize master equation
    master_equation = LindbladMasterEquation(
        hamiltonians=[laser2, laser1], jump_operators=[spontaneous_emission])
    # Begin with all qubits in the ground codes
    psi = np.zeros((rydberg_hamiltonian_cost.hamiltonian.shape[0], 1),
                   dtype=np.complex128)
    psi[-1, -1] = 1
    psi = tools.outer_product(psi, psi)
    # Generate annealing schedule
    results = master_equation.run_ode_solver(
        psi, 0, tf, num_from_time(tf), schedule=lambda t: schedule(t, tf))
    cost_function = [
        rydberg_hamiltonian_cost.cost_function(results[i], is_ket=False) / mis
        for i in range(results.shape[0])
    ]
    print(cost_function[-1])
    plt.scatter(np.linspace(0, tf, num_from_time(tf)),
                cost_function,
                c='teal',
                label='approximation ratio')
    plt.legend()
    plt.xlabel(r'Approximation ratio')
    plt.ylabel(r'Time $t$')
    plt.show()
Пример #8
0
def sk_integer(n, verbose=False):
    graph = nx.complete_graph(n)
    weights = [-1, 1]
    for (i, j) in itertools.combinations(range(n), 2):
        graph[i][j]['weight'] = weights[np.random.randint(2)]
    if verbose:
        print(graph.edges.data())
    return Graph(graph)
Пример #9
0
def low_energy_subspace_at_fixed_time(graph, s,  use_Z2_symmetry=True, n_ground=None, k=None, p=2):
    # Compute the number of ground states and first excited states
    if p == 2:
        cost = HamiltonianMaxCut(graph, cost_function=False, use_Z2_symmetry=use_Z2_symmetry)
    if p != 2:
        ham = graph
        if use_Z2_symmetry:
            graph = sk_integer(int(np.log2(graph.shape[0]))+1)
        else:
            graph = sk_integer(int(np.log2(graph.shape[0])))

        cost = HamiltonianMaxCut(graph, cost_function=False, use_Z2_symmetry=use_Z2_symmetry)
        # Replace the cost Hamiltonian
        cost._diagonal_hamiltonian = ham
        if use_Z2_symmetry:
            cost._hamiltonian = sparse.csr_matrix((cost._diagonal_hamiltonian.flatten(), (np.arange(2 ** (graph.n-1)),
                                                 np.arange(2 ** (graph.n-1)))),shape=(2 ** (graph.n-1), 2 ** (graph.n-1)))
        else:
            cost._hamiltonian = sparse.csr_matrix((cost._diagonal_hamiltonian.flatten(), (np.arange(2 ** (graph.n)),
                                                 np.arange(2 ** (graph.n)))),shape=(2 ** (graph.n), 2 ** (graph.n)))
    # Generate a dummy graph with one fewer nodes
    # Cut cost and driver hamiltonian in half to account for Z2 symmetry
    if use_Z2_symmetry:
        driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n - 1)))
        driver.hamiltonian
        row = list(range(2 ** (graph.n - 1)))
        column = list(range(2 ** (graph.n - 1)))
        column.reverse()
        driver._hamiltonian = driver._hamiltonian + (-1) ** graph.n * sparse.csr_matrix(
            (np.ones(2 ** (graph.n - 1)), (row, column)))
    else:
        driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n)))
    if k is None:
        if n_ground is None:
            n_ground = len(ground_states(cost.hamiltonian))
        k=n_ground+1

    def schedule(t):
        cost.energies = (np.sqrt(np.math.factorial(p)/(2 * graph.n**(p-1))) * t,)
        driver.energies = (1 - t,)
    schedule(s)
    eigvals = SchrodingerEquation(hamiltonians=[cost, driver]).eig(k=k, return_eigenvectors=False)
    eigvals = np.flip(eigvals)
    return s, eigvals
Пример #10
0
def gap_over_time(graph, verbose=False, use_Z2_symmetry=True):
    # Compute the number of ground states and first excited states
    cost = HamiltonianMaxCut(graph, cost_function=False, use_Z2_symmetry=use_Z2_symmetry)
    print(np.min(cost.hamiltonian) / graph.n ** (3 / 2))
    # Generate a dummy graph with one fewer nodes
    # Cut cost and driver hamiltonian in half to account for Z2 symmetry
    if use_Z2_symmetry:
        driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n - 1)))
        driver.hamiltonian
        row = list(range(2 ** (graph.n - 1)))
        column = list(range(2 ** (graph.n - 1)))
        column.reverse()
        driver._hamiltonian = driver._hamiltonian + (-1) ** graph.n * sparse.csr_matrix(
            (np.ones(2 ** (graph.n - 1)), (row, column)))
    else:
        driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n)))
    if verbose:
        print(ground_states(cost.hamiltonian))
    n_ground = len(ground_states(cost.hamiltonian))
    # print(driver.hamiltonian.todense())
    # print(np.linalg.eigh(driver.hamiltonian.todense()))
    if verbose:
        print('degeneracy ', n_ground)
    times = np.arange(0, 1, .01)

    def schedule(t):
        cost.energies = (1 / np.sqrt(graph.n) * t,)
        driver.energies = (1 - t,)

    all_eigvals = np.zeros((len(times), n_ground + 1))
    for i in range(len(times)):
        if verbose:
            print(times[i])
        schedule(times[i])
        eigvals = SchrodingerEquation(hamiltonians=[cost, driver]).eig(k=n_ground + 1, return_eigenvectors=False)
        eigvals = np.flip(eigvals)
        all_eigvals[i] = eigvals
    for i in range(n_ground + 1):
        plt.scatter(times, all_eigvals[:, i], color='blue', s=2)
    if verbose:
        print(all_eigvals)
    plt.xlabel(r'normalized time $s$')
    plt.ylabel(r'energy')
    plt.show()
Пример #11
0
def small_3regular_graph(visualize=False):
    edges = [(6, 9), (6, 4), (6, 0), (9, 3), (9, 0), (1, 3), (1, 5), (1, 7),
             (3, 8), (4, 7), (4, 2), (7, 0), (2, 8), (2, 5), (8, 5)]
    graph = nx.Graph()
    graph.add_nodes_from(list(range(10)))
    graph.add_edges_from(edges)
    if visualize:
        nx.draw(graph, pos=nx.bipartite_layout(graph, list(graph.nodes)[:5]))
        plt.show()
    return Graph(graph)
Пример #12
0
def small_bipartite_graph(visualize=False):
    edges = [(0, 5), (0, 6), (0, 7), (0, 8), (0, 9), (1, 5), (1, 6), (1, 7),
             (1, 8), (1, 9), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9), (3, 5),
             (3, 7), (3, 8), (3, 9), (4, 5), (4, 6), (4, 7), (4, 9)]
    graph = nx.Graph()
    graph.add_nodes_from(list(range(10)))
    graph.add_edges_from(edges)
    if visualize:
        nx.draw(graph, pos=nx.bipartite_layout(graph, list(graph.nodes)[:5]))
        plt.show()
    return Graph(graph)
Пример #13
0
def medium_3regular_graph(visualize=False):
    edges = [(4, 7), (4, 10), (4, 15), (7, 15), (7, 2), (10, 11), (10, 12),
             (11, 9), (11, 5), (2, 5), (2, 3), (5, 14), (3, 14), (3, 6),
             (14, 15), (12, 0), (12, 8), (9, 1), (9, 13), (6, 8), (6, 13),
             (1, 13), (1, 0), (0, 8)]
    graph = nx.Graph()
    graph.add_nodes_from(list(range(10)))
    graph.add_edges_from(edges)
    if visualize:
        nx.draw(graph, pos=nx.bipartite_layout(graph, list(graph.nodes)[:5]))
    plt.show()
    return Graph(graph)
Пример #14
0
def medium_bipartite_graph(visualize=False):
    edges = [(0, 7), (0, 8), (0, 9), (0, 12), (0, 13), (1, 7), (1, 8), (1, 10),
             (1, 11), (1, 12), (1, 13), (2, 7),
             (2, 8), (2, 9), (2, 10), (2, 12), (2, 13), (3, 7), (3, 8), (3, 9),
             (3, 10), (4, 8), (4, 11), (4, 12), (4, 13), (5, 8), (5, 10),
             (5, 11), (6, 7), (6, 8), (6, 10), (6, 11), (6, 13)]
    graph = nx.Graph()
    graph.add_nodes_from(list(range(10)))
    graph.add_edges_from(edges)
    if visualize:
        nx.draw(graph, pos=nx.bipartite_layout(graph, list(graph.nodes)[:5]))
        plt.show()
    return Graph(graph)
Пример #15
0
def jump_rate_scaling():
    graph = Graph(nx.star_graph(n=4))  # line_graph(n=3)
    energy = 1
    phis = np.arange(.001, .01, .001)
    rates = np.zeros(len(phis))
    rydberg_hamiltonian_cost = hamiltonian.HamiltonianMIS(graph,
                                                          IS_subspace=True,
                                                          code=qubit)
    print(rydberg_hamiltonian_cost.hamiltonian)
    for d in range(len(phis)):
        print(d)
        laser = EffectiveOperatorHamiltonian(omega_g=np.cos(phis[d]),
                                             omega_r=np.sin(phis[d]),
                                             energies=(energy, ),
                                             graph=graph)
        dissipation = EffectiveOperatorDissipation(omega_g=np.cos(phis[d]),
                                                   omega_r=np.sin(phis[d]),
                                                   rates=(energy, ),
                                                   graph=graph)
        # Find the dressed states
        simulation = SchrodingerEquation(hamiltonians=[laser])
        eigval, eigvec = simulation.eig()
        eigval = [
            rydberg_hamiltonian_cost.approximation_ratio(
                State(eigvec[i].T, is_ket=True, graph=graph, IS_subspace=True))
            for i in range(eigval.shape[0])
        ]
        print(eigval)
        optimal_eigval_index = np.argmax(eigval)
        optimal_eigval = eigvec[optimal_eigval_index]
        rate = 0
        for i in range(graph.n):
            # Compute the decay rate of the MIS into other states
            for j in range(eigvec.shape[0]):
                if j != optimal_eigval_index:
                    rate += np.abs(eigvec[j] @ dissipation.jump_operators[i]
                                   @ optimal_eigval.T)**2
        rates[d] = rate
    fit = np.polyfit(np.log(phis), np.log(rates), deg=1)

    plt.scatter(np.log(phis), np.log(rates), color='teal')
    plt.plot(np.log(phis),
             fit[0] * np.log(phis) + fit[1],
             color='k',
             label=r'$y=$' + str(np.round(fit[0], decimals=2)) + r'$x+$' +
             str(np.round(fit[1], decimals=2)))
    plt.legend()
    plt.xlabel(r'$\log(\phi)$')
    plt.ylabel(r'$\log(\rm{rate})$')
    plt.show()
Пример #16
0
def delta_vs_T():
    graph, mis = degree_fails_graph(return_mis=True)
    graph = Graph(graph)
    graph.mis_size = mis
    times = np.concatenate([np.arange(7, 11, 1), np.arange(20, 110, 10)])
    detunings = np.arange(3, 27, 3)
    print(times, detunings)
    cost_function = []
    for i in range(len(times)):
        cost_function_detuning = []
        for j in range(len(detunings)):
            simulation = adiabatic_simulation(graph,
                                              noise_model='continuous',
                                              delta=detunings[j])
            results, info = simulation.run(
                times[i],
                schedule=lambda t, tf: simulation.rydberg_MIS_schedule(
                    t, tf, coefficients=[np.sqrt(detunings[j]), 1]),
                method='RK23')
            cost = simulation.cost_hamiltonian.cost_function(
                results[-1]) / simulation.graph.mis_size
            print(times[i], detunings[j], cost)
            cost_function_detuning.append(cost)
        cost_function.append(cost_function_detuning)

    plt.imshow(cost_function,
               vmin=0,
               vmax=1,
               interpolation=None,
               extent=[0, max(detunings), max(times), 0],
               origin='upper')
    plt.xticks(detunings)
    plt.yticks(times)
    plt.colorbar()
    plt.xlabel(r'Detuning $\delta$')
    plt.ylabel(r'Annealing time $T$')
    plt.show()
Пример #17
0
def make_ring_graph_multiring(n):
    assert n % 2 == 0 and n % 3 == 0
    graph_dict = {x: {(x + i) % n: {'weight': 1} for i in [-1, 1]} for x in range(n)}

    for i in range(int(n)):
        if i % 3 == 0:
            graph_dict[i][(i + int(n / 2) + 1) % n] = {'weight': 1}
            graph_dict[(i + int(n / 2) + 1) % n][i] = {'weight': 1}
            graph_dict[i][(i + int(n / 2) - 1) % n] = {'weight': 1}
            graph_dict[(i + int(n / 2) - 1) % n][i] = {'weight': 1}
        else:
            graph_dict[i][(i + int(n / 2)) % n] = {'weight': 1}
            graph_dict[(i + int(n / 2)) % n][i] = {'weight': 1}
    graph = nx.to_networkx_graph(graph_dict)
    return Graph(graph)
Пример #18
0
def large_bipartite_graph(visualize=False):
    edges = [(0, 11), (0, 13), (0, 14), (0, 15), (0, 17), (0, 18), (0, 19),
             (1, 10), (1, 14), (1, 15), (1, 16), (1, 18), (2, 10), (2, 11),
             (2, 13), (2, 14), (2, 19), (3, 12), (3, 16), (3, 18), (3, 19),
             (4, 11), (4, 13), (4, 14), (4, 15), (4, 16), (4, 18), (5, 10),
             (5, 12), (5, 14), (5, 15), (5, 17), (5, 18), (5, 19), (6, 10),
             (6, 12), (6, 13), (6, 14), (6, 15), (6, 16), (6, 17), (6, 18),
             (6, 19), (7, 10), (7, 11), (7, 12), (7, 13), (7, 14), (7, 16),
             (7, 17), (7, 18), (7, 19), (8, 10), (8, 12), (8, 13), (8, 14),
             (8, 16), (8, 17), (8, 18), (8, 19), (9, 11), (9, 12), (9, 13),
             (9, 14), (9, 16), (9, 17), (9, 19)]
    graph = nx.Graph()
    graph.add_nodes_from(list(range(10)))
    graph.add_edges_from(edges)
    if visualize:
        nx.draw(graph, pos=nx.bipartite_layout(graph, list(graph.nodes)[:5]))
        plt.show()
    return Graph(graph)
Пример #19
0
def generate_SDP_graph(d, epsilon, visualize=False, le=False):
    graph = nx.Graph()
    graph.add_nodes_from(np.arange(2**d))
    for i in range(2**d):
        for j in range(2**d):
            binary_i = 2 * (tools.int_to_nary(i, size=d) - 1 / 2) / np.sqrt(d)
            binary_j = 2 * (tools.int_to_nary(j, size=d) - 1 / 2) / np.sqrt(d)
            if le:
                if (1 - np.dot(binary_i, binary_j)) / 2 > 1 - epsilon + 1e-5:
                    graph.add_edge(i, j, weight=1)
            else:
                if np.isclose((1 - np.dot(binary_i, binary_j)) / 2,
                              1 - epsilon):
                    graph.add_edge(i, j, weight=1)
    if visualize:
        nx.draw(graph, with_labels=True)
        plt.show()
    return Graph(graph)
Пример #20
0
def omega_vs_T(graph=None, mis=None, show_graph=False, n=3):
    schedule = lambda t, tf: [[t / tf, (tf - t) / tf, 1], [1]]
    if graph is None:
        graph, mis = line_graph(n=n)
    graph = Graph(graph)
    if show_graph:
        nx.draw(graph)
        plt.show()
    times = np.arange(20, 100, 10)
    omegas = np.arange(5, 8, .25)
    print(times, omegas)
    cost_function = []
    for i in range(len(times)):
        cost_function_detuning = []
        for j in range(len(omegas)):
            simulation = adiabatic_simulation(graph,
                                              noise_model='continuous',
                                              delta=omegas[j])
            results = master_equation.run_ode_solver(
                psi,
                0,
                times[i],
                num_from_time(times[i]),
                schedule=lambda t: schedule(t, times[i]))
            cost = rydberg_hamiltonian_cost.cost_function(results[-1],
                                                          is_ket=False) / mis
            print(times[i], omegas[j], cost)
            cost_function_detuning.append(cost)
        cost_function.append(cost_function_detuning)

    plt.imshow(cost_function,
               vmin=0,
               vmax=1,
               interpolation=None,
               extent=[0, max(omegas), max(times), 0],
               origin='upper')
    plt.xticks(np.linspace(0, max(omegas), 10))
    plt.yticks(np.linspace(0, max(times), 10))
    plt.colorbar()
    plt.xlabel(r'Rabi frequency $\Omega$')
    plt.ylabel(r'Annealing time $T$')
    plt.show()
Пример #21
0
def time_performance():
    graph, mis = line_graph(3, return_mis=True)
    # graph, mis = degree_fails_graph(return_mis=True)
    graph = Graph(graph)
    ratios_d = [1]
    ratios_r = [1]
    for d in ratios_d:
        for r in ratios_r:
            print(d, r)

            def rydberg_EIT_schedule(t, tf, coefficients=None):
                if coefficients is None:
                    coefficients = [1, 1]
                for i in range(len(simulation_eit.hamiltonian)):
                    if i == 0:
                        # We don't want to update normal detunings
                        simulation_eit.hamiltonian[i].energies = [
                            t / tf * coefficients[0]
                        ]
                    if i == 1:
                        simulation_eit.hamiltonian[i].energies = [
                            (tf - t) / tf * coefficients[1]
                        ]

                return True

            simulation_eit = eit_simulation(graph,
                                            noise_model='continuous',
                                            gamma=1,
                                            delta=d,
                                            Omega_g=r,
                                            Omega_r=r)
            simulation_eit.performance_vs_total_time(
                [5, 10, 15, 20],
                metric='cost_function',
                schedule=lambda t, tf: rydberg_EIT_schedule(
                    t, tf, coefficients=[r, r]),
                plot=True,
                verbose=True,
                method='RK23')
Пример #22
0
def find_gap_fixed_n(n, use_degenerate=True, use_Z2_symmetry=True, verbose=False, p=2):
    # Compute the number of ground states and first excited states
    n_ground = np.inf
    if not use_degenerate:
        if use_Z2_symmetry:
            if p == 2:
                while n_ground != 1:
                    graph = sk_integer(n)
                    cost = HamiltonianMaxCut(graph, cost_function=False, use_Z2_symmetry=use_Z2_symmetry)
                    n_ground = len(ground_states(cost.hamiltonian))
                    if verbose and n_ground != 1:
                        print('Initially failed to find graph, n_ground =', n_ground)
            else:
                graph = sk_hamiltonian(n, p=p, use_degenerate=use_degenerate, use_Z2_symmetry=use_Z2_symmetry)

        else:
            if p == 2:
                while n_ground != 2:
                    graph = sk_integer(n)
                    cost = HamiltonianMaxCut(graph, cost_function=False, use_Z2_symmetry=use_Z2_symmetry)
                    n_ground = len(ground_states(cost.hamiltonian))
                    if verbose and n_ground != 2:
                        print('Initially failed to find graph, n_ground =', n_ground)
            else:
                graph = sk_hamiltonian(n, p=p, use_degenerate=use_degenerate, use_Z2_symmetry=use_Z2_symmetry)
    else:
        if p == 2:
            graph = sk_integer(n)
            cost = HamiltonianMaxCut(graph, cost_function=False, use_Z2_symmetry=use_Z2_symmetry)
            n_ground = len(ground_states(cost.hamiltonian))
        else:
            graph = sk_hamiltonian(n, p=p, use_degenerate=use_degenerate, use_Z2_symmetry=use_Z2_symmetry)
    if p != 2:
        ham = graph
        graph = sk_integer(n)
        cost = HamiltonianMaxCut(graph, cost_function=False, use_Z2_symmetry=use_Z2_symmetry)
        # Replace the cost Hamiltonian
        cost._diagonal_hamiltonian = ham
        n_ground = len(ground_states(ham))

        if use_Z2_symmetry:
            cost._hamiltonian = sparse.csr_matrix((cost._diagonal_hamiltonian.flatten(), (np.arange(2 ** (graph.n - 1)),
                                                                                          np.arange(
                                                                                              2 ** (graph.n - 1)))),
                                                  shape=(2 ** (graph.n - 1), 2 ** (graph.n - 1)))
        else:
            cost._hamiltonian = sparse.csr_matrix((cost._diagonal_hamiltonian.flatten(), (np.arange(2 ** (graph.n)),
                                                                                          np.arange(2 ** (graph.n)))),
                                                  shape=(2 ** (graph.n), 2 ** (graph.n)))
    # Generate a dummy graph with one fewer nodes
    # Cut cost and driver hamiltonian in half to account for Z2 symmetry
    if use_Z2_symmetry:
        driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n - 1)))
        driver.hamiltonian
        row = list(range(2 ** (graph.n - 1)))
        column = list(range(2 ** (graph.n - 1)))
        column.reverse()
        driver._hamiltonian = driver._hamiltonian + (-1) ** graph.n * sparse.csr_matrix(
            (np.ones(2 ** (graph.n - 1)), (row, column)))
    else:
        driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n)))

    def schedule(t):
        cost.energies = (np.sqrt(np.math.factorial(p)/(2 * graph.n**(p-1))) * t,)
        driver.energies = (1-t,)

    def gap(t):
        t = t[0]
        if verbose:
            print('time', t)
        schedule(t)
        eigvals = SchrodingerEquation(hamiltonians=[cost, driver]).eig(k=n_ground + 1, return_eigenvectors=False)
        eigvals = np.flip(eigvals)
        if verbose:
            print('gap', eigvals[-1]-eigvals[0])
        return eigvals[-1]-eigvals[0]

    # Now minimize the gap
    min_gap = scipy.optimize.minimize(gap, .85, bounds=[(0, 1)])
    return min_gap.fun
Пример #23
0
    def __init__(self,
                 omega_g,
                 omega_r,
                 rates=(1, ),
                 graph: Graph = None,
                 IS_subspace=True,
                 code=qubit):
        self.omega_g = omega_g
        self.omega_r = omega_r

        self.IS_subspace = IS_subspace
        self.transition = (0, 1)
        self.graph = graph
        # Construct jump operators
        if self.IS_subspace:
            # Generate sparse mixing Hamiltonian
            assert graph is not None
            assert isinstance(graph, Graph)
            if code is not qubit:
                IS, nary_to_index, num_IS = graph.independent_sets_code(
                    self.code)
            else:
                # We have already solved for this information
                IS, nary_to_index, num_IS = graph.independent_sets, graph.binary_to_index, graph.num_independent_sets
            self._jump_operators_rg = []
            self._jump_operators_gg = []
            # For each atom, consider the states spontaneous emission can generate transitions between
            # Over-allocate space
            for j in range(graph.n):
                rows_rg = np.zeros(num_IS, dtype=int)
                columns_rg = np.zeros(num_IS, dtype=int)
                entries_rg = np.zeros(num_IS, dtype=int)
                rows_gg = np.zeros(num_IS, dtype=int)
                columns_gg = np.zeros(num_IS, dtype=int)
                entries_gg = np.zeros(num_IS, dtype=int)
                num_terms_gg = 0
                num_terms_rg = 0
                for i in IS:
                    if IS[i][2][j] == self.transition[0]:
                        # Flip spin at this location
                        # Get binary representation
                        temp = IS[i][2].copy()
                        temp[j] = self.transition[1]
                        flipped_temp = tools.nary_to_int(temp, base=code.d)
                        if flipped_temp in nary_to_index:
                            # This is a valid spin flip
                            rows_rg[num_terms_rg] = nary_to_index[flipped_temp]
                            columns_rg[num_terms_rg] = i
                            entries_rg[num_terms_rg] = 1
                            num_terms_rg += 1
                    elif IS[i][2][j] == self.transition[1]:
                        rows_gg[num_terms_gg] = i
                        columns_gg[num_terms_gg] = i
                        entries_gg[num_terms_gg] = 1
                        num_terms_gg += 1

                # Cut off the excess in the arrays
                columns_rg = columns_rg[:num_terms_rg]
                rows_rg = rows_rg[:num_terms_rg]
                entries_rg = entries_rg[:num_terms_rg]
                columns_gg = columns_gg[:num_terms_gg]
                rows_gg = rows_gg[:num_terms_gg]
                entries_gg = entries_gg[:num_terms_gg]
                # Now, append the jump operator
                jump_operator_rg = sparse.csc_matrix(
                    (entries_rg, (rows_rg, columns_rg)),
                    shape=(num_IS, num_IS))
                jump_operator_gg = sparse.csc_matrix(
                    (entries_gg, (rows_gg, columns_gg)),
                    shape=(num_IS, num_IS))

                self._jump_operators_rg.append(jump_operator_rg)
                self._jump_operators_gg.append(jump_operator_gg)
            self._jump_operators_rg = np.asarray(self._jump_operators_rg)
            self._jump_operators_gg = np.asarray(self._jump_operators_gg)
        else:
            #self._jump_operators_rg = []
            #self._jump_operators_gg = []
            op_rg = np.array([[[0, 0], [1, 0]]])
            op_gg = np.array([[[0, 0], [0, 1]]])
            """for i in range(self.graph.n):
                jump_operator_rg = tools.tensor_product(
                    [np.identity(2 ** i), op_rg, np.identity(2 ** (self.graph.n - i-1))])
                self._jump_operators_rg.append(jump_operator_rg)
                jump_operator_gg = tools.tensor_product(
                    [np.identity(2 ** i), op_gg, np.identity(2 ** (self.graph.n - i-1))])
                self._jump_operators_gg.append(jump_operator_gg)"""
            self._jump_operators_rg = op_rg
            self._jump_operators_gg = op_gg

        super().__init__(None,
                         rates=rates,
                         graph=graph,
                         IS_subspace=IS_subspace,
                         code=code)
Пример #24
0
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
from qsim.tools.tools import *
from qsim.evolution.hamiltonian import HamiltonianMaxCut
from qsim.graph_algorithms.graph import Graph
from sklearn.metrics import pairwise_distances
from scipy.sparse import csr_matrix

while True:
    d = 5
    n = 24
    graph = nx.random_regular_graph(d, n)
    hamz = HamiltonianMaxCut(Graph(graph))
    maxcut_size = np.max(hamz._diagonal_hamiltonian).astype(int)
    mincut_size = np.min(hamz._diagonal_hamiltonian).astype(int)
    #plt.hist(np.array(csr_matrix.diagonal(hamz.hamiltonian)), bins=maxcut_size-mincut_size, range=(mincut_size, maxcut_size))
    #plt.show()
    independence_polynomial = []
    for i in range(maxcut_size - 7, maxcut_size):
        independence_polynomial.append(
            len(np.argwhere(hamz._diagonal_hamiltonian == i).T[0]))
    ratios = []
    for i in range(len(independence_polynomial) - 1):
        try:
            ratios.append(independence_polynomial[i] /
                          independence_polynomial[i + 1])
        except ZeroDivisionError:
            ratios.append(0)
    print(independence_polynomial, ratios)
    if True:  #ratios[-1] > 3:
Пример #25
0
def effective_operator_comparison(graph=None,
                                  mis=None,
                                  tf=10,
                                  show_graph=False,
                                  n=3,
                                  gamma=500):
    # Generate annealing schedule
    def schedule1(t, tf):
        return [[t / tf, (tf - t) / tf, 1], [1]]

    if graph is None:
        graph, mis = line_graph(n=n)
    graph = Graph(graph)
    if show_graph:
        nx.draw(graph)
        plt.show()
    # Generate the driving and Rydberg Hamiltonians
    rabi1 = 1
    laser1 = hamiltonian.HamiltonianDriver(transition=(0, 1),
                                           energies=[rabi1],
                                           code=rydberg,
                                           IS_subspace=True,
                                           graph=graph)
    rabi2 = 1
    laser2 = hamiltonian.HamiltonianDriver(transition=(1, 2),
                                           energies=[rabi2],
                                           code=rydberg,
                                           IS_subspace=True,
                                           graph=graph)
    rydberg_hamiltonian_cost = hamiltonian.HamiltonianRydberg(graph,
                                                              code=rydberg,
                                                              detuning=1,
                                                              energy=0,
                                                              IS_subspace=True)
    # Initialize spontaneous emission
    spontaneous_emission_rate = gamma
    spontaneous_emission = lindblad_operators.SpontaneousEmission(
        transition=(1, 2),
        rate=spontaneous_emission_rate,
        code=rydberg,
        IS_subspace=True,
        graph=graph)
    strong_spontaneous_emission_rate = (1, 1)
    strong_spontaneous_emission = lindblad_operators.StrongSpontaneousEmission(
        transition=(0, 2),
        rates=strong_spontaneous_emission_rate,
        code=rydberg,
        IS_subspace=True,
        graph=graph)

    def schedule2(t, tf):
        return [[],
                [(2 * t / tf / np.sqrt(spontaneous_emission_rate),
                  2 * (tf - t) / tf / np.sqrt(spontaneous_emission_rate))]]

    # Initialize master equation
    master_equation = LindbladMasterEquation(
        hamiltonians=[laser2, laser1], jump_operators=[spontaneous_emission])
    # Begin with all qubits in the ground codes
    psi = np.zeros((rydberg_hamiltonian_cost.hamiltonian.shape[0], 1),
                   dtype=np.complex128)
    psi[-1, -1] = 1
    psi = tools.outer_product(psi, psi)

    # Integrate the master equation
    results1 = master_equation.run_ode_solver(
        psi, 0, tf, num_from_time(tf), schedule=lambda t: schedule1(t, tf))
    cost_function = [
        rydberg_hamiltonian_cost.cost_function(results1[i], is_ket=False) / mis
        for i in range(results1.shape[0])
    ]
    # Initialize master equation
    master_equation = LindbladMasterEquation(
        hamiltonians=[], jump_operators=[strong_spontaneous_emission])

    # Integrate the master equation
    results2 = master_equation.run_ode_solver(
        psi, 0, tf, num_from_time(tf), schedule=lambda t: schedule2(t, tf))
    cost_function_strong = [
        rydberg_hamiltonian_cost.cost_function(results2[i], is_ket=False) / mis
        for i in range(results2.shape[0])
    ]
    print(results2[-1])
    times = np.linspace(0, tf, num_from_time(tf))
    # Compute the fidelity of the results
    fidelities = [
        tools.fidelity(results1[i], results2[i])
        for i in range(results1.shape[0])
    ]
    plt.plot(times, fidelities, color='r', label='Fidelity')
    plt.plot(times,
             cost_function,
             color='teal',
             label='Rydberg EIT approximation ratio')
    plt.plot(times,
             cost_function_strong,
             color='y',
             linestyle=':',
             label='Effective operator approximation ratio')

    plt.hlines(1, 0, max(times), linestyles=':', colors='k')
    plt.legend(loc='lower right')
    plt.ylim(0, 1.03)
    plt.xlabel(r'Annealing time $t$')
    plt.ylabel(r'Approximation ratio')
    plt.show()
Пример #26
0
    def __init__(self,
                 omega_g,
                 omega_r,
                 rates=(1, ),
                 graph: Graph = None,
                 IS_subspace=True,
                 code=qubit):
        self.omega_g = omega_g
        self.omega_r = omega_r

        self.IS_subspace = IS_subspace
        self.transition = (0, 1)
        self.graph = graph
        # Construct jump operators
        if self.IS_subspace:
            # Generate sparse mixing Hamiltonian
            assert graph is not None
            assert isinstance(graph, Graph)
            if code is not qubit:
                IS, num_IS = graph.independent_sets_qudit(self.code)
            else:
                # We have already solved for this information
                IS, num_IS = graph.independent_sets, graph.num_independent_sets
            self._jump_operators_rg = []
            self._jump_operators_gg = []
            # For each atom, consider the states spontaneous emission can generate transitions between
            # Over-allocate space
            for j in range(graph.n):
                rows_rg = np.zeros(num_IS, dtype=int)
                columns_rg = np.zeros(num_IS, dtype=int)
                entries_rg = np.zeros(num_IS, dtype=int)
                rows_gg = np.zeros(num_IS, dtype=int)
                columns_gg = np.zeros(num_IS, dtype=int)
                entries_gg = np.zeros(num_IS, dtype=int)
                num_terms_gg = 0
                num_terms_rg = 0
                for i in range(IS.shape[0]):
                    if IS[i, j] == self.transition[0]:
                        # Flip spin at this location
                        # Get binary representation
                        temp = IS[i].copy()
                        temp[j] = self.transition[1]
                        where_matched = (np.argwhere(
                            np.sum(np.abs(IS - temp), axis=1) == 0).flatten())
                        if len(where_matched) > 0:
                            # This is a valid spin flip
                            rows_rg[num_terms_rg] = where_matched[0]
                            columns_rg[num_terms_rg] = i
                            entries_rg[num_terms_rg] = 1
                            num_terms_rg += 1
                    elif IS[i, j] == self.transition[1]:
                        rows_gg[num_terms_gg] = i
                        columns_gg[num_terms_gg] = i
                        entries_gg[num_terms_gg] = 1
                        num_terms_gg += 1

                # Cut off the excess in the arrays
                columns_rg = columns_rg[:num_terms_rg]
                rows_rg = rows_rg[:num_terms_rg]
                entries_rg = entries_rg[:num_terms_rg]
                columns_gg = columns_gg[:num_terms_gg]
                rows_gg = rows_gg[:num_terms_gg]
                entries_gg = entries_gg[:num_terms_gg]
                # Now, append the jump operator
                jump_operator_rg = sparse.csc_matrix(
                    (entries_rg, (rows_rg, columns_rg)),
                    shape=(num_IS, num_IS))
                jump_operator_gg = sparse.csc_matrix(
                    (entries_gg, (rows_gg, columns_gg)),
                    shape=(num_IS, num_IS))

                self._jump_operators_rg.append(jump_operator_rg)
                self._jump_operators_gg.append(jump_operator_gg)
            self._jump_operators_rg = np.asarray(self._jump_operators_rg)
            self._jump_operators_gg = np.asarray(self._jump_operators_gg)
        else:
            # self._jump_operators_rg = []
            # self._jump_operators_gg = []
            op_rg = np.array([[[0, 0], [1, 0]]])
            op_gg = np.array([[[0, 0], [0, 1]]])
            self._jump_operators_rg = op_rg
            self._jump_operators_gg = op_gg

        super().__init__(None,
                         rates=rates,
                         graph=graph,
                         IS_subspace=IS_subspace,
                         code=code)
Пример #27
0
    def __init__(self,
                 omega_g,
                 omega_r,
                 energies=(1, ),
                 graph: Graph = None,
                 IS_subspace=True,
                 code=qubit):
        # Just need to define self.hamiltonian
        assert IS_subspace
        self.energies = energies
        self.IS_subspace = IS_subspace
        self.graph = graph
        self.omega_r = omega_r
        self.omega_g = omega_g
        self.code = code
        assert self.code is qubit

        if self.IS_subspace:
            # Generate sparse mixing Hamiltonian
            assert graph is not None
            assert isinstance(graph, Graph)
            if code is not qubit:
                IS, num_IS = graph.independent_sets_qudit(self.code)
            else:
                # We have already solved for this information
                IS, num_IS = graph.independent_sets, graph.num_independent_sets
            self.transition = (0, 1)
            self._hamiltonian_rr = np.zeros((num_IS, num_IS))
            self._hamiltonian_gg = np.zeros((num_IS, num_IS))
            self._hamiltonian_cross_terms = np.zeros((num_IS, num_IS))
            for k in range(IS.shape[0]):
                self._hamiltonian_rr[k, k] = np.sum(
                    IS[k][1] == self.transition[0])
                self._hamiltonian_gg[k, k] = np.sum(
                    IS[k][1] == self.transition[1])
            self._csc_hamiltonian_rr = sparse.csc_matrix(self._hamiltonian_rr)
            self._csc_hamiltonian_gg = sparse.csc_matrix(self._hamiltonian_gg)
            # For each IS, look at spin flips generated by the laser
            # Over-allocate space
            rows = np.zeros(graph.n * num_IS, dtype=int)
            columns = np.zeros(graph.n * num_IS, dtype=int)
            entries = np.zeros(graph.n * num_IS, dtype=float)
            num_terms = 0
            for i in range(graph.num_independent_sets):
                for j in range(graph.n):
                    if IS[i, j] == self.transition[1]:
                        # Flip spin at this location
                        # Get binary representation
                        temp = IS[i].copy()
                        temp[j] = self.transition[0]
                        where_matched = (np.argwhere(
                            np.sum(np.abs(IS - temp), axis=1) == 0).flatten())
                        if len(where_matched) > 0:
                            # This is a valid spin flip
                            rows[num_terms] = where_matched[0]
                            columns[num_terms] = i
                            entries[num_terms] = 1
                            num_terms += 1
            # Cut off the excess in the arrays
            columns = columns[:2 * num_terms]
            rows = rows[:2 * num_terms]
            entries = entries[:2 * num_terms]
            # Populate the second half of the entries according to self.pauli
            columns[num_terms:2 * num_terms] = rows[:num_terms]
            rows[num_terms:2 * num_terms] = columns[:num_terms]
            entries[num_terms:2 * num_terms] = entries[:num_terms]
            # Now, construct the Hamiltonian
            self._csc_hamiltonian_cross_terms = sparse.csc_matrix(
                (entries, (rows, columns)), shape=(num_IS, num_IS))
            self._hamiltonian_cross_terms = self._csc_hamiltonian_cross_terms

        else:
            # We are not in the IS subspace
            pass
Пример #28
0
    def test_logical_codes(self):
        # Construct a known graph
        G = nx.random_regular_graph(1, 2)
        for e in G.edges:
            G[e[0]][e[1]]['weight'] = 1
        nx.draw_networkx(G)
        # Uncomment to visualize graph
        # plt.draw_graph(G)
        G = Graph(G)
        print('No logical encoding:')
        hc_qubit = hamiltonian.HamiltonianMIS(G)
        hamiltonians = [hc_qubit, hamiltonian.HamiltonianDriver()]
        sim = qaoa.SimulateQAOA(G,
                                cost_hamiltonian=hc_qubit,
                                hamiltonian=hamiltonians,
                                noise_model=None,
                                noise=noises)
        # Set the default variational operators
        results = sim.find_parameters_brute(n=10)
        self.assertTrue(np.isclose(results['approximation_ratio'], 1))

        print('Two qubit code:')
        hc_two_qubit_code = hamiltonian.HamiltonianMIS(G, code=two_qubit_code)
        hamiltonians = [
            hc_two_qubit_code,
            hamiltonian.HamiltonianDriver(code=two_qubit_code)
        ]

        sim_code = qaoa.SimulateQAOA(G,
                                     code=two_qubit_code,
                                     cost_hamiltonian=hc_two_qubit_code,
                                     hamiltonian=hamiltonians)

        # Find optimal parameters via brute force search
        sim_code.find_parameters_brute(n=10)
        self.assertTrue(np.isclose(results['approximation_ratio'], 1))

        print('Two qubit code with penalty:')
        # Set the default variational operators with a penalty Hamiltonian
        hc_qubit = hamiltonian.HamiltonianMIS(G, code=two_qubit_code)
        hamiltonians = [
            hc_qubit,
            hamiltonian.HamiltonianBookatzPenalty(code=two_qubit_code),
            hamiltonian.HamiltonianDriver(code=two_qubit_code)
        ]
        sim_penalty = qaoa.SimulateQAOA(G,
                                        cost_hamiltonian=hc_qubit,
                                        hamiltonian=hamiltonians,
                                        code=two_qubit_code)
        # You should get the same thing
        results = sim_penalty.find_parameters_brute(n=10)
        self.assertTrue(np.isclose(results['approximation_ratio'], 1))

        print('Jordan-Farhi-Shor code:')
        hc_jordan_farhi_shor = hamiltonian.HamiltonianMIS(
            G, code=jordan_farhi_shor)
        hamiltonians = [
            hc_jordan_farhi_shor,
            hamiltonian.HamiltonianDriver(code=jordan_farhi_shor)
        ]

        sim_code = qaoa.SimulateQAOA(G,
                                     code=jordan_farhi_shor,
                                     cost_hamiltonian=hc_jordan_farhi_shor,
                                     hamiltonian=hamiltonians)

        sim_code.find_parameters_brute(n=10)
        self.assertTrue(np.isclose(results['approximation_ratio'], 1))