Esempio n. 1
0
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)
Esempio n. 2
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')
Esempio n. 3
0
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
Esempio n. 4
0
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
Esempio n. 5
0
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
Esempio n. 6
0
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)