def test_tile_around_edge_defects_pegasus(self): pegasus_shape = [5] # P5 structured sampler with one missing external edge that does not p # prevent tesselation of 2x2 blocks (12 tiles, equivalent to full yield) broken_edges_nice_coordinates = [(0, 1, 0, 0, 0), (0, 2, 0, 0, 0)] broken_edges = [ tuple( dnx.pegasus_coordinates(pegasus_shape[0]).nice_to_linear(coord) for coord in broken_edges_nice_coordinates) ] mock_sampler = MockDWaveSampler(topology_type='pegasus', topology_shape=pegasus_shape, broken_edges=broken_edges) sampler = TilingComposite(mock_sampler, 2, 2, 4) self.assertTrue(len(sampler.embeddings) == 12) # P5 structured sampler with one missing internal edge that prevents # tesselation of 2x2 blocks (otherwise 12 tiles, with edge defect 11) broken_edge_nice_coordinates = [(0, 0, 0, 0, 0), (0, 0, 0, 1, 0)] broken_edges = [ tuple( dnx.pegasus_coordinates(pegasus_shape[0]).nice_to_linear(coord) for coord in broken_edge_nice_coordinates) ] mock_sampler = MockDWaveSampler(topology_type='pegasus', topology_shape=pegasus_shape, broken_edges=broken_edges) sampler = TilingComposite(mock_sampler, 2, 2, 4) self.assertTrue(len(sampler.embeddings) == 11)
def test_sublattice_mappings(self): def check_subgraph_mapping(f, g, h): for v in g: if not h.has_node(f(v)): raise RuntimeError( f"node {v} mapped to {f(v)} is not in {h.graph['name']} ({h.graph['labels']})" ) for u, v in g.edges: if not h.has_edge(f(u), f(v)): raise RuntimeError( f"edge {(u, v)} mapped to {(f(u), f(v))} not present in {h.graph['name']} ({h.graph['labels']})" ) coords5 = dnx.pegasus_coordinates(5) coords3 = dnx.pegasus_coordinates(3) p3l = dnx.pegasus_graph(3) p3c = dnx.pegasus_graph(3, coordinates=True) p3n = coords3.graph_to_nice(p3c) p5l = dnx.pegasus_graph(5) p5c = dnx.pegasus_graph(5, coordinates=True) p5n = coords5.graph_to_nice(p5c) for target in p5l, p5c, p5n: for source in p3l, p3c, p3n: covered = set() for f in dnx.pegasus_sublattice_mappings(source, target): check_subgraph_mapping(f, source, target) covered.update(map(f, source)) self.assertEqual(covered, set(target)) c2l = dnx.chimera_graph(2) c2c = dnx.chimera_graph(2, coordinates=True) c23l = dnx.chimera_graph(2, 3) c32c = dnx.chimera_graph(3, 2, coordinates=True) p5n = dnx.pegasus_graph(5, nice_coordinates=True) p5l = coords5.graph_to_linear(p5n) p5c = coords5.graph_to_pegasus(p5n) for target in p5l, p5c, p5n: for source in c2l, c2c, c23l, c32c, target: covered = set() for f in dnx.pegasus_sublattice_mappings(source, target): check_subgraph_mapping(f, source, target) covered.update(map(f, source)) self.assertEqual(covered, set(target))
def test_tile_around_node_defects_pegasus(self): pegasus_shape = [5] # Create a pegasus P5 structured solver subject to node defects with the # following (nice-coordinate) 3x4x4 cell-level structure: # OOOX OOOO OOOO # OOOO OOOO OOOO # OOOO OOOO OOOO # OOOO OOOO OOOX # where O: complete cell, X: incomplete cell broken_node_nice_coordinates = [(0, 0, 3, 0, 1), (2, 3, 3, 1, 3)] broken_node_linear_coordinates = [ dnx.pegasus_coordinates(pegasus_shape[0]).nice_to_linear(coord) for coord in broken_node_nice_coordinates ] mock_sampler = MockDWaveSampler( topology_type='pegasus', topology_shape=pegasus_shape, broken_nodes=broken_node_linear_coordinates) # Tile with 2x2 cells: sampler = TilingComposite(mock_sampler, 2, 2, 4) # Given the above pegasus graph, check that the embeddings are as # follows: # 00XX 3344 7788 # 0011 3344 7788 # 2211 5566 99XX # 22XX 5566 99XX # Check correct number of embeddings and size of each is sufficient, # given chimera test checks detailed position: self.assertTrue(len(sampler.embeddings) == 10) self.assertFalse(any([len(emb) != 32 for emb in sampler.embeddings]))
def test_nice_coordinates(self): G = dnx.pegasus_graph(4, nice_coordinates=True) H = dnx.chimera_graph(3, coordinates=True) for p, q in H.edges(): for t in range(3): pg = (t, ) + p qg = (t, ) + q self.assertTrue(G.has_edge(pg, qg)) coords = dnx.pegasus_coordinates(4) n2p = coords.nice_to_pegasus p2n = coords.pegasus_to_nice n2l = coords.nice_to_linear l2n = coords.linear_to_nice for p in G.nodes(): self.assertEqual(p2n(n2p(p)), p) self.assertEqual(l2n(n2l(p)), p) self.assertTrue(H.has_node(p[1:])) G = dnx.pegasus_graph(4) for p in G.nodes(): self.assertEqual(n2l(l2n(p)), p) G = dnx.pegasus_graph(4, coordinates=True) for p in G.nodes(): self.assertEqual(n2p(p2n(p)), p)
def test_consistent_linear_nice_pegasus(self): P4 = dnx.pegasus_graph(4, nice_coordinates=True) coords = dnx.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 test_coordinate_subgraphs(self): G = dnx.pegasus_graph(4) H = dnx.pegasus_graph(4, coordinates=True) coords = dnx.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 __init__(self, *args, **kwargs): super(TestBusclique, self).__init__(*args, **kwargs) self.c16 = dnx.chimera_graph(16) self.p16 = dnx.pegasus_graph(16) self.c16c = dnx.chimera_graph(16, coordinates=True, data=False) self.c428 = dnx.chimera_graph(4, n=2, t=8) self.c248 = dnx.chimera_graph(2, n=4, t=8) self.c42A = dnx.chimera_graph(4, n=2, t=9) c4c_0 = subgraph_node_yield(dnx.chimera_graph(4, coordinates=True), .95) p4c_0 = subgraph_node_yield(dnx.pegasus_graph(4, coordinates=True), .95) c4c = [ c4c_0, subgraph_edge_yield(c4c_0, .95), subgraph_edge_yield_few_bad(c4c_0, .95, 6) ] p4c = [ p4c_0, subgraph_edge_yield(p4c_0, .95), subgraph_edge_yield_few_bad(p4c_0, .95, 6) ] p4coords = dnx.pegasus_coordinates(4) c4coords = dnx.chimera_coordinates(4, 4, 4) c4 = [ relabel(c, c4coords.iter_chimera_to_linear, c4coords.iter_chimera_to_linear_pairs, 4) for c in c4c ] p4 = [ relabel(p, p4coords.iter_pegasus_to_linear, p4coords.iter_pegasus_to_linear_pairs, 4) for p in p4c ] p4n = [ relabel(p, p4coords.iter_pegasus_to_nice, p4coords.iter_pegasus_to_nice_pairs, 4, nice_coordinates=True) for p in p4c ] self.c4, self.c4_nd, self.c4_d = list(zip(c4, c4c)) self.p4, self.p4_nd, self.p4_d = list(zip(p4, p4c, p4n))
def test_coordinate_basics(self): G = dnx.pegasus_graph(4, fabric_only=False) H = dnx.pegasus_graph(4, coordinates=True, fabric_only=False) coords = dnx.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_graph_relabeling(self): def graph_equal(g, h): self.assertEqual(set(g), set(h)) self.assertEqual(set(map(tuple, map(sorted, g.edges))), set(map(tuple, map(sorted, g.edges)))) for v, d in g.nodes(data=True): self.assertEqual(h.nodes[v], d) coords = dnx.pegasus_coordinates(3) nodes_nice = dnx.pegasus_graph(3, nice_coordinates=True) nodes_linear = list(coords.iter_nice_to_linear(nodes_nice)) nodes_pegasus = list(coords.iter_nice_to_pegasus(nodes_nice)) for data in True, False: p3l = dnx.pegasus_graph(3, data=data).subgraph(nodes_linear) p3p = dnx.pegasus_graph(3, data=data, coordinates=True).subgraph(nodes_pegasus) p3n = dnx.pegasus_graph(3, data=data, nice_coordinates=True) graph_equal(p3l, coords.graph_to_linear(p3l)) graph_equal(p3l, coords.graph_to_linear(p3p)) graph_equal(p3l, coords.graph_to_linear(p3n)) graph_equal(p3p, coords.graph_to_pegasus(p3l)) graph_equal(p3p, coords.graph_to_pegasus(p3p)) graph_equal(p3p, coords.graph_to_pegasus(p3n)) graph_equal(p3n, coords.graph_to_nice(p3l)) graph_equal(p3n, coords.graph_to_nice(p3p)) graph_equal(p3n, coords.graph_to_nice(p3n)) h = dnx.pegasus_graph(2) del h.graph['labels'] with self.assertRaises(ValueError): coords.graph_to_nice(h) with self.assertRaises(ValueError): coords.graph_to_linear(h) with self.assertRaises(ValueError): coords.graph_to_pegasus(h)
def _initialize_weights_pegasus(pegasus_graph, size, draw_inter_weight, draw_intra_weight, draw_other_weight): # 'nice' coordinate indices names (as per d-wave's documentation): p_coor = dwave.pegasus_coordinates(size) for _from, _to in pegasus_graph.edges: _from_nice = p_coor.linear_to_nice(_from) _to_nice = p_coor.linear_to_nice(_to) if not in_chimera_subgraph(_from_nice, _to_nice): pegasus_graph.add_edge(_from, _to, weight=draw_other_weight()) else: if in_same_chimera_tile(_from_nice, _to_nice): # edge from one side to the other (internal edge) if not on_same_side(_from_nice, _to_nice): pegasus_graph.add_edge(_from, _to, weight=draw_intra_weight()) else: # odd couplers pegasus_graph.add_edge(_from, _to, weight=draw_other_weight()) else: pegasus_graph.add_edge(_from, _to, weight=draw_inter_weight())
def __init__(self, sampler, sub_m, sub_n, t=4): self.parameters = sampler.parameters.copy() self.properties = properties = {'child_properties': sampler.properties} tile = dnx.chimera_graph(sub_m, sub_n, t) self.nodelist = sorted(tile.nodes) self.edgelist = sorted(sorted(edge) for edge in tile.edges) # dimod.Structured abstract base class automatically populates adjacency # and structure as mixins based on nodelist and edgelist if not isinstance(sampler, dimod.Structured): # we could also just tile onto the unstructured sampler but in that # case we would need to know how many tiles to use raise ValueError("given child sampler should be structured") self.children = [sampler] # Chimera values (unless pegasus specified) num_sublattices = 1 nodes_per_cell = t * 2 edges_per_cell = t * t if not ('topology' in sampler.properties and 'type' in sampler.properties['topology'] and 'shape' in sampler.properties['topology']): raise ValueError('To use this composite it is necessary for the' 'structured sampler to have an explicit topology' '(sampler.properties[\'topology\']). Necessary' 'fields are \'type\' and \'shape\'. ') if sampler.properties['topology']['type'] == 'chimera': if len(sampler.properties['topology']['shape']) != 3: raise ValueError('topology shape is not of length 3 ' '(not compatible with chimera)') if sampler.properties['topology']['shape'][2] != t: raise ValueError('Tiling methodology requires that solver' 'and subproblem have identical shore size') m = sampler.properties['topology']['shape'][0] n = sampler.properties['topology']['shape'][1] else: if len(sampler.properties['topology']['shape']) != 1: raise ValueError('topology shape is not of length 1 ' '(not compatible with pegasus)') # Full yield in odd-couplers also required. # Generalizes chimera subgraph requirement and leads to some # simplification of expressions, but at with a cost in cell-yield edges_per_cell += t # Square solvers only by pegasus lattice definition PN yields # 3 by N-1 by N-1 cells: num_sublattices = 3 m = n = sampler.properties['topology']['shape'][0] - 1 if t != 4: raise ValueError( 't=4 for all pegasus processors, value is not typically' 'stored in solver properties and is difficult to infer.' 'Therefore only the value t=4 is supported.') if num_sublattices == 1: # Chimera defaults. Appended coordinates (treat as first and only sublattice) system = dnx.chimera_graph(m, n, t, node_list=sampler.structure.nodelist, edge_list=sampler.structure.edgelist) c2i = {(0, *chimera_index): linear_index for (linear_index, chimera_index) in system.nodes(data='chimera_index')} else: system = dnx.pegasus_graph(m, node_list=sampler.structure.nodelist, edge_list=sampler.structure.edgelist) # Vector specification in terms of nice coordinates: c2i = { dnx.pegasus_coordinates(m + 1).linear_to_nice(linear_index): linear_index for linear_index in system.nodes() } sub_c2i = { chimera_index: linear_index for (linear_index, chimera_index) in tile.nodes(data='chimera_index') } # Count the connections between these qubits def _between(qubits1, qubits2): edges = [ edge for edge in system.edges if edge[0] in qubits1 and edge[1] in qubits2 ] return len(edges) # Get the list of qubits in a cell def _cell_qubits(s, i, j): return [ c2i[(s, i, j, u, k)] for u in range(2) for k in range(t) if (s, i, j, u, k) in c2i ] # get a mask of complete cells cells = [[[False for _ in range(n)] for _ in range(m)] for _ in range(num_sublattices)] for s in range(num_sublattices): for i in range(m): for j in range(n): qubits = _cell_qubits(s, i, j) cells[s][i][j] = (len(qubits) == nodes_per_cell and _between(qubits, qubits) == edges_per_cell) # List of 'embeddings' self.embeddings = properties['embeddings'] = embeddings = [] # For each possible chimera cell check if the next few cells are complete for s in range(num_sublattices): for i in range(m + 1 - sub_m): for j in range(n + 1 - sub_n): # Check if the sub cells are matched match = all(cells[s][i + sub_i][j + sub_j] for sub_i in range(sub_m) for sub_j in range(sub_n)) # Check if there are connections between the cells. # Both Pegasus and Chimera have t vertical and t horizontal between cells: for sub_i in range(sub_m): for sub_j in range(sub_n): if sub_m > 1 and sub_i < sub_m - 1: match &= _between( _cell_qubits(s, i + sub_i, j + sub_j), _cell_qubits(s, i + sub_i + 1, j + sub_j)) == t if sub_n > 1 and sub_j < sub_n - 1: match &= _between( _cell_qubits(s, i + sub_i, j + sub_j), _cell_qubits(s, i + sub_i, j + sub_j + 1)) == t if match: # Pull those cells out into an embedding. embedding = {} for sub_i in range(sub_m): for sub_j in range(sub_n): cells[s][i + sub_i][ j + sub_j] = False # Mark cell as matched for u in range(2): for k in range(t): embedding[sub_c2i[sub_i, sub_j, u, k]] = { c2i[(s, i + sub_i, j + sub_j, u, k)] } embeddings.append(embedding) if len(embeddings) == 0: raise ValueError("no tile embeddings found; " "is the sampler Pegasus or Chimera structured?")
import dwave_networkx as dnx from dwave.system.samplers import DWaveSampler dwave_sampler_pegasus = DWaveSampler(solver={'topology__type': 'pegasus'}) props_pegasus = dwave_sampler_pegasus.properties # Get total qubits - should be 24 * N * (N - 1) total_qubits = props_pegasus['num_qubits'] # Get total number of inactive qubits total_inactive = [ i for i in range(total_qubits) if i not in dwave_sampler_pegasus.nodelist ] print(len(total_inactive)) # This should convert the known inactive qubit indices to Pegasus coordinates. inactive_pegasus_coord = [ dnx.pegasus_coordinates(16).linear_to_pegasus(k) for k in total_inactive ] # With coordinates=True, we only get the fabric qubits pegasus_graph = dnx.pegasus_graph(16, coordinates=True) active_fabric = [ node for node in pegasus_graph.nodes if node not in inactive_pegasus_coord ] print(len(active_fabric)) # another way to compute the number of active qubits active_qubits = dwave_sampler_pegasus.solver.num_active_qubits print(active_qubits)
def _lookup_intersection_coordinates(G): """For a dwave_networkx graph G, this returns a dictionary mapping the lattice points to sets of vertices of G. For Chimera, Pegasus and Zephyr, each lattice point corresponds to the 2 qubits intersecting at that point. """ graph_data = G.graph family = graph_data.get("family") data_key = None intersection_points = defaultdict(set) if family == "chimera": shore = graph_data.get("tile") collect_intersection_points = _chimera_all_intersection_points if graph_data["labels"] == "coordinate": def get_coords(v): return v elif graph_data["data"]: data_key = "chimera_index" else: coords = dnx.chimera_coordinates(graph_data['rows'], n=graph_data['columns'], t=shore) get_coords = coords.linear_to_chimera elif family == "pegasus": shore = [ graph_data['vertical_offsets'], graph_data['horizontal_offsets'] ] collect_intersection_points = _pegasus_all_intersection_points if graph_data["labels"] == "coordinate": def get_coords(v): return v elif graph_data["data"]: data_key = "pegasus_index" else: coords = dnx.pegasus_coordinates(graph_data['rows']) if graph_data['labels'] == 'int': get_coords = coords.linear_to_pegasus elif graph_data['labels'] == 'nice': get_coords = coords.nice_to_pegasus elif family == "zephyr": shore = graph_data.get("tile") collect_intersection_points = _zephyr_all_intersection_points if graph_data["labels"] == "coordinate": def get_coords(v): return v elif graph_data["data"]: data_key = "zephyr_index" else: coords = dnx.zephyr_coordinates(graph_data['rows'], t=shore) get_coords = coords.linear_to_zephyr if data_key is None: for v in G: collect_intersection_points(intersection_points, shore, v, *get_coords(v)) else: for v, d in G.nodes(data=True): collect_intersection_points(intersection_points, shore, v, *d[data_key]) return intersection_points
def make_origin_embeddings(qpu_sampler=None, lattice_type=None): """Creates optimal embeddings for a lattice. The embeddings created are compatible with the topology and shape of a specified ``qpu_sampler``. Args: qpu_sampler (:class:`dimod.Sampler`, optional): Quantum sampler such as a D-Wave system. If not specified, the :class:`~dwave.system.samplers.DWaveSampler` sampler class is used to select a QPU solver with a topology compatible with the specified ``lattice_type`` (e.g. an Advantage system for a 'pegasus' lattice type). lattice_type (str, optional, default=qpu_sampler.properties['topology']['type']): Options are: * "cubic" Embeddings compatible with the schemes arXiv:2009.12479 and arXiv:2003.00133 are created for a ``qpu_sampler`` of topology type either 'pegasus' or 'chimera'. * "pegasus" Embeddings are chain length one (minimal and native). If ``qpu_sampler`` topology type is 'pegasus', maximum scale subgraphs are embedded using the ``nice_coordinates`` vector labeling scheme for variables. * "chimera" Embeddings are chain length one (minimal and native). If ``qpu_sampler`` topology type is 'chimera', maximum scale chimera subgraphs are embedded using the chimera vector labeling scheme for variables. Returns: A list of embeddings. Each embedding is a dictionary, mapping geometric problem keys to sets of qubits (chains) compatible with the ``qpu_sampler``. Examples: This example creates a list of three cubic lattice embeddings compatible with the default online system. These three embeddings are related by rotation of the lattice: for a Pegasus P16 system the embeddings are for lattices of size (15,15,12), (12,15,15) and (15,12,15) respectively. >>> from dwave.system.samplers import DWaveSampler # doctest: +SKIP >>> sampler = DWaveSampler() # doctest: +SKIP >>> embeddings = make_origin_embeddings(qpu_sampler=sampler, ... lattice_type='cubic') # doctest: +SKIP """ if qpu_sampler is None: if lattice_type == 'pegasus' or lattice_type == 'chimera': qpu_sampler = DWaveSampler(solver={'topology__type': lattice_type}) else: qpu_sampler = DWaveSampler() qpu_type = qpu_sampler.properties['topology']['type'] if lattice_type is None: lattice_type = qpu_type qpu_shape = qpu_sampler.properties['topology']['shape'] target = nx.Graph() target.add_edges_from(qpu_sampler.edgelist) if qpu_type == lattice_type: # Fully yielded fully utilized native topology problem. # This method is also easily adapted to work for any chain-length 1 # embedding origin_embedding = {q: [q] for q in qpu_sampler.properties['qubits']} if lattice_type == 'pegasus': # Trimming to nice_coordinate supported embeddings is not a unique, # options, it has some advantages and some disadvantages: proposed_source = dnx.pegasus_graph(qpu_shape[0], nice_coordinates=True) proposed_source = nx.relabel_nodes( proposed_source, { q: dnx.pegasus_coordinates(qpu_shape[0]).nice_to_linear(q) for q in proposed_source.nodes() }) lin_to_vec = dnx.pegasus_coordinates(qpu_shape[0]).linear_to_nice elif lattice_type == 'chimera': proposed_source = dnx.chimera_graph(qpu_shape[0], qpu_shape[1], qpu_shape[2]) lin_to_vec = dnx.chimera_coordinates( qpu_shape[0]).linear_to_chimera else: raise ValueError( f'Unsupported native processor topology {qpu_type}. ' 'Support for Zephyr and other topologies is straightforward to ' 'add subject to standard dwave_networkx library tool availability.' ) elif lattice_type == 'cubic': if qpu_type == 'pegasus': vec_to_lin = dnx.pegasus_coordinates( qpu_shape[0]).pegasus_to_linear L = qpu_shape[0] - 1 dimensions = [L, L, 12] # See arXiv:2003.00133 origin_embedding = { (x, y, z): [ vec_to_lin((0, x, z + 4, y)), vec_to_lin((1, y + 1, 7 - z, x)) ] for x in range(L) for y in range(L) for z in range(8) if target.has_edge(vec_to_lin(( 0, x, z + 4, y)), vec_to_lin((1, y + 1, 7 - z, x))) } origin_embedding.update({ (x, y, z): [ vec_to_lin((0, x + 1, z - 8, y)), vec_to_lin((1, y, 19 - z, x)) ] for x in range(L) for y in range(L) for z in range(8, 12) if target.has_edge(vec_to_lin(( 0, x + 1, z - 8, y)), vec_to_lin((1, y, 19 - z, x))) }) elif qpu_type == 'chimera': vec_to_lin = dnx.chimera_coordinates( qpu_shape[0], qpu_shape[1], qpu_shape[2]).chimera_to_linear L = qpu_shape[0] // 2 dimensions = [L, L, 8] # See arxiv:2009.12479, one choice amongst many origin_embedding = { (x, y, z): [ vec_to_lin(coord) for coord in [(2 * x + 1, 2 * y, 0, z), (2 * x, 2 * y, 0, z), (2 * x, 2 * y, 1, z), (2 * x, 2 * y + 1, 1, z)] ] for x in range(L) for y in range(L) for z in range(4) if target.has_edge(vec_to_lin(( 2 * x + 1, 2 * y, 0, z)), vec_to_lin((2 * x, 2 * y, 0, z))) and target.has_edge(vec_to_lin(( 2 * x, 2 * y, 0, z)), vec_to_lin((2 * x, 2 * y, 1, z))) and target.has_edge(vec_to_lin(( 2 * x, 2 * y, 1, z)), vec_to_lin((2 * x, 2 * y + 1, 1, z))) } origin_embedding.update({ (x, y, 4 + z): [ vec_to_lin(coord) for coord in [(2 * x + 1, 2 * y, 1, z), (2 * x + 1, 2 * y + 1, 1, z), (2 * x + 1, 2 * y + 1, 0, z), (2 * x, 2 * y + 1, 0, z)] ] for x in range(L) for y in range(L) for z in range(4) if target.has_edge(vec_to_lin(( 2 * x + 1, 2 * y, 1, z)), vec_to_lin((2 * x + 1, 2 * y + 1, 1, z))) and target.has_edge(vec_to_lin(( 2 * x + 1, 2 * y + 1, 1, z)), vec_to_lin((2 * x + 1, 2 * y + 1, 0, z))) and target. has_edge(vec_to_lin((2 * x + 1, 2 * y + 1, 0, z)), vec_to_lin((2 * x, 2 * y + 1, 0, z))) }) else: raise ValueError(f'Unsupported qpu_sampler topology {qpu_type} ' 'for cubic lattice solver') proposed_source = _make_cubic_lattice(dimensions) else: raise ValueError('Unsupported combination of lattice_type ' 'and qpu_sampler topology') origin_embedding = _yield_limited_origin_embedding(origin_embedding, proposed_source, target) if qpu_type == lattice_type: # Convert keys to standard vector scheme: origin_embedding = { lin_to_vec(node): origin_embedding[node] for node in origin_embedding } # We can propose additional embeddings. Or we can use symmetries of the # target graph (automorphisms), to create additional embedding options. # This is important in the cubic case, because the subregion shape and # embedding features are asymmetric in the x, y and z directions. # Various symmetries can be exploited in all lattices. origin_embeddings = [origin_embedding] if lattice_type == 'cubic': # A rotation is sufficient for demonstration purposes: origin_embeddings.append({(key[2], key[0], key[1]): value for key, value in origin_embedding.items()}) origin_embeddings.append({(key[1], key[2], key[0]): value for key, value in origin_embedding.items()}) elif lattice_type == 'pegasus': # A horizontal to vertical flip is sufficient for demonstration purposes: # Flip north-east to south-west axis (see draw_pegasus): L = qpu_shape[0] origin_embeddings.append({(key[0], L - 2 - key[2], L - 2 - key[1], 1 - key[3], 3 - key[4]): value for key, value in origin_embedding.items()}) else: # A horizontal to vertical flip is sufficient for demonstration purposes: origin_embeddings.append({(key[1], key[0], 1 - key[2], key[3]): value for key, value in origin_embedding.items()}) return origin_embeddings