def chimera_elimination_order(m, n=None, t=4, coordinates=False): """Provides a variable elimination order for a Chimera graph. A graph defined by ``chimera_graph(m,n,t)`` has treewidth :math:`max(m,n)*t`. This function outputs a variable elimination order inducing a tree decomposition of that width. Parameters ---------- 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. coordinates bool (optional, default False): If True, the elimination order is given in terms of 4-term Chimera coordinates, otherwise given in linear indices. Returns ------- order : list An elimination order that induces the treewidth of chimera_graph(m,n,t). Examples -------- >>> G = dnx.chimera_elimination_order(1, 1, 4) # a single Chimera tile """ if n is None: n = m index_flip = m > n if index_flip: m, n = n, m def chimeraI(m0, n0, k0, l0): if index_flip: return m*2*t*n0 + 2*t*m0 + t*(1-k0) + l0 else: return n*2*t*m0 + 2*t*n0 + t*k0 + l0 order = [] for n_i in range(n): for t_i in range(t): for m_i in range(m): order.append(chimeraI(m_i, n_i, 0, t_i)) for n_i in range(n): for m_i in range(m): for t_i in range(t): order.append(chimeraI(m_i, n_i, 1, t_i)) if coordinates: return list(chimera_coordinates(m,n,t).iter_linear_to_chimera(order)) else: return order
def test_3x3_identity(self): C33 = dnx.chimera_graph(3, 3) coord = chimera_coordinates(3, 3, 4) labels = canonical_chimera_labeling(C33) labels = {v: coord.chimera_to_linear(labels[v]) for v in labels} G = nx.relabel_nodes(C33, labels, copy=True) self.assertTrue(nx.is_isomorphic(G, C33))
def test_row_identity(self): C41 = dnx.chimera_graph(4, 1) coord = chimera_coordinates(4, 1, 4) labels = canonical_chimera_labeling(C41) labels = {v: coord.chimera_to_linear(labels[v]) for v in labels} G = nx.relabel_nodes(C41, labels, copy=True) self.assertTrue(nx.is_isomorphic(G, C41))
def test_tile_identity(self): C1 = dnx.chimera_graph(1) coord = chimera_coordinates(1, 1, 4) labels = canonical_chimera_labeling(C1) labels = {v: coord.chimera_to_linear(labels[v]) for v in labels} G = nx.relabel_nodes(C1, labels, copy=True) self.assertTrue(nx.is_isomorphic(G, C1)) self.assertEqual(set(G), set(C1))
def test_bqm_tile_identity(self): C1bqm = dimod.BinaryQuadraticModel.from_ising( {}, {e: -1 for e in dnx.chimera_graph(1).edges}) coord = chimera_coordinates(1, 1, 4) labels = canonical_chimera_labeling(C1bqm) labels = {v: coord.chimera_to_linear(labels[v]) for v in labels} bqm = C1bqm.relabel_variables(labels, inplace=False) self.assertEqual(bqm, C1bqm)
def test_reversed(self): C33 = nx.OrderedGraph() C33.add_nodes_from(reversed(range(3 * 3 * 4))) C33.add_edges_from(dnx.chimera_graph(3, 3, 4).edges) coord = chimera_coordinates(3, 3, 4) labels = canonical_chimera_labeling(C33) labels = {v: coord.chimera_to_linear(labels[v]) for v in labels} G = nx.relabel_nodes(C33, labels, copy=True) self.assertTrue(nx.is_isomorphic(G, C33))
def test_coordinate_subgraphs(self): from dwave_networkx.generators.chimera import chimera_coordinates from random import sample G = dnx.chimera_graph(4) H = dnx.chimera_graph(4, coordinates=True) coords = chimera_coordinates(4) lmask = sample(list(G.nodes()), G.number_of_nodes() // 2) cmask = list(coords.iter_linear_to_chimera(lmask)) self.assertEqual(lmask, list(coords.iter_chimera_to_linear(cmask))) Gm = dnx.chimera_graph(4, node_list=lmask) Hm = dnx.chimera_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.chimera_graph(4, edge_list=EG) Hn = dnx.chimera_graph(4, edge_list=EH, coordinates=True) Gnodes = Gn.nodes Hnodes = Hn.nodes for v in Gnodes: q = Gnodes[v]['chimera_index'] self.assertIn(q, Hnodes) self.assertEqual(Hnodes[q]['linear_index'], v) self.assertEqual(v, coords.chimera_to_linear(q)) self.assertEqual(q, coords.linear_to_chimera(v)) for q in Hnodes: v = Hnodes[q]['linear_index'] self.assertIn(v, Gnodes) self.assertEqual(Gnodes[v]['chimera_index'], q) self.assertEqual(v, coords.chimera_to_linear(q)) self.assertEqual(q, coords.linear_to_chimera(v)) self.assertEqual( EG, sorted(map(sorted, coords.iter_chimera_to_linear_pairs(Hn.edges())))) self.assertEqual( EH, sorted(map(sorted, coords.iter_linear_to_chimera_pairs(Gn.edges()))))
def test_coordinate_basics(self): from dwave_networkx.generators.chimera import chimera_coordinates G = dnx.chimera_graph(4) H = dnx.chimera_graph(4, coordinates=True) coords = chimera_coordinates(4) Gnodes = G.nodes Hnodes = H.nodes for v in Gnodes: q = Gnodes[v]['chimera_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]['chimera_index'], q) self.assertEqual(v, coords.int(q)) self.assertEqual(q, coords.tuple(v))
def test_construction_string_labels(self): C22 = dnx.chimera_graph(2, 2, 3) coord = chimera_coordinates(2, 2, 3) alpha = 'abcdefghijklmnopqrstuvwxyz' bqm = dimod.BinaryQuadraticModel.empty(dimod.BINARY) for u, v in reversed(list(C22.edges)): bqm.add_interaction(alpha[u], alpha[v], 1) assert len(bqm.quadratic) == len(C22.edges) assert len(bqm) == len(C22) labels = canonical_chimera_labeling(bqm) labels = {v: alpha[coord.chimera_to_linear(labels[v])] for v in labels} bqm2 = bqm.relabel_variables(labels, inplace=False) self.assertEqual(bqm, bqm2)
def __init__(self, S, Tg, **params): # Parse parameters self.origin = params.pop('origin', None) self.orientation = params.pop('orientation', None) # Parse Target graph self.Tg = Tg if Tg.graph['family'] != 'chimera': raise ValueError( "Invalid target graph family. Only valid for 'chimera' graphs") self.m = Tg.graph['columns'] self.n = Tg.graph['rows'] self.t = Tg.graph['tile'] self.coordinates = Tg.graph['labels'] == 'coordinate' self.qubit_cols = self.m * self.t self.qubit_rows = self.n * self.t self.c2i = chimera_coordinates(self.m, self.n, self.t) # Parse Source graph. try: P, Q = nx.bipartite.sets(nx.Graph(S)) except: try: Sg = nx.complete_bipartite_graph(*S) P = set() Q = set() for v, data in Sg.nodes(data=True): if data['bipartite'] == 1: Q.add(v) else: P.add(v) except: raise RuntimeError("Input must be iterable of edges of a " "complete bipartite graph, or tuple with " "dimensions (p,q).") self.P = {k: [] for k in P} self.Q = {k: [] for k in Q} self.faults = None
def chimera_layout(G, scale=1., center=None, dim=2): """Positions the nodes of graph G in a Chimera cross topology. NumPy (https://scipy.org) is required for this function. Parameters ---------- G : NetworkX graph Should be a Chimera graph or a subgraph of a Chimera graph. If every node in G has a `chimera_index` attribute, those are used to place the nodes. Otherwise makes a best-effort attempt to find positions. 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.chimera_graph(1) >>> pos = dnx.chimera_layout(G) """ if not isinstance(G, nx.Graph): empty_graph = nx.Graph() empty_graph.add_edges_from(G) G = empty_graph # now we get chimera coordinates for the translation # first, check if we made it if G.graph.get("family") == "chimera": m = G.graph['rows'] n = G.graph['columns'] t = G.graph['tile'] # get a node placement function xy_coords = chimera_node_placer_2d(m, n, t, 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['chimera_index']) for v, dat in G.nodes(data=True) } else: coord = chimera_coordinates(m, n, t) pos = { v: xy_coords(*coord.linear_to_chimera(v)) for v in G.nodes() } else: # best case scenario, each node in G has a chimera_index attribute. Otherwise # we will try to determine it using the find_chimera_indices function. if all('chimera_index' in dat for __, dat in G.nodes(data=True)): chimera_indices = { v: dat['chimera_index'] for v, dat in G.nodes(data=True) } else: chimera_indices = find_chimera_indices(G) # we could read these off of the name attribute for G, but we would want the values in # the nodes to override the name in case of conflict. m = max(idx[0] for idx in chimera_indices.values()) + 1 n = max(idx[1] for idx in chimera_indices.values()) + 1 t = max(idx[3] for idx in chimera_indices.values()) + 1 xy_coords = chimera_node_placer_2d(m, n, t, scale, center, dim) # compute our coordinates pos = { v: xy_coords(i, j, u, k) for v, (i, j, u, k) in chimera_indices.items() } return pos