def test_digraph_missing_edges(self): G1 = nx.DiGraph() G1.add_weighted_edges_from([ ('a', 'b', 0.5), ('b', 'a', 0.8), ('b', 'c', 1.0), ('c', 'b', 0.7), ('a', 'c', 2.0), ('c', 'a', 2.0), ]) Q1 = dnx.traveling_salesperson_qubo(G1, lagrange=10) G2 = nx.DiGraph() G2.add_weighted_edges_from([ ('a', 'b', 0.5), ('b', 'a', 0.8), ('c', 'b', 0.7), ('a', 'c', 2.0), ('c', 'a', 2.0), ]) # make sure that missing_edge_weight gets applied correctly Q2 = dnx.traveling_salesperson_qubo(G2, lagrange=10, missing_edge_weight=1.0) self.assertDictEqual(Q1, Q2)
def test_k4(self): # good routes are 0,1,2,3 or 3,2,1,0 (and their rotations) G = nx.Graph() G.add_weighted_edges_from([(0, 1, 1), (1, 2, 1), (2, 3, 1), (3, 0, 1), (0, 2, 2), (1, 3, 2)]) Q = dnx.traveling_salesperson_qubo(G, lagrange=10) bqm = dimod.BinaryQuadraticModel.from_qubo(Q) # good routes won't have 0<->2 or 1<->3 min_routes = [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (1, 2, 3, 0), (3, 2, 1, 0), (2, 1, 0, 3), (1, 0, 3, 2), (0, 3, 2, 1)] # get the min energy of the qubo sampleset = dimod.ExactSolver().sample(bqm) ground_energy = sampleset.first.energy # all possible routes are equally good for route in min_routes: sample = {v: 0 for v in bqm.variables} for idx, city in enumerate(route): sample[(city, idx)] = 1 self.assertAlmostEqual(bqm.energy(sample), ground_energy) # all min-energy solutions are valid routes ground_count = 0 for sample, energy in sampleset.data(['sample', 'energy']): if abs(energy - ground_energy) > .001: break ground_count += 1 self.assertEqual(ground_count, len(min_routes))
def test_k4_equal_weights(self): # k5 with all equal weights so all paths are equally good G = nx.Graph() G.add_weighted_edges_from( (u, v, .5) for u, v in itertools.combinations(range(4), 2)) Q = dnx.traveling_salesperson_qubo(G, lagrange=10) bqm = dimod.BinaryQuadraticModel.from_qubo(Q) # all routes are min weight min_routes = list(itertools.permutations(G.nodes)) # get the min energy of the qubo sampleset = dimod.ExactSolver().sample(bqm) ground_energy = sampleset.first.energy # all possible routes are equally good for route in min_routes: sample = {v: 0 for v in bqm.variables} for idx, city in enumerate(route): sample[(city, idx)] = 1 self.assertAlmostEqual(bqm.energy(sample), ground_energy) # all min-energy solutions are valid routes ground_count = 0 for sample, energy in sampleset.data(['sample', 'energy']): if abs(energy - ground_energy) > .001: break ground_count += 1 self.assertEqual(ground_count, len(min_routes))
def test_k3_bidirectional(self): G = nx.DiGraph() G.add_weighted_edges_from([('a', 'b', 0.5), ('b', 'a', 0.5), ('b', 'c', 1.0), ('c', 'b', 1.0), ('a', 'c', 2.0), ('c', 'a', 2.0)]) Q = dnx.traveling_salesperson_qubo(G, lagrange=10) bqm = dimod.BinaryQuadraticModel.from_qubo(Q) # all routes are min weight min_routes = list(itertools.permutations(G.nodes)) # get the min energy of the qubo sampleset = dimod.ExactSolver().sample(bqm) ground_energy = sampleset.first.energy # all possible routes are equally good for route in min_routes: sample = {v: 0 for v in bqm.variables} for idx, city in enumerate(route): sample[(city, idx)] = 1 self.assertAlmostEqual(bqm.energy(sample), ground_energy) # all min-energy solutions are valid routes ground_count = 0 for sample, energy in sampleset.data(['sample', 'energy']): if abs(energy - ground_energy) > .001: break ground_count += 1 self.assertEqual(ground_count, len(min_routes))
def test_docstring_size(self): # in the docstring we state the size of the resulting BQM, this checks # that for n in range(3, 20): G = nx.Graph() G.add_weighted_edges_from( (u, v, .5) for u, v in itertools.combinations(range(n), 2)) Q = dnx.traveling_salesperson_qubo(G) bqm = dimod.BinaryQuadraticModel.from_qubo(Q) self.assertEqual(len(bqm), n**2) self.assertEqual(len(bqm.quadratic), 2 * n * n * (n - 1))
def test_weighted_complete_graph(self): G = nx.Graph() G.add_weighted_edges_from({(0, 1, 1), (0, 2, 100), (0, 3, 1), (1, 2, 1), (1, 3, 100), (2, 3, 1)}) lagrange = 5.0 Q = dnx.traveling_salesperson_qubo(G, lagrange, 'weight') N = G.number_of_nodes() correct_sum = G.size( 'weight') * 2 * N - 2 * N * N * lagrange + 2 * N * N * ( N - 1) * lagrange actual_sum = sum(Q.values()) self.assertEqual(correct_sum, actual_sum)
def traveling_salesperson(G, sampler=None, lagrange=None, weight='weight', start=None, **sampler_args): # get lists with all cities list_cities = list(G.nodes()) # Get a QUBO representation of the problem Q = dnx.traveling_salesperson_qubo(G, lagrange, weight) # use the sampler to find low energy states response = sampler.sample_qubo(Q, **sampler_args) sample = response.first.sample # fill route with None values route = [None] * len(G) # get cities from sample # NOTE: Prevent duplicate city entries by enforcing only one occurrence per city along route for (city, time), val in sample.items(): if val and (city not in route): route[time] = city # run heuristic replacing None values if None in route: # get not assigned cities cities_unassigned = [city for city in list_cities if city not in route] cities_unassigned = list(np.random.permutation(cities_unassigned)) for idx, city in enumerate(route): if city == None: route[idx] = cities_unassigned[0] cities_unassigned.remove(route[idx]) # cycle solution to start at provided start location if start is not None and route[0] != start: # rotate to put the start in front idx = route.index(start) route = route[idx:] + route[:idx] return route
def test_exceptions(self): G = nx.Graph([(0, 1)]) with self.assertRaises(ValueError): dnx.traveling_salesperson_qubo(G)
def test_empty(self): Q = dnx.traveling_salesperson_qubo(nx.Graph()) self.assertEqual(Q, {})
def traveling_salesperson(G, sampler=None, lagrange=None, weight='weight', start=None, **sampler_args): """Returns an approximate minimum traveling salesperson route. Defines a QUBO with ground states corresponding to the minimum routes and uses the sampler to sample from it. A route is a cycle in the graph that reaches each node exactly once. A minimum route is a route with the smallest total edge weight. Parameters ---------- G : NetworkX graph The graph on which to find a minimum traveling salesperson route. This should be a complete graph with non-zero weights on every edge. sampler : A binary quadratic model sampler. A sampler is a process that samples from low energy states in models defined by an Ising equation or a Quadratic Unconstrained Binary Optimization Problem (QUBO). A sampler is expected to have a 'sample_qubo' and 'sample_ising' method. A sampler is expected to return an iterable of samples, in order of increasing energy. If no sampler is provided, one must be provided using the `set_default_sampler` function. lagrange : number, optional (default None) Lagrange parameter to weight constraints (visit every city once) versus objective (shortest distance route). weight : optional (default 'weight') The name of the edge attribute containing the weight. start : node, optional If provided, the route will begin at `start`. sampler_args : Additional keyword parameters are passed to the sampler. Returns ------- route : list List of nodes in order to be visited on a route Examples -------- >>> import dimod ... >>> G = nx.Graph() >>> G.add_weighted_edges_from({(0, 1, .1), (0, 2, .5), (0, 3, .1), (1, 2, .1), ... (1, 3, .5), (2, 3, .1)}) >>> dnx.traveling_salesperson(G, dimod.ExactSolver(), start=0) # doctest: +SKIP [0, 1, 2, 3] Notes ----- Samplers by their nature may not return the optimal solution. This function does not attempt to confirm the quality of the returned sample. """ # get lists with all cities list_cities = list(G.nodes()) # Get a QUBO representation of the problem Q = dnx.traveling_salesperson_qubo(G, lagrange, weight) # use the sampler to find low energy states response = sampler.sample_qubo(Q, **sampler_args) sample = response.first.sample # fill route with None values route = [None] * len(G) # get cities from sample # NOTE: Prevent duplicate city entries by enforcing only one occurrence per city along route for (city, time), val in sample.items(): if val and (city not in route): route[time] = city # run heuristic replacing None values if None in route: # get not assigned cities cities_unassigned = [city for city in list_cities if city not in route] cities_unassigned = list(np.random.permutation(cities_unassigned)) for idx, city in enumerate(route): if city == None: route[idx] = cities_unassigned[0] cities_unassigned.remove(route[idx]) # cycle solution to start at provided start location if start is not None and route[0] != start: # rotate to put the start in front idx = route.index(start) route = route[idx:] + route[:idx] return route
from dwave.system.samplers import DWaveSampler from dwave.system.composites import EmbeddingComposite import dimod import dwave_networkx as dnx ans = dnx.traveling_salesperson(G, sampler=dimod.ExactSolver(), start=0) print("ExactSolver [0 1 2 3] ") print(ans) #Calculated by Electronic computer # ------- Set up our QUBO dictionary ------- Q = defaultdict(int) # Initialize our Q matrix # Update Q matrix for every edge in the graph for i, j in G.edges: Q[(i, i)] += -1 Q[(j, j)] += -1 Q[(i, j)] += 2 # ------- Run our QUBO on the QPU ------- # Set up QPU parameters chainstrength = 1.0 numruns = 100 Q = dnx.traveling_salesperson_qubo(G) # Run the QUBO on the solver from your config file sampler = EmbeddingComposite(DWaveSampler()) d_answer = dnx.traveling_salesperson(G, sampler, start=0) print("QuantumSolver") print(d_answer ) #Calculated by Quantum Computer or Hybrid Server (Electronic+Quantum)