def pegasus_layout(G, scale=1., center=None, dim=2, crosses=False): """Positions the nodes of graph G in a Pegasus topology. NumPy (http://scipy.org) is required for this function. Parameters ---------- G : NetworkX graph Should be a Pegasus graph or a subgraph of a Pegasus graph. This should be the product of dwave_networkx.pegasus_graph scale : float (default 1.) Scale factor. When scale = 1, all positions fit within [0, 1] on the x-axis and [-1, 0] on the y-axis. center : None or array (default None) Coordinates of the top left corner. dim : int (default 2) Number of dimensions. When dim > 2, all extra dimensions are set to 0. crosses: boolean (optional, default False) If crosses is True, K_4,4 subgraphs are shown in a cross rather than L configuration. Ignored if G was defined with nice_coordinates=True. Returns ------- pos : dict A dictionary of positions keyed by node. Examples -------- >>> G = dnx.pegasus_graph(1) >>> pos = dnx.pegasus_layout(G) """ if not isinstance(G, nx.Graph) or G.graph.get("family") != "pegasus": raise ValueError("G must be generated by dwave_networkx.pegasus_graph") if G.graph.get('labels') == 'nice': m = 3*(G.graph['rows']-1) c_coords = chimera_node_placer_2d(m, m, 4, scale=scale, center=center, dim=dim) def xy_coords(t, y, x, u, k): return c_coords(3*y+2-t, 3*x+t, u, k) pos = {v: xy_coords(*v) for v in G.nodes()} else: xy_coords = pegasus_node_placer_2d(G, scale, center, dim, crosses=crosses) if G.graph.get('labels') == 'coordinate': pos = {v: xy_coords(*v) for v in G.nodes()} elif G.graph.get('data'): pos = {v: xy_coords(*dat['pegasus_index']) for v, dat in G.nodes(data=True)} else: m = G.graph.get('rows') coord = pegasus_coordinates(m) pos = {v: xy_coords(*coord.tuple(v)) for v in G.nodes()} return pos
def pegasus_elimination_order(n, coordinates=False): """Provides a variable elimination order for the Pegasus graph. The treewidth of a Pegasus graph ``pegasus_graph(n)`` is lower-bounded by :math:`12n-11` and upper bounded by :math:`12n-4` [bbrr]_ . Simple pegasus variable elimination order rules: - eliminate vertical qubits, one column at a time - eliminate horizontal qubits in each column once their adjacent vertical qubits have been eliminated Args ---- n : int The size parameter for the Pegasus lattice. coordinates : bool, optional (default False) If True, the elimination order is given in terms of 4-term Pegasus coordinates, otherwise given in linear indices. Returns ------- order : list An elimination order that provides an upper bound on the treewidth. .. [bbrr] Boothby, K., P. Bunky, J. Raymond, A. Roy. Next-Generation Topology of D-Wave Quantum Processors. Technical Report, Februrary 2019. https://www.dwavesys.com/resources/publications?type=white """ m = n l = 12 # ordering for horizontal qubits in each tile, from east to west: h_order = [4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11] order = [] for n_i in range(n): # for each tile offset # eliminate vertical qubits: for l_i in range(0, l, 2): for l_v in range(l_i, l_i + 2): for m_i in range(m - 1): # for each column order.append((0, n_i, l_v, m_i)) # eliminate horizontal qubits: if n_i > 0 and not(l_i % 4): # a new set of horizontal qubits have had all their neighbouring vertical qubits eliminated. for m_i in range(m): for l_h in range(h_order[l_i], h_order[l_i] + 4): order.append((1, m_i, l_h, n_i - 1)) if coordinates: return order else: return list(pegasus_coordinates(n).iter_pegasus_to_linear(order))
def _pegasus_fragment_helper(m=None, target_graph=None): # This is a function that takes m or a target_graph and produces a # `processor` object for the corresponding Pegasus graph, and a function # that translates embeddings produced by that object back to the original # pegasus graph. Consumed by `find_clique_embedding` and # `find_biclique_embedding`. # Organize parameter values if target_graph is None: if m is None: raise TypeError("m and target_graph cannot both be None.") target_graph = pegasus_graph(m) m = target_graph.graph['rows'] # We only support square Pegasus graphs # Deal with differences in ints vs coordinate target_graphs if target_graph.graph['labels'] == 'nice': back_converter = pegasus_coordinates.pegasus_to_nice back_translate = lambda embedding: { key: [back_converter(p) for p in chain] for key, chain in embedding.items() } elif target_graph.graph['labels'] == 'int': # Convert nodes in terms of Pegasus coordinates coord_converter = pegasus_coordinates(m) # A function to convert our final coordinate embedding to an ints embedding back_translate = lambda embedding: { key: list(coord_converter.iter_pegasus_to_linear(chain)) for key, chain in embedding.items() } else: back_translate = lambda embedding: embedding # collect edges of the graph produced by splitting each Pegasus qubit into six pieces fragment_edges = list(fragmented_edges(target_graph)) # Find clique embedding in K2,2 Chimera graph embedding_processor = processor(fragment_edges, M=m * 6, N=m * 6, L=2, linear=False) # Convert chimera fragment embedding in terms of Pegasus coordinates defragment_tuple = get_tuple_defragmentation_fn(target_graph) def embedding_to_pegasus(nodes, emb): emb = map(defragment_tuple, emb) emb = dict(zip(nodes, emb)) emb = back_translate(emb) return emb return embedding_processor, embedding_to_pegasus
def test_consistent_linear_nice_pegasus(self): P4 = dnx.pegasus_graph(4, nice_coordinates=True) coords = pegasus_coordinates(4) # `.*_to_*` methods for n, data in P4.nodes(data=True): p = data['pegasus_index'] i = data['linear_index'] self.assertEqual(coords.nice_to_pegasus(n), p) self.assertEqual(coords.pegasus_to_nice(p), n) self.assertEqual(coords.nice_to_linear(n), i) self.assertEqual(coords.linear_to_nice(i), n) self.assertEqual(coords.linear_to_pegasus(i), p) self.assertEqual(coords.pegasus_to_linear(p), i) # `.iter_*_to_*` methods nlist, plist, ilist = zip(*((n, d['pegasus_index'], d['linear_index']) for n, d in P4.nodes(data=True))) self.assertEqual(tuple(coords.iter_nice_to_pegasus(nlist)), plist) self.assertEqual(tuple(coords.iter_pegasus_to_nice(plist)), nlist) self.assertEqual(tuple(coords.iter_nice_to_linear(nlist)), ilist) self.assertEqual(tuple(coords.iter_linear_to_nice(ilist)), nlist) self.assertEqual(tuple(coords.iter_pegasus_to_linear(plist)), ilist) self.assertEqual(tuple(coords.iter_linear_to_pegasus(ilist)), plist) # `.iter_*_to_*_pairs` methods nedgelist = [] pedgelist = [] iedgelist = [] for u, v in P4.edges: nedgelist.append((u, v)) pedgelist.append((P4.nodes[u]['pegasus_index'], P4.nodes[v]['pegasus_index'])) iedgelist.append((P4.nodes[u]['linear_index'], P4.nodes[v]['linear_index'])) self.assertEqual(list(coords.iter_nice_to_pegasus_pairs(nedgelist)), pedgelist) self.assertEqual(list(coords.iter_pegasus_to_nice_pairs(pedgelist)), nedgelist) self.assertEqual(list(coords.iter_nice_to_linear_pairs(nedgelist)), iedgelist) self.assertEqual(list(coords.iter_linear_to_nice_pairs(iedgelist)), nedgelist) self.assertEqual(list(coords.iter_pegasus_to_linear_pairs(pedgelist)), iedgelist) self.assertEqual(list(coords.iter_linear_to_pegasus_pairs(iedgelist)), pedgelist)
def pegasus_layout(G, scale=1., center=None, dim=2): """Positions the nodes of graph G in a Pegasus topology. NumPy (http://scipy.org) is required for this function. Parameters ---------- G : NetworkX graph Should be a Pegasus graph or a subgraph of a Pegasus graph. This should be the product of dwave_networkx.pegasus_graph scale : float (default 1.) Scale factor. When scale = 1, all positions fit within [0, 1] on the x-axis and [-1, 0] on the y-axis. center : None or array (default None) Coordinates of the top left corner. dim : int (default 2) Number of dimensions. When dim > 2, all extra dimensions are set to 0. Returns ------- pos : dict A dictionary of positions keyed by node. Examples -------- >>> G = dnx.pegasus_graph(1) >>> pos = dnx.pegasus_layout(G) """ if not isinstance(G, nx.Graph) or G.graph.get("family") != "pegasus": raise ValueError("G must be generated by dwave_networkx.pegasus_graph") xy_coords = pegasus_node_placer_2d(G, scale, center, dim) if G.graph.get('labels') == 'coordinate': pos = {v: xy_coords(*v) for v in G.nodes()} elif G.graph.get('data'): pos = { v: xy_coords(*dat['pegasus_index']) for v, dat in G.nodes(data=True) } else: m = G.graph.get('rows') coord = pegasus_coordinates(m) pos = {v: xy_coords(*coord.tuple(v)) for v in G.nodes()} return pos
def test_coordinate_subgraphs(self): from dwave_networkx.generators.pegasus import pegasus_coordinates from random import sample G = dnx.pegasus_graph(4) H = dnx.pegasus_graph(4, coordinates=True) coords = pegasus_coordinates(4) lmask = sample(list(G.nodes()), G.number_of_nodes() // 2) cmask = list(coords.tuples(lmask)) self.assertEqual(lmask, list(coords.ints(cmask))) Gm = dnx.pegasus_graph(4, node_list=lmask) Hm = dnx.pegasus_graph(4, node_list=cmask, coordinates=True) Gs = G.subgraph(lmask) Hs = H.subgraph(cmask) EG = sorted(map(sorted, Gs.edges())) EH = sorted(map(sorted, Hs.edges())) self.assertEqual(EG, sorted(map(sorted, Gm.edges()))) self.assertEqual(EH, sorted(map(sorted, Hm.edges()))) Gn = dnx.pegasus_graph(4, edge_list=EG) Hn = dnx.pegasus_graph(4, edge_list=EH, coordinates=True) Gnodes = Gn.nodes Hnodes = Hn.nodes for v in Gnodes: q = Gnodes[v]['pegasus_index'] self.assertIn(q, Hnodes) self.assertEqual(Hnodes[q]['linear_index'], v) self.assertEqual(v, coords.int(q)) self.assertEqual(q, coords.tuple(v)) for q in Hnodes: v = Hnodes[q]['linear_index'] self.assertIn(v, Gnodes) self.assertEqual(Gnodes[v]['pegasus_index'], q) self.assertEqual(v, coords.int(q)) self.assertEqual(q, coords.tuple(v)) self.assertEqual(EG, sorted(map(sorted, coords.int_pairs(Hn.edges())))) self.assertEqual(EH, sorted(map(sorted, coords.tuple_pairs(Gn.edges()))))
def test_coordinate_basics(self): G = dnx.pegasus_graph(4, fabric_only=False) H = dnx.pegasus_graph(4, coordinates=True, fabric_only=False) coords = pegasus_coordinates(4) Gnodes = G.nodes Hnodes = H.nodes for v in Gnodes: q = Gnodes[v]['pegasus_index'] self.assertIn(q, Hnodes) self.assertEqual(Hnodes[q]['linear_index'], v) self.assertEqual(v, coords.pegasus_to_linear(q)) self.assertEqual(q, coords.linear_to_pegasus(v)) for q in Hnodes: v = Hnodes[q]['linear_index'] self.assertIn(v, Gnodes) self.assertEqual(Gnodes[v]['pegasus_index'], q) self.assertEqual(v, coords.pegasus_to_linear(q)) self.assertEqual(q, coords.linear_to_pegasus(v))
def test_coordinate_basics(self): from dwave_networkx.generators.pegasus import pegasus_coordinates G = dnx.pegasus_graph(4, fabric_only=False) H = dnx.pegasus_graph(4, coordinates=True, fabric_only=False) coords = pegasus_coordinates(4) Gnodes = G.nodes Hnodes = H.nodes for v in Gnodes: q = Gnodes[v]['pegasus_index'] self.assertIn(q, Hnodes) self.assertEqual(Hnodes[q]['linear_index'], v) self.assertEqual(v, coords.int(q)) self.assertEqual(q, coords.tuple(v)) for q in Hnodes: v = Hnodes[q]['linear_index'] self.assertIn(v, Gnodes) self.assertEqual(Gnodes[v]['pegasus_index'], q) self.assertEqual(v, coords.int(q)) self.assertEqual(q, coords.tuple(v))
def test_coordinate_subgraphs(self): G = dnx.pegasus_graph(4) H = dnx.pegasus_graph(4, coordinates=True) coords = pegasus_coordinates(4) lmask = sample(list(G.nodes()), G.number_of_nodes()//2) cmask = list(coords.iter_linear_to_pegasus(lmask)) self.assertEqual(lmask, list(coords.iter_pegasus_to_linear(cmask))) Gm = dnx.pegasus_graph(4, node_list=lmask) Hm = dnx.pegasus_graph(4, node_list=cmask, coordinates=True) Gs = G.subgraph(lmask) Hs = H.subgraph(cmask) EG = sorted(map(sorted, Gs.edges())) EH = sorted(map(sorted, Hs.edges())) self.assertEqual(EG, sorted(map(sorted, Gm.edges()))) self.assertEqual(EH, sorted(map(sorted, Hm.edges()))) Gn = dnx.pegasus_graph(4, edge_list=EG) Hn = dnx.pegasus_graph(4, edge_list=EH, coordinates=True) Gnodes = Gn.nodes Hnodes = Hn.nodes for v in Gnodes: q = Gnodes[v]['pegasus_index'] self.assertIn(q, Hnodes) self.assertEqual(Hnodes[q]['linear_index'], v) self.assertEqual(v, coords.pegasus_to_linear(q)) self.assertEqual(q, coords.linear_to_pegasus(v)) for q in Hnodes: v = Hnodes[q]['linear_index'] self.assertIn(v, Gnodes) self.assertEqual(Gnodes[v]['pegasus_index'], q) self.assertEqual(v, coords.pegasus_to_linear(q)) self.assertEqual(q, coords.linear_to_pegasus(v)) self.assertEqual(EG, sorted(map(sorted, coords.iter_pegasus_to_linear_pairs(Hn.edges())))) self.assertEqual(EH, sorted(map(sorted, coords.iter_linear_to_pegasus_pairs(Gn.edges()))))
def find_clique_embedding(k, m=None, target_graph=None): """Find an embedding of a k-sized clique on a Pegasus graph (target_graph). This clique is found by transforming the Pegasus graph into a K2,2 Chimera graph and then applying a Chimera clique finding algorithm. The results are then converted back in terms of Pegasus coordinates. Note: If target_graph is None, m will be used to generate a m-by-m Pegasus graph. Hence m and target_graph cannot both be None. Args: k (int/iterable/:obj:`networkx.Graph`): Number of members in the requested clique; list of nodes; a complete graph that you want to embed onto the target_graph m (int): Number of tiles in a row of a square Pegasus graph target_graph (:obj:`networkx.Graph`): A Pegasus graph Returns: dict: A dictionary representing target_graphs's clique embedding. Each dictionary key represents a node in said clique. Each corresponding dictionary value is a list of pegasus coordinates that should be chained together to represent said node. """ # Organize parameter values if target_graph is None: if m is None: raise TypeError("m and target_graph cannot both be None.") target_graph = pegasus_graph(m) m = target_graph.graph['rows'] # We only support square Pegasus graphs _, nodes = k # Deal with differences in ints vs coordinate target_graphs if target_graph.graph['labels'] == 'nice': fwd_converter = get_nice_to_pegasus_fn(m=m) back_converter = get_pegasus_to_nice_fn(m=m) pegasus_coords = [fwd_converter(*p) for p in target_graph.nodes] back_translate = lambda embedding: { key: [back_converter(*p) for p in chain] for key, chain in embedding.items() } elif target_graph.graph['labels'] == 'int': # Convert nodes in terms of Pegasus coordinates coord_converter = pegasus_coordinates(m) pegasus_coords = map(coord_converter.tuple, target_graph.nodes) # A function to convert our final coordinate embedding to an ints embedding back_translate = lambda embedding: { key: list(coord_converter.ints(chain)) for key, chain in embedding.items() } else: pegasus_coords = target_graph.nodes back_translate = lambda embedding: embedding # Break each Pegasus qubits into six Chimera fragments # Note: By breaking the graph in this way, you end up with a K2,2 Chimera graph fragment_tuple = get_tuple_fragmentation_fn(target_graph) fragments = fragment_tuple(pegasus_coords) # Create a K2,2 Chimera graph # Note: 6 * m because Pegasus qubits split into six pieces, so the number of rows and columns # get multiplied by six chim_m = 6 * m chim_graph = chimera_graph(chim_m, t=2, coordinates=True) # Determine valid fragment couplers in a K2,2 Chimera graph edges = chim_graph.subgraph(fragments).edges() # Find clique embedding in K2,2 Chimera graph embedding_processor = processor(edges, M=chim_m, N=chim_m, L=2, linear=False) chimera_clique_embedding = embedding_processor.tightestNativeClique( len(nodes)) # Convert chimera fragment embedding in terms of Pegasus coordinates defragment_tuple = get_tuple_defragmentation_fn(target_graph) pegasus_clique_embedding = map(defragment_tuple, chimera_clique_embedding) pegasus_clique_embedding = dict(zip(nodes, pegasus_clique_embedding)) pegasus_clique_embedding = back_translate(pegasus_clique_embedding) if len(pegasus_clique_embedding) != len(nodes): raise ValueError("No clique embedding found") return pegasus_clique_embedding
def pegasus_layout(G, scale=1., center=None, dim=2, crosses=False): """Positions the nodes of graph G in a Pegasus topology. `NumPy <http://scipy.org>`_ is required for this function. Parameters ---------- G : NetworkX graph A Pegasus graph or a subgraph of a Pegasus graph, as produced by the :func:`dwave_networkx.pegasus_graph` function. scale : float (default 1.) Scale factor. A setting of ``scale = 1`` fits all positions within [0, 1] on the x-axis and [-1, 0] on the y-axis. center : None or array (default None) Coordinates of the top left corner. dim : int (default 2) Number of dimensions. When dim > 2, all extra dimensions are set to 0. crosses: boolean (optional, default False) If True, :math:`K_{4,4}` subgraphs are shown in a cross rather than L configuration. Ignored if G is defined with ``nice_coordinates=True``. Returns ------- pos : dict Positions as a dictionary keyed by node. Examples -------- This example gives the positions of a Pegasus lattice of size 2. >>> G = dnx.pegasus_graph(2) >>> pos = dnx.pegasus_layout(G) """ if not isinstance(G, nx.Graph) or G.graph.get("family") != "pegasus": raise ValueError("G must be generated by dwave_networkx.pegasus_graph") if G.graph.get('labels') == 'nice': m = 3*(G.graph['rows']-1) c_coords = chimera_node_placer_2d(m, m, 4, scale=scale, center=center, dim=dim) def xy_coords(t, y, x, u, k): return c_coords(3*y+2-t, 3*x+t, u, k) pos = {v: xy_coords(*v) for v in G.nodes()} else: xy_coords = pegasus_node_placer_2d(G, scale, center, dim, crosses=crosses) if G.graph.get('labels') == 'coordinate': pos = {v: xy_coords(*v) for v in G.nodes()} elif G.graph.get('data'): pos = {v: xy_coords(*dat['pegasus_index']) for v, dat in G.nodes(data=True)} else: m = G.graph.get('rows') coord = pegasus_coordinates(m) pos = {v: xy_coords(*coord.linear_to_pegasus(v)) for v in G.nodes()} return pos
def find_clique_embedding(k, m=None, target_graph=None): """Find an embedding for a clique in a Pegasus graph. Given a clique (fully connected graph) and target Pegasus graph, attempts to find an embedding by transforming the Pegasus graph into a :math:`K_{2,2}` Chimera graph and then applying a Chimera clique-finding algorithm. Results are converted back to Pegasus coordinates. Args: k (int/iterable/:obj:`networkx.Graph`): A complete graph to embed, formatted as a number of nodes, node labels, or a NetworkX graph. m (int): Number of tiles in a row of a square Pegasus graph. Required to generate an m-by-m Pegasus graph when `target_graph` is None. target_graph (:obj:`networkx.Graph`): A Pegasus graph. Required when `m` is None. Returns: dict: An embedding as a dict, where keys represent the clique's nodes and values, formatted as lists, represent chains of pegasus coordinates. Examples: This example finds an embedding for a :math:`K_3` complete graph in a 2-by-2 Pegaus graph. >>> from dwave.embedding.pegasus import find_clique_embedding ... >>> print(find_clique_embedding(3, 2)) # doctest: +SKIP {0: [10, 34], 1: [35, 11], 2: [32, 12]} """ # Organize parameter values if target_graph is None: if m is None: raise TypeError("m and target_graph cannot both be None.") target_graph = pegasus_graph(m) m = target_graph.graph['rows'] # We only support square Pegasus graphs _, nodes = k # Deal with differences in ints vs coordinate target_graphs if target_graph.graph['labels'] == 'nice': fwd_converter = get_nice_to_pegasus_fn() back_converter = get_pegasus_to_nice_fn() pegasus_coords = [fwd_converter(*p) for p in target_graph.nodes] back_translate = lambda embedding: {key: [back_converter(*p) for p in chain] for key, chain in embedding.items()} elif target_graph.graph['labels'] == 'int': # Convert nodes in terms of Pegasus coordinates coord_converter = pegasus_coordinates(m) pegasus_coords = map(coord_converter.tuple, target_graph.nodes) # A function to convert our final coordinate embedding to an ints embedding back_translate = lambda embedding: {key: list(coord_converter.ints(chain)) for key, chain in embedding.items()} else: pegasus_coords = target_graph.nodes back_translate = lambda embedding: embedding # Break each Pegasus qubits into six Chimera fragments # Note: By breaking the graph in this way, you end up with a K2,2 Chimera graph fragment_tuple = get_tuple_fragmentation_fn(target_graph) fragments = fragment_tuple(pegasus_coords) # Create a K2,2 Chimera graph # Note: 6 * m because Pegasus qubits split into six pieces, so the number of rows and columns # get multiplied by six chim_m = 6 * m chim_graph = chimera_graph(chim_m, t=2, coordinates=True) # Determine valid fragment couplers in a K2,2 Chimera graph edges = chim_graph.subgraph(fragments).edges() # Find clique embedding in K2,2 Chimera graph embedding_processor = processor(edges, M=chim_m, N=chim_m, L=2, linear=False) chimera_clique_embedding = embedding_processor.tightestNativeClique(len(nodes)) # Convert chimera fragment embedding in terms of Pegasus coordinates defragment_tuple = get_tuple_defragmentation_fn(target_graph) pegasus_clique_embedding = map(defragment_tuple, chimera_clique_embedding) pegasus_clique_embedding = dict(zip(nodes, pegasus_clique_embedding)) pegasus_clique_embedding = back_translate(pegasus_clique_embedding) if len(pegasus_clique_embedding) != len(nodes): raise ValueError("No clique embedding found") return pegasus_clique_embedding