def test_evil_K_2_2(self): couplers = [((0, 0, 0, i), (1, 0, 0, i)) for i in range(4)] couplers += [((0, 0, 1, i), (0, 1, 1, i)) for i in range(4)] couplers += [((0, 0, 0, 0), (0, 0, 1, 0))] proc = processor(couplers, M=2, N=2, L=4, linear=False, proc_limit=2**16) emb = proc.tightestNativeBiClique(1, 1) verify_biclique(proc, emb, 1, 1, 1, 1) for _ in range(100): # this should be plenty proc = processor(couplers, M=2, N=2, L=4, linear=False, proc_limit=4) emb = proc.tightestNativeBiClique(1, 1) if emb is not None: break verify_biclique(proc, emb, 1, 1, 1, 1)
def setUp(self): M = 12 N = 7 L = 4 eden_qubits = [(x, y, u, k) for x in range(M) for y in range(N) for u in (0, 1) for k in range(L)] # one K_12, contains K_{8,8} Cliq1 = {(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (0, 2)} # another K_12 Cliq2 = {(1, 4), (0, 5), (0, 6), (1, 5), (1, 6), (2, 5)} # K_{12,16} after deleting all horizontal qubits in alternating rows Rect1 = {(x, y) for x in range(4, 7) for y in range(7)} kill1 = {} # deleting those alternating rows Rect2 = {(x, y) for x in range(4, 7) for y in range(1, 7, 2)} kill2 = {(0, 0), (0, 1), (0, 2), (0, 3)} # K_{12,12} after deleting all u=1,k=3 Rect3 = {(x, y) for x in range(8, 12) for y in range(3)} kill3 = {(1, 3)} # K_{8,16} Rect4 = {(x, y) for x in range(8, 12) for y in range(4, 6)} kill4 = {} XYFilter = Cliq1 | Cliq2 | Rect1 | Rect2 | Rect3 | Rect4 def xyfilt(tup): x, y, u, k = tup return (x, y) in XYFilter eden_qubits = filter(xyfilt, eden_qubits) for rect, kill in zip((Rect1, Rect2, Rect3, Rect4), (kill1, kill2, kill3, kill4)): def killf(tup): x, y, u, k = tup return ((x, y) not in rect) or ((u, k) not in kill) eden_qubits = filter(killf, eden_qubits) eden_qubits = list(eden_qubits) # need to run the function now eden_qubits = set(eden_qubits) eden_couplers = [ (q, n) for q in eden_qubits for n in set(_chimera_neighbors(M, N, L, q)) & eden_qubits ] eden_couplers.extend( ((x, y, 0, 0), (x, y, 1, 0)) for x, y in ((2, 2), (2, 3), (3, 3))) eden_couplers = [_bulk_to_linear(M, N, L, c) for c in eden_couplers] self.eden_proc = eden_proc = processor(eden_couplers, M=M, N=N, L=L) eden_proc._linear = False
def setUp(self): M = 6 N = 6 L = 4 eden_qubits = [(x, y, u, k) for x in range(M) for y in range(N) for u in (0, 1) for k in range(L)] dead_qubits = [(3, 3, 1, 2), (4, 3, 0, 0), (4, 3, 1, 0), (5, 3, 1, 0), (3, 4, 0, 1), (3, 4, 1, 0), (3, 4, 1, 2), (4, 4, 0, 0), (4, 4, 1, 1), (3, 5, 0, 0), (3, 5, 0, 1), (3, 5, 0, 2)] XYFilter = [(0, 2), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4), (5, 0), (5, 1), (5, 2), (5, 3)] def xyfilt(tup): x, y, u, k = tup return (x, y) in XYFilter eden_qubits = filter(xyfilt, set(eden_qubits) - set(dead_qubits)) eden_qubits = set(eden_qubits) eden_couplers = [ (q, n) for q in eden_qubits for n in set(_chimera_neighbors(M, N, L, q)) & eden_qubits ] eden_couplers = [_bulk_to_linear(M, N, L, c) for c in eden_couplers] eden2_proc = processor(eden_couplers, M=M) eden2_proc._linear = False self.eden2_proc = eden2_proc
def find_clique_embedding(k, m, n=None, t=None, target_edges=None): """Find an embedding for a clique in a Chimera graph. Given a target :term:`Chimera` graph size, and a clique (fully connect graph), attempts to find an embedding. Args: k (int/iterable): Clique to embed. If k is an integer, generates an embedding for a clique of size k labelled [0,k-1]. If k is an iterable, generates an embedding for a clique of size len(k), where iterable k is the variable labels. m (int): Number of rows in the Chimera lattice. n (int, optional, default=m): Number of columns in the Chimera lattice. t (int, optional, default 4): Size of the shore within each Chimera tile. target_edges (iterable[edge]): A list of edges in the target Chimera graph. Nodes are labelled as returned by :func:`~dwave_networkx.generators.chimera_graph`. Returns: dict: An embedding mapping a clique to the Chimera lattice. Examples: The first example finds an embedding for a :math:`K_4` complete graph in a single Chimera unit cell. The second for an alphanumerically labeled :math:`K_3` graph in 4 unit cells. >>> from dwave.embedding.chimera import find_clique_embedding ... >>> embedding = find_clique_embedding(4, 1, 1) >>> embedding # doctest: +SKIP {0: [4, 0], 1: [5, 1], 2: [6, 2], 3: [7, 3]} >>> from dwave.embedding.chimera import find_clique_embedding ... >>> embedding = find_clique_embedding(['a', 'b', 'c'], m=2, n=2, t=4) >>> embedding # doctest: +SKIP {'a': [20, 16], 'b': [21, 17], 'c': [22, 18]} """ _, nodes = k m, n, t, target_edges = _chimera_input(m, n, t, target_edges) embedding = processor(target_edges, M=m, N=n, L=t).tightestNativeClique(len(nodes)) if not embedding: raise ValueError("cannot find a K{} embedding for given Chimera lattice".format(k)) return dict(zip(nodes, embedding))
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_qubits_and_couplers(self): M = N = L = 2 qubits = {(x, y, u, k) for x in range(M) for y in range(N) for u in range(2) for k in range(L)} couplers = [(p, q) for q in qubits for p in _chimera_neighbors(M, N, L, q)] proc = processor(couplers, M=M, N=N, L=L, linear=False)._proc0 for q in proc: assert proc[q] == set(_chimera_neighbors( M, N, L, q)), "Neighborhood is wrong!" qubits.remove(q) assert not qubits, "qubits are missing from proc"
def test_proclimit_cornercase(self): couplers = [((0, y, 1, i), (0, y + 1, 1, i)) for y in range(2) for i in range(4)] couplers += [((0, y, 1, i), (0, y, 0, j)) for y in range(3) for i in range(4) for j in range(4) if i != 0 or j != y] emb = None count = 0 while emb is None and count < 100: proc = processor(couplers, M=1, N=3, L=4, linear=False, proc_limit=2000) emb = proc.tightestNativeBiClique(3, 9, chain_imbalance=None) count += 1 verify_biclique(proc, emb, 3, 9, 3, 1)
def test_objectives(self): couplers = [((0, 0, 0, i), (1, 0, 0, i)) for i in range(4)] couplers += [((0, 0, 1, i), (0, 1, 1, i)) for i in range(4)] couplers += [((0, 0, 0, 0), (0, 0, 1, 0))] proc = processor(couplers, M=2, N=2, L=4, linear=False, proc_limit=2**16) empty = proc._subprocessor( proc._proc0) # an eden_processor with all qubits disabled emb = proc.tightestNativeBiClique(0, 0) proc._processors = [empty] + proc._processors + [empty] verify_biclique(proc, emb, 0, 0, 0, 0) empty.largestNativeBiClique = lambda *a, **k: (None, None) emb = proc.largestNativeBiClique() verify_biclique(proc, emb, 1, 1, 1, 1)
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, n=None, t=None, target_edges=None): """Find an embedding for a clique in a Chimera graph. Given the node labels or size of a clique (fully connected graph) and size or edges of the target :term:`Chimera` graph, attempts to find an embedding. Args: k (int/iterable): Clique to embed. If k is an integer, generates an embedding for a clique of size k labelled [0,k-1]. If k is an iterable of nodes, generates an embedding for a clique of size len(k) labelled for the given nodes. m (int): Number of rows in the Chimera lattice. n (int, optional, default=m): Number of columns in the Chimera lattice. t (int, optional, default 4): Size of the shore within each Chimera tile. target_edges (iterable[edge]): A list of edges in the target Chimera graph. Nodes are labelled as returned by :func:`~dwave_networkx.generators.chimera_graph`. Returns: dict: An embedding mapping a clique to the Chimera lattice. Examples: The first example finds an embedding for a :math:`K_4` complete graph in a single Chimera unit cell. The second for an alphanumerically labeled :math:`K_3` graph in 4 unit cells. >>> from dwave.embedding.chimera import find_clique_embedding ... >>> embedding = find_clique_embedding(4, 1, 1) >>> embedding # doctest: +SKIP {0: [4, 0], 1: [5, 1], 2: [6, 2], 3: [7, 3]} >>> from dwave.embedding.chimera import find_clique_embedding ... >>> embedding = find_clique_embedding(['a', 'b', 'c'], m=2, n=2, t=4) >>> embedding # doctest: +SKIP {'a': [20, 16], 'b': [21, 17], 'c': [22, 18]} """ import random _, nodes = k m, n, t, target_edges = _chimera_input(m, n, t, target_edges) # Special cases to return optimal embeddings for small k. The general clique embedder uses chains of length # at least 2, whereas cliques of size 1 and 2 can be embedded with single-qubit chains. if len(nodes) == 1: # If k == 1 we simply return a single chain consisting of a randomly sampled qubit. qubits = set().union(*target_edges) qubit = random.choice(tuple(qubits)) embedding = [[qubit]] elif len(nodes) == 2: # If k == 2 we simply return two one-qubit chains that are the endpoints of a randomly sampled coupler. if not isinstance(target_edges, abc.Sequence): target_edges = list(target_edges) edge = random.choice(target_edges) embedding = [[edge[0]], [edge[1]]] else: # General case for k > 2. embedding = processor(target_edges, M=m, N=n, L=t).tightestNativeClique(len(nodes)) if not embedding: raise ValueError("cannot find a K{} embedding for given Chimera lattice".format(len(nodes))) return dict(zip(nodes, embedding))
def find_biclique_embedding(a, b, m, n=None, t=None, target_edges=None): """Find an embedding for a biclique in a Chimera graph. Given a biclique (a bipartite graph where every vertex in a set in connected to all vertices in the other set) and a target :term:`Chimera` graph size or edges, attempts to find an embedding. Args: a (int/iterable): Left shore of the biclique to embed. If a is an integer, generates an embedding for a biclique with the left shore of size a labelled [0,a-1]. If a is an iterable of nodes, generates an embedding for a biclique with the left shore of size len(a) labelled for the given nodes. b (int/iterable): Right shore of the biclique to embed.If b is an integer, generates an embedding for a biclique with the right shore of size b labelled [0,b-1]. If b is an iterable of nodes, generates an embedding for a biclique with the right shore of size len(b) labelled for the given nodes. m (int): Number of rows in the Chimera lattice. n (int, optional, default=m): Number of columns in the Chimera lattice. t (int, optional, default 4): Size of the shore within each Chimera tile. target_edges (iterable[edge]): A list of edges in the target Chimera graph. Nodes are labelled as returned by :func:`~dwave_networkx.generators.chimera_graph`. Returns: tuple: A 2-tuple containing: dict: An embedding mapping the left shore of the biclique to the Chimera lattice. dict: An embedding mapping the right shore of the biclique to the Chimera lattice. Examples: This example finds an embedding for an alphanumerically labeled biclique in a single Chimera unit cell. >>> from dwave.embedding.chimera import find_biclique_embedding ... >>> left, right = find_biclique_embedding(['a', 'b', 'c'], ['d', 'e'], 1, 1) >>> print(left, right) # doctest: +SKIP {'a': [4], 'b': [5], 'c': [6]} {'d': [0], 'e': [1]} """ _, anodes = a _, bnodes = b m, n, t, target_edges = _chimera_input(m, n, t, target_edges) embedding = processor(target_edges, M=m, N=n, L=t).tightestNativeBiClique(len(anodes), len(bnodes)) if not embedding: raise ValueError("cannot find a K{},{} embedding for given Chimera lattice".format(a, b)) left, right = embedding return dict(zip(anodes, left)), dict(zip(bnodes, right))
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