def weighted_maximum_cut(G, sampler=None, **sampler_args): """Returns an approximate weighted maximum cut. Defines an Ising problem with ground states corresponding to a weighted maximum cut and uses the sampler to sample from it. A weighted maximum cut is a subset S of the vertices of G that maximizes the sum of the edge weights between S and its complementary subset. Parameters ---------- G : NetworkX graph The graph on which to find a weighted maximum cut. Each edge in G should have a numeric `weight` attribute. sampler A binary quadratic model sampler. A sampler is a process that samples from low energy states in models defined by an Ising equation or a Quadratic Unconstrained Binary Optimization Problem (QUBO). A sampler is expected to have a 'sample_qubo' and 'sample_ising' method. A sampler is expected to return an iterable of samples, in order of increasing energy. If no sampler is provided, one must be provided using the `set_default_sampler` function. sampler_args Additional keyword parameters are passed to the sampler. Returns ------- S : set A maximum cut of G. Notes ----- Samplers by their nature may not return the optimal solution. This function does not attempt to confirm the quality of the returned sample. """ # In order to form the Ising problem, we want to increase the # energy by 1 for each edge between two nodes of the same color. # The linear biases can all be 0. h = {v: 0. for v in G} try: J = {(u, v): G[u][v]['weight'] for u, v in G.edges} except KeyError: raise DWaveNetworkXException("edges must have 'weight' attribute") # draw the lowest energy sample from the sampler response = sampler.sample_ising(h, J, **sampler_args) sample = next(iter(response)) return set(v for v in G if sample[v] >= 0)
def find_chimera_indices(G): """Attempts to determine the Chimera indices of the nodes in graph G. See the :func:`~chimera_graph()` function for a definition of a Chimera graph and Chimera indices. Parameters ---------- G : NetworkX graph Should be a single-tile Chimera graph. Returns ------- chimera_indices : dict A dict of the form {node: (i, j, u, k), ...} where (i, j, u, k) is a 4-tuple of integer Chimera indices. Examples -------- >>> G = dnx.chimera_graph(1, 1, 4) >>> chimera_indices = dnx.find_chimera_indices(G) >>> G = nx.Graph() >>> G.add_edges_from([(0, 2), (1, 2), (1, 3), (0, 3)]) >>> chimera_indices = dnx.find_chimera_indices(G) >>> nx.set_node_attributes(G, chimera_indices, 'chimera_index') """ # if the nodes are orderable, we want the lowest-order one. try: nlist = sorted(G.nodes) except TypeError: nlist = G.nodes() n_nodes = len(nlist) # create the object that will store the indices chimera_indices = {} # ok, let's first check for the simple cases if n_nodes == 0: return chimera_indices elif n_nodes == 1: raise DWaveNetworkXException( 'Singleton graphs are not Chimera-structured') elif n_nodes == 2: return {nlist[0]: (0, 0, 0, 0), nlist[1]: (0, 0, 1, 0)} # next, let's get the bicoloring of the graph; this raises an exception if the graph is # not bipartite coloring = color(G) # we want the color of the node to be the u term in the Chimera-index, so we want the # first node in nlist to be color 0 if coloring[nlist[0]] == 1: coloring = {v: 1 - coloring[v] for v in coloring} # we also want the diameter of the graph # claim: diameter(G) == m + n for |G| > 2 dia = diameter(G) # we have already handled the |G| <= 2 case, so we know, for diameter == 2, that the Chimera # graph is a single tile if dia == 2: shore_indices = [0, 0] for v in nlist: u = coloring[v] chimera_indices[v] = (0, 0, u, shore_indices[u]) shore_indices[u] += 1 return chimera_indices # NB: max degree == shore size <==> one tile raise Exception( 'not yet implemented for Chimera graphs with more than one tile')
def pegasus_graph(m, create_using=None, node_list=None, edge_list=None, data=True, offset_lists=None, offsets_index=None, coordinates=False, fabric_only=True): """ Creates a Pegasus graph with size parameter m. The Pegasus topology produced by this generator with default parameters is one member of a large family of topologies under consideration, and may not be reflected in future products. A Pegasus lattice is a graph minor of a lattice similar to Chimera, where unit tiles are completely connected. In the most generality, our prelattice Q(N0,N1) contains nodes of the form (i, j, 0, k) with 0 <= k < 2 [vertical nodes] and (i, j, 1, k) with 0 <= k < 2 [horizontal nodes] for 0 <= i <= N0 and 0 <= j < N1; and edges of the form (i, j, u, k) ~ (i+u, j+1-u, u, k) [external edges] (i, j, 0, k) ~ (i, j, 1, k) [internal edges] (i, j, u, 0) ~ (i, j, u, 1) [odd edges] The minor is specified by two lists of offsets; S0 and S1 of length L0 and L1 (where L0 and L1, and the entries of S0 and S1, must be divisible by 2). From these offsets, we construct our minor, a Pegasus lattice, by contracting the complete intervals of external edges, I(0, w, k, z) = [(L1*w + k, L0*z + S0[k] + r, 0, k % 2) for 0 <= r < L0] I(1, w, k, z) = [(L1*z + S1[k] + r, L0*w + k, 1, k % 2) for 0 <= r < L1] and deleting the prelattice nodes of any interval not fully contained in Q(N0, N1). This generator is specialized to L0 = L1 = 12; N0 = N1 = 12m. The notation (u, w, k, z) is called the pegasus index of a node in a pegasus lattice. The entries can be interpreted as following, u : qubit orientation (0 = vertical, 1 = horizontal) w : orthogonal major offset k : orthogonal minor offset z : parallel offset and the edges in the minor have the form (u, w, k, z) ~ (u, w, k, z+1) [external edges] (0, w0, k0, z0) ~ (1, w1, k1, z1) [internal edges, see below] (u, w, 2k, z) ~ (u, w, 2k+1, z) [odd edges] where internal edges only exist when w1 = z0 + (1 if k1 < S0[k0] else 0), and z1 = w0 - (1 if k0 < S1[k1] else 0) linear indices are computed from pegasus indices by the formula q = ((u * m + w) * 12 + k) * (m - 1) + z Parameters ---------- m : int The size parameter for the Pegasus lattice. create_using : Graph, optional (default None) If provided, this graph is cleared of nodes and edges and filled with the new graph. Usually used to set the type of the graph. node_list : iterable, optional (default None) Iterable of nodes in the graph. If None, calculated from m. Note that this list is used to remove nodes, so any nodes specified not in range(24 * m * (m-1)) will not be added. edge_list : iterable, optional (default None) Iterable of edges in the graph. If None, edges are generated as described above. The nodes in each edge must be integer-labeled in range(24 * m * (m-1)). data : bool, optional (default True) If True, each node has a pegasus_index attribute. The attribute is a 4-tuple Pegasus index as defined above. (if coordinates = True, we set a linear_index, which is an integer) coordinates : bool, optional (default False) If True, node labels are 4-tuple Pegasus indices offset_lists : pair of lists, optional (default None) Used to directly control the offsets, each list in the pair should have length 12, and contain even ints. If offset_lists is not None, then offsets_index must be None. offsets_index : int, optional (default None) A number between 0 and 7 inclusive, to select a preconfigured set of topological parameters. If both offsets_index and offset_lists are None, then we set offsets_index = 0. At least one of these two parameters must be None. fabric_only: bool, optional (default True) The Pegasus graph, by definition, will have some disconnected components. If this True, we will only construct nodes from the largest component. Otherwise, the full disconnected graph will be constructed. Ignored if edge_lists is not None """ warnings.warn( "The Pegasus topology produced by this generator with default parameters is one member of a large family of topologies under consideration, and may not be reflected in future products" ) if offset_lists is None: offsets_descriptor = offsets_index = offsets_index or 0 offset_lists = [ [( 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, 6, 6, ), ( 6, 6, 6, 6, 2, 2, 2, 2, 10, 10, 10, 10, )], [( 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, 6, 6, ), ( 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, 6, 6, )], [( 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, 6, 6, ), ( 10, 10, 10, 10, 6, 6, 6, 6, 2, 2, 2, 2, )], [( 10, 10, 10, 10, 6, 6, 6, 6, 2, 2, 2, 2, ), ( 10, 10, 10, 10, 6, 6, 6, 6, 2, 2, 2, 2, )], [( 10, 10, 10, 10, 6, 6, 6, 6, 2, 2, 2, 2, ), ( 2, 2, 2, 2, 6, 6, 6, 6, 10, 10, 10, 10, )], [( 6, 6, 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, ), ( 6, 6, 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, )], [( 6, 6, 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, ), ( 6, 6, 10, 10, 10, 10, 2, 2, 2, 2, 6, 6, )], [( 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ), ( 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, )], ][offsets_index] elif offsets_index is not None: raise DWaveNetworkXException( "provide at most one of offsets_index and offset_lists") else: for ori in 0, 1: for x, y in zip(offset_lists[ori][::2], offset_lists[ori][1::2]): if x != y: warnings.warn( "The offets list you've provided is possibly non-physical. Odd-coupled qubits should have the same value." ) offsets_descriptor = offset_lists G = nx.empty_graph(0, create_using) G.name = "pegasus_graph(%s, %s)" % (m, offsets_descriptor) construction = (("family", "pegasus"), ("rows", m), ("columns", m), ("tile", 12), ("vertical_offsets", offset_lists[0]), ("horizontal_offsets", offset_lists[1]), ("data", data), ("labels", "coordinate" if coordinates else "int")) G.graph.update(construction) max_size = m * (m - 1) * 24 # max number of nodes G can have m1 = m - 1 if coordinates: c2i = lambda *q: q else: def c2i(u, w, k, z): return u * 12 * m * m1 + w * 12 * m1 + k * m1 + z if edge_list is None: if fabric_only: fabric_start = min(s for s in offset_lists[1]), min( s for s in offset_lists[0]) fabric_end = 12 - max(s for s in offset_lists[1]), 12 - max( s for s in offset_lists[0]) else: fabric_end = fabric_start = 0, 0 G.add_edges_from((c2i(u, w, k, z), c2i(u, w, k, z + 1)) for u in (0, 1) for w in range(m) for k in range(fabric_start[u] if w == 0 else 0, 12 - (fabric_end[u] if w == m1 else 0)) for z in range(m1 - 1)) G.add_edges_from((c2i(u, w, k, z), c2i(u, w, k + 1, z)) for u in (0, 1) for w in range(m) for k in range(fabric_start[u] if w == 0 else 0, 12 - (fabric_end[u] if w == m1 else 0), 2) for z in range(m1)) off0, off1 = offset_lists G.add_edges_from( (c2i(0, w, k, z), c2i(1, z + (kk < off0[k]), kk, w - (k < off1[kk]))) for w in range(m) for kk in range(12) for k in range(0 if w else off1[kk], 12 if w < m1 else off1[kk]) for z in range(m1)) else: G.add_edges_from(edge_list) if node_list is not None: nodes = set(node_list) G.remove_nodes_from(set(G) - nodes) G.add_nodes_from(nodes) # for singleton nodes if data: v = 0 for u in range(2): for w in range(m): for k in range(12): for z in range(m1): q = u, w, k, z if coordinates: if q in G: G.node[q]['linear_index'] = v else: if v in G: G.node[v]['pegasus_index'] = q v += 1 return G
def pegasus_graph(m, create_using=None, node_list=None, edge_list=None, data=True, offset_lists=None, offsets_index=None, coordinates=False, fabric_only=True, nice_coordinates=False): """ Creates a Pegasus graph with size parameter `m`. Parameters ---------- m : int Size parameter for the Pegasus lattice. create_using : Graph, optional (default None) If provided, this graph is cleared of nodes and edges and filled with the new graph. Usually used to set the type of the graph. node_list : iterable, optional (default None) Iterable of nodes in the graph. If None, calculated from `m`. Note that this list is used to remove nodes, so any nodes specified not in ``range(24 * m * (m-1))`` are not added. edge_list : iterable, optional (default None) Iterable of edges in the graph. If None, edges are generated as described below. The nodes in each edge must be integer-labeled in ``range(24 * m * (m-1))``. data : bool, optional (default True) If True, each node has a pegasus_index attribute. The attribute is a 4-tuple Pegasus index as defined below. If the `coordinates` parameter is True, a linear_index, which is an integer, is used. coordinates : bool, optional (default False) If True, node labels are 4-tuple Pegasus indices. Ignored if the `nice_coordinates` parameter is True. offset_lists : pair of lists, optional (default None) Directly controls the offsets. Each list in the pair must have length 12 and contain even ints. If `offset_lists` is not None, the `offsets_index` parameter must be None. offsets_index : int, optional (default None) A number between 0 and 7, inclusive, that selects a preconfigured set of topological parameters. If both the `offsets_index` and `offset_lists` parameters are None, the `offsets_index` parameters is set to zero. At least one of these two parameters must be None. fabric_only: bool, optional (default True) The Pegasus graph, by definition, has some disconnected components. If True, the generator only constructs nodes from the largest component. If False, the full disconnected graph is constructed. Ignored if the `edge_lists` parameter is not None or `nice_coordinates` is True nice_coordinates: bool, optional (default False) If the `offsets_index` parameter is 0, the graph uses a "nicer" coordinate system, more compatible with Chimera addressing. These coordinates are 5-tuples taking the form :math:`(t, y, x, u, k)` where :math:`0 <= x < M-1`, :math:`0 <= y < M-1`, :math:`0 <= u < 2`, :math:`0 <= k < 4`, and :math:`0 <= t < 3`. For any given :math:`0 <= t0 < 3`, the subgraph of nodes with :math:`t = t0` has the structure of `chimera(M-1, M-1, 4)` with the addition of odd couplers. Supercedes both the `fabric_only` and `coordinates` parameters. Returns ------- G : NetworkX Graph A Pegasus lattice for size parameter `m`. The maximum degree of this graph is 15. The number of nodes depends on multiple parameters; for example, * `pegasus_graph(1)`: zero nodes * `pegasus_graph(m, fabric_only=False)`: :math:`24m(m-1)` nodes * `pegasus_graph(m, fabric_only=True)`: :math:`24m(m-1)-8(m-1)` nodes * `pegasus_graph(m, nice_coordinates=True)`: :math:`24(m-1)^2` nodes Counting formulas for edges have a complicated dependency on parameter settings. Some example upper bounds are: * `pegasus_graph(1, fabric_only=False)`: zero edges * `pegasus_graph(m, fabric_only=False)`: :math:`12*(15*(m-1)^2 + m - 3)` edges if m > 1 Note that the formulas above are valid for default offset parameters. A Pegasus lattice is a graph minor of a lattice similar to Chimera, where unit tiles are completely connected. In its most general definition, prelattice :math:`Q(N0,N1)` contains nodes of the form * vertical nodes: :math:`(i, j, 0, k)` with :math:`0 <= k < 2` * horizontal nodes: :math:`(i, j, 1, k)` with :math:`0 <= k < 2` for :math:`0 <= i <= N0` and :math:`0 <= j < N1`, and edges of the form * external: :math:`(i, j, u, k)` ~ :math:`(i+u, j+1-u, u, k)` * internal: :math:`(i, j, 0, k)` ~ :math:`(i, j, 1, h)` * odd: :math:`(i, j, u, 0)` ~ :math:`(i, j, u, 1)` Given two lists of offsets, :math:`S0` and :math:`S1`, of length :math:`L0` and :math:`L1`, where both lengths and values must be divisible by 2, the minor---a Pegasus lattice---is constructed by contracting the complete intervals of external edges:: I(0, w, k, z) = [(L1*w + k, L0*z + S0[k] + r, 0, k % 2) for 0 <= r < L0] I(1, w, k, z) = [(L1*z + S1[k] + r, L0*w + k, 1, k % 2) for 0 <= r < L1] and deleting the prelattice nodes of any interval not fully contained in :math:`Q(N0, N1)`. This generator, 'pegasus_graph()', is specialized for the minor constructed by prelattice and offset parameters :math:`L0 = L1 = 12` and :math:`N0 = N1 = 12m`. The *Pegasus index* of a node in a Pegasus lattice, :math:`(u, w, k, z)`, can be interpreted as: * :math:`u`: qubit orientation (0 = vertical, 1 = horizontal) * :math:`w`: orthogonal major offset * :math:`k`: orthogonal minor offset * :math:`z`: parallel offset Edges in the minor have the form * external: :math:`(u, w, k, z)` ~ :math:`(u, w, k, z+1)` * internal: :math:`(0, w0, k0, z0)` ~ :math:`(1, w1, k1, z1)` * odd: :math:`(u, w, 2k, z)` ~ :math:`(u, w, 2k+1, z)` where internal edges only exist when 1. w1 = z0 + (1 if k1 < S0[k0] else 0) 2. z1 = w0 - (1 if k0 < S1[k1] else 0) Linear indices are computed from Pegasus indices by the formula:: q = ((u * m + w) * 12 + k) * (m - 1) + z Examples ======== >>> G = dnx.pegasus_graph(2, nice_coordinates=True) >>> G.nodes(data=True)[(0, 0, 0, 0, 0)] # doctest: +SKIP {'linear_index': 4, 'pegasus_index': (0, 0, 4, 0)} """ if offset_lists is None: offsets_descriptor = offsets_index = offsets_index or 0 offset_lists = [ [(2, 2, 2, 2, 10, 10, 10, 10, 6, 6, 6, 6,), (6, 6, 6, 6, 2, 2, 2, 2, 10, 10, 10, 10,)], [(2, 2, 2, 2, 10, 10, 10, 10, 6, 6, 6, 6,), (2, 2, 2, 2, 10, 10, 10, 10, 6, 6, 6, 6,)], [(2, 2, 2, 2, 10, 10, 10, 10, 6, 6, 6, 6,), (10, 10, 10, 10, 6, 6, 6, 6, 2, 2, 2, 2,)], [(10, 10, 10, 10, 6, 6, 6, 6, 2, 2, 2, 2,), (10, 10, 10, 10, 6, 6, 6, 6, 2, 2, 2, 2,)], [(10, 10, 10, 10, 6, 6, 6, 6, 2, 2, 2, 2,), (2, 2, 2, 2, 6, 6, 6, 6, 10, 10, 10, 10,)], [(6, 6, 2, 2, 2, 2, 10, 10, 10, 10, 6, 6,), (6, 6, 2, 2, 2, 2, 10, 10, 10, 10, 6, 6,)], [(6, 6, 2, 2, 2, 2, 10, 10, 10, 10, 6, 6,), (6, 6, 10, 10, 10, 10, 2, 2, 2, 2, 6, 6,)], [(6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,), (6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,)], ][offsets_index] elif offsets_index is not None: raise DWaveNetworkXException("provide at most one of offsets_index and offset_lists") else: for ori in 0, 1: for x, y in zip(offset_lists[ori][::2], offset_lists[ori][1::2]): if x != y: warnings.warn("The offets list you've provided is possibly non-physical. Odd-coupled qubits should have the same value.") offsets_descriptor = offset_lists G = nx.empty_graph(0, create_using) G.name = "pegasus_graph(%s, %s)" % (m, offsets_descriptor) m1 = m - 1 if nice_coordinates: if offsets_index != 0: raise NotImplementedError("nice coordinate system is only implemented for offsets_index 0") labels = 'nice' pegasus_to_nice = pegasus_coordinates.pegasus_to_nice nice_to_pegasus = pegasus_coordinates.nice_to_pegasus label = lambda *q: pegasus_to_nice(q) elif coordinates: label = lambda *q: q labels = 'coordinate' else: labels = 'int' def label(u, w, k, z): return u * 12 * m * m1 + w * 12 * m1 + k * m1 + z construction = (("family", "pegasus"), ("rows", m), ("columns", m), ("tile", 12), ("vertical_offsets", offset_lists[0]), ("horizontal_offsets", offset_lists[1]), ("data", data), ("labels", labels)) G.graph.update(construction) max_size = m * (m - 1) * 24 # max number of nodes G can have if edge_list is None: if nice_coordinates: fabric_start = 4,8 fabric_end = 8, 4 elif fabric_only: fabric_start = min(s for s in offset_lists[1]), min(s for s in offset_lists[0]) fabric_end = 12 - max(s for s in offset_lists[1]), 12 - max(s for s in offset_lists[0]) else: fabric_end = fabric_start = 0, 0 G.add_edges_from((label(u, w, k, z), label(u, w, k, z + 1)) for u in (0, 1) for w in range(m) for k in range(fabric_start[u] if w == 0 else 0, 12 - (fabric_end[u] if w == m1 else 0)) for z in range(m1 - 1)) G.add_edges_from((label(u, w, k, z), label(u, w, k + 1, z)) for u in (0, 1) for w in range(m) for k in range(fabric_start[u] if w == 0 else 0, 12 - (fabric_end[u] if w == m1 else 0), 2) for z in range(m1)) off0, off1 = offset_lists def qfilter(u, w, k, z): if w == 0: return k >= fabric_start[u] if w == m1: return k < 12-fabric_end[u] return True def efilter(e): return qfilter(*e[0]) and qfilter(*e[1]) internal_couplers = (((0, w, k, z), (1, z + (kk < off0[k]), kk, w - (k < off1[kk]))) for w in range(m) for kk in range(12) for k in range(0 if w else off1[kk], 12 if w < m1 else off1[kk]) for z in range(m1)) G.add_edges_from((label(*e[0]), label(*e[1])) for e in internal_couplers if efilter(e)) else: G.add_edges_from(edge_list) if node_list is not None: nodes = set(node_list) G.remove_nodes_from(set(G) - nodes) G.add_nodes_from(nodes) # for singleton nodes if data: v = 0 if nice_coordinates: def fill_data(): q = (u, w, k, z) d = get_node_data(pegasus_to_nice(q)) if d is not None: d['linear_index'] = v d['pegasus_index'] = q elif coordinates: def fill_data(): d = get_node_data((u, w, k, z)) if d is not None: d['linear_index'] = v else: def fill_data(): d = get_node_data(v) if d is not None: d['pegasus_index'] = (u, w, k, z) get_node_data = G.nodes.get for u in range(2): for w in range(m): for k in range(12): for z in range(m1): fill_data() v += 1 return G
def pegasus_graph(m, create_using=None, node_list=None, edge_list=None, data=True, offset_lists=None, offsets_index=None, coordinates=False, fabric_only=True, nice_coordinates=False): """ Creates a Pegasus graph with size parameter m. The number of nodes and edges varies according to multiple parameters, for example, pegasus_graph(1) contains zero nodes, pegasus_graph(m, fabric_only=False) contains :math:`24m(m-1)` nodes, pegasus_graph(m, fabric_only=True) contains :math:`24m(m-1)-8(m-1)` nodes, and pegasus_graph(m, nice_coordinates=True) contains :math:`24(m-1)^2` nodes. The maximum degree of these graph is 15, and counting formulas are more complicated for edges given most parameter settings. Upper bounds are given below, pegasus_graph(1, fabric_only=False) has zero edges, pegasus_graph(m, fabric_only=False) has :math:`12*(15*(m-1)^2 + m - 3)` edges if m > 1 Note that the above are valid with default offset parameters. A Pegasus lattice is a graph minor of a lattice similar to Chimera, where unit tiles are completely connected. In the most generality, prelattice :math:`Q(N0,N1)` contains nodes of the form :math:`(i, j, 0, k)` with :math:`0 <= k < 2` [vertical nodes] and :math:`(i, j, 1, k)` with :math:`0 <= k < 2` [horizontal nodes] for :math:`0 <= i <= N0` and :math:`0 <= j < N1`; and edges of the form :math:`(i, j, u, k)` ~ :math:`(i+u, j+1-u, u, k)` [external edges] :math:`(i, j, 0, k)` ~ :math:`(i, j, 1, k)` [internal edges] :math:`(i, j, u, 0)` ~ :math:`(i, j, u, 1)` [odd edges] The minor is specified by two lists of offsets; :math:`S0` and :math:`S1` of length :math:`L0` and :math:`L1` (where :math:`L0` and :math:`L1`, and the entries of :math:`S0` and :math:`S1`, must be divisible by 2). From these offsets, we construct our minor, a Pegasus lattice, by contracting the complete intervals of external edges:: I(0, w, k, z) = [(L1*w + k, L0*z + S0[k] + r, 0, k % 2) for 0 <= r < L0] I(1, w, k, z) = [(L1*z + S1[k] + r, L0*w + k, 1, k % 2) for 0 <= r < L1] and deleting the prelattice nodes of any interval not fully contained in :math:`Q(N0, N1)`. This generator is specialized to :math:`L0 = L1 = 12`; :math:`N0 = N1 = 12m`. The notation :math:`(u, w, k, z)` is called the Pegasus index of a node in a Pegasus lattice. The entries can be interpreted as following, :math:`u`: qubit orientation (0 = vertical, 1 = horizontal) :math:`w`: orthogonal major offset :math:`k`: orthogonal minor offset :math:`z`: parallel offset and the edges in the minor have the form :math:`(u, w, k, z)` ~ :math:`(u, w, k, z+1)` [external edges] :math:`(0, w0, k0, z0)` ~ :math:`(1, w1, k1, z1)` [internal edges, see below] :math:`(u, w, 2k, z)` ~ :math:`(u, w, 2k+1, z)` [odd edges] where internal edges only exist when:: w1 = z0 + (1 if k1 < S0[k0] else 0), and z1 = w0 - (1 if k0 < S1[k1] else 0) linear indices are computed from pegasus indices by the formula:: q = ((u * m + w) * 12 + k) * (m - 1) + z Parameters ---------- m : int The size parameter for the Pegasus lattice. create_using : Graph, optional (default None) If provided, this graph is cleared of nodes and edges and filled with the new graph. Usually used to set the type of the graph. node_list : iterable, optional (default None) Iterable of nodes in the graph. If None, calculated from m. Note that this list is used to remove nodes, so any nodes specified not in range(24 * m * (m-1)) will not be added. edge_list : iterable, optional (default None) Iterable of edges in the graph. If None, edges are generated as described above. The nodes in each edge must be integer-labeled in range(24 * m * (m-1)). data : bool, optional (default True) If True, each node has a pegasus_index attribute. The attribute is a 4-tuple Pegasus index as defined above. (if coordinates = True, we set a linear_index, which is an integer) coordinates : bool, optional (default False) If True, node labels are 4-tuple Pegasus indices. Ignored if nice_coordinates is True offset_lists : pair of lists, optional (default None) Used to directly control the offsets, each list in the pair should have length 12, and contain even ints. If offset_lists is not None, then offsets_index must be None. offsets_index : int, optional (default None) A number between 0 and 7 inclusive, to select a preconfigured set of topological parameters. If both offsets_index and offset_lists are None, then we set offsets_index = 0. At least one of these two parameters must be None. fabric_only: bool, optional (default True) The Pegasus graph, by definition, will have some disconnected components. If this True, we will only construct nodes from the largest component. Otherwise, the full disconnected graph will be constructed. Ignored if edge_lists is not None or nice_coordinates is True nice_coordinates: bool, optional (default False) In the case that offsets_index = 0, generate the graph with a nicer coordinate system which is more compatible with Chimera addressing. These coordinates are 5-tuples taking the form (t, y, x, u, k) where 0 <= x < M-1, 0 <= y < M-1, 0 <= u < 2, 0 <= k < 4 and 0 <= t < 3. For any given 0 <= t0 < 3, the subgraph of nodes with t = t0 has the structure of chimera(M-1, M-1, 4) with the addition of odd couplers. Supercedes both the fabric_only and coordinates parameters. """ if offset_lists is None: offsets_descriptor = offsets_index = offsets_index or 0 offset_lists = [ [( 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, 6, 6, ), ( 6, 6, 6, 6, 2, 2, 2, 2, 10, 10, 10, 10, )], [( 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, 6, 6, ), ( 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, 6, 6, )], [( 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, 6, 6, ), ( 10, 10, 10, 10, 6, 6, 6, 6, 2, 2, 2, 2, )], [( 10, 10, 10, 10, 6, 6, 6, 6, 2, 2, 2, 2, ), ( 10, 10, 10, 10, 6, 6, 6, 6, 2, 2, 2, 2, )], [( 10, 10, 10, 10, 6, 6, 6, 6, 2, 2, 2, 2, ), ( 2, 2, 2, 2, 6, 6, 6, 6, 10, 10, 10, 10, )], [( 6, 6, 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, ), ( 6, 6, 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, )], [( 6, 6, 2, 2, 2, 2, 10, 10, 10, 10, 6, 6, ), ( 6, 6, 10, 10, 10, 10, 2, 2, 2, 2, 6, 6, )], [( 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ), ( 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, )], ][offsets_index] elif offsets_index is not None: raise DWaveNetworkXException( "provide at most one of offsets_index and offset_lists") else: for ori in 0, 1: for x, y in zip(offset_lists[ori][::2], offset_lists[ori][1::2]): if x != y: warnings.warn( "The offets list you've provided is possibly non-physical. Odd-coupled qubits should have the same value." ) offsets_descriptor = offset_lists G = nx.empty_graph(0, create_using) G.name = "pegasus_graph(%s, %s)" % (m, offsets_descriptor) m1 = m - 1 if nice_coordinates: if offsets_index != 0: raise NotImplementedError( "nice coordinate system is only implemented for offsets_index 0" ) labels = 'nice' c2i = get_pegasus_to_nice_fn() elif coordinates: c2i = lambda *q: q labels = 'coordinate' else: labels = 'int' def c2i(u, w, k, z): return u * 12 * m * m1 + w * 12 * m1 + k * m1 + z construction = (("family", "pegasus"), ("rows", m), ("columns", m), ("tile", 12), ("vertical_offsets", offset_lists[0]), ("horizontal_offsets", offset_lists[1]), ("data", data), ("labels", labels)) G.graph.update(construction) max_size = m * (m - 1) * 24 # max number of nodes G can have if edge_list is None: if nice_coordinates: fabric_start = 4, 8 fabric_end = 8, 4 elif fabric_only: fabric_start = min(s for s in offset_lists[1]), min( s for s in offset_lists[0]) fabric_end = 12 - max(s for s in offset_lists[1]), 12 - max( s for s in offset_lists[0]) else: fabric_end = fabric_start = 0, 0 G.add_edges_from((c2i(u, w, k, z), c2i(u, w, k, z + 1)) for u in (0, 1) for w in range(m) for k in range(fabric_start[u] if w == 0 else 0, 12 - (fabric_end[u] if w == m1 else 0)) for z in range(m1 - 1)) G.add_edges_from((c2i(u, w, k, z), c2i(u, w, k + 1, z)) for u in (0, 1) for w in range(m) for k in range(fabric_start[u] if w == 0 else 0, 12 - (fabric_end[u] if w == m1 else 0), 2) for z in range(m1)) off0, off1 = offset_lists def qfilter(u, w, k, z): if w == 0: return k >= fabric_start[u] if w == m1: return k < 12 - fabric_end[u] return True def efilter(e): return qfilter(*e[0]) and qfilter(*e[1]) internal_couplers = ( ((0, w, k, z), (1, z + (kk < off0[k]), kk, w - (k < off1[kk]))) for w in range(m) for kk in range(12) for k in range(0 if w else off1[kk], 12 if w < m1 else off1[kk]) for z in range(m1)) G.add_edges_from( (c2i(*e[0]), c2i(*e[1])) for e in internal_couplers if efilter(e)) else: G.add_edges_from(edge_list) if node_list is not None: nodes = set(node_list) G.remove_nodes_from(set(G) - nodes) G.add_nodes_from(nodes) # for singleton nodes if data: v = 0 for u in range(2): for w in range(m): for k in range(12): for z in range(m1): q = u, w, k, z if nice_coordinates: p = c2i(*q) if p in G: G.nodes[p]['linear_index'] = v G.nodes[p]['pegasus_index'] = q elif coordinates: if q in G: G.nodes[q]['linear_index'] = v else: if v in G: G.nodes[v]['pegasus_index'] = q v += 1 return G
def weighted_maximum_cut(G, sampler=None, **sampler_args): """Returns an approximate weighted maximum cut. Defines an Ising problem with ground states corresponding to a weighted maximum cut and uses the sampler to sample from it. A weighted maximum cut is a subset S of the vertices of G that maximizes the sum of the edge weights between S and its complementary subset. Parameters ---------- G : NetworkX graph The graph on which to find a weighted maximum cut. Each edge in G should have a numeric `weight` attribute. sampler A binary quadratic model sampler. A sampler is a process that samples from low energy states in models defined by an Ising equation or a Quadratic Unconstrained Binary Optimization Problem (QUBO). A sampler is expected to have a 'sample_qubo' and 'sample_ising' method. A sampler is expected to return an iterable of samples, in order of increasing energy. If no sampler is provided, one must be provided using the `set_default_sampler` function. sampler_args Additional keyword parameters are passed to the sampler. Returns ------- S : set A maximum cut of G. Example ------- This example uses a sampler from `dimod <https://github.com/dwavesystems/dimod>`_ to find a weighted maximum cut for a graph of a Chimera unit cell. The graph is created using the `chimera_graph()` function with weights added to all its edges such that those incident to nodes {6, 7} have weight -1 while the others are +1. A weighted maximum cut should cut as many of the latter and few of the former as possible. >>> import dimod >>> import dwave_networkx as dnx >>> samplerSA = dimod.SimulatedAnnealingSampler() >>> G = dnx.chimera_graph(1, 1, 4) >>> for u, v in G.edges: ....: if (u >= 6) | (v >=6): ....: G[u][v]['weight']=-1 ....: else: G[u][v]['weight']=1 ....: >>> dnx.weighted_maximum_cut(G, samplerSA) {4, 5} Notes ----- Samplers by their nature may not return the optimal solution. This function does not attempt to confirm the quality of the returned sample. """ # In order to form the Ising problem, we want to increase the # energy by 1 for each edge between two nodes of the same color. # The linear biases can all be 0. h = {v: 0. for v in G} try: J = {(u, v): G[u][v]['weight'] for u, v in G.edges} except KeyError: raise DWaveNetworkXException("edges must have 'weight' attribute") # draw the lowest energy sample from the sampler response = sampler.sample_ising(h, J, **sampler_args) sample = next(iter(response)) return set(v for v in G if sample[v] >= 0)