def test_empty_list(self): # Set up defragmentation function pg = pegasus_graph(2) defragment_tuple = get_tuple_defragmentation_fn(pg) # De-fragment chimera coordinates chimera_coords = [] pegasus_coords = defragment_tuple(chimera_coords) self.assertEqual([], pegasus_coords)
def test_mixed_fragments(self): # Set up defragmenation function pg = pegasus_graph(8) defragment_tuple = get_tuple_defragmentation_fn(pg) # De-fragment chimera coordinates chimera_coords = [(17, 14, 0, 0), (22, 14, 0, 0), (24, 32, 1, 0), (1, 31, 0, 0)] pegasus_coords = defragment_tuple(chimera_coords) expected_pegasus_coords = {(0, 2, 4, 2), (1, 4, 0, 4), (0, 5, 2, 0)} self.assertEqual(expected_pegasus_coords, set(pegasus_coords))
def test_multiple_fragments_from_same_qubit(self): # Set up defragmentation function pg = pegasus_graph(3) defragment_tuple = get_tuple_defragmentation_fn(pg) # De-fragment chimera coordinates chimera_coords = [(9, 8, 1, 1), (9, 11, 1, 1)] pegasus_coords = defragment_tuple(chimera_coords) expected_pegasus_coords = [(1, 1, 7, 1)] self.assertEqual(expected_pegasus_coords, pegasus_coords)
def test_single_fragment(self): # Set up defragmentation function pg = pegasus_graph(4) defragment_tuple = get_tuple_defragmentation_fn(pg) # De-fragment chimera coordinates chimera_coords = [(3, 7, 0, 0)] pegasus_coords = defragment_tuple(chimera_coords) expected_pegasus_coords = [(0, 1, 2, 0)] self.assertEqual(expected_pegasus_coords, pegasus_coords)
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 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 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