示例#1
0
def frustrated_loop(graph,
                    num_cycles,
                    R=float('inf'),
                    cycle_predicates=tuple(),
                    max_failed_cycles=100,
                    planted_solution=None,
                    seed=None):
    """Generate a frustrated-loop problem.

    A generic frustrated-loop (FL) problem is a sum of Hamiltonians, each generated
    from a single "good" loop, as follows:

    1. Generate a loop by random walking on the support graph.
    2. If the cycle meets provided predicates, continue; if not, return to  step 1.
    3. Choose one edge of the loop to be anti-ferromagnetic; all other edges are
       ferromagnetic.
    4. Add the loop's coupler values to the FL problem.
       If at any time the magnitude of a coupler in the FL problem exceeds a given
       recision `R`, remove that coupler from consideration in the loop generation
       procedure.

    This is a generic generator of FL problems that encompasses both the original
    FL problem definition\ [#HJARTL]_ and the limited FL problem
    definition\ [#KLH]_\ .

    Args:
        graph (int/tuple[nodes, edges]/list[edge]/:obj:`~networkx.Graph`):
            The graph to build the frustrated loops on. Either an integer n,
            interpreted as a complete graph of size n, a nodes/edges pair, a
            list of edges or a NetworkX graph.

        num_cyles (int):
            Desired number of frustrated cycles.

        R (int, optional, default=inf):
            Maximum interaction weight.

        cycle_predicates (tuple[function], optional):
            An iterable of functions, which should accept a cycle and return a bool.

        max_failed_cycles (int, optional, default=100):
            Maximum number of failures to find a cycle before terminating.

        planted_solution (dict, optional, default=None):
            Other solutions with the same energy may exist, but the energy value
            of the (possibly degenerate) ground state will hold. If None,
            planted_solution is: {v:-1 for v in graph}

        seed (int, optional, default=None):
            Random seed.

    .. [#HJARTL] Hen, I., J. Job, T. Albash, T.F. Rønnow, M. Troyer, D. Lidar. Probing for quantum
        speedup in spin glass problems with planted solutions. https://arxiv.org/abs/1502.01663v2

    .. [#KLH] King, A.D., T. Lanting, R. Harris. Performance of a quantum annealer on range-limited
        constraint satisfaction problems. https://arxiv.org/abs/1502.02098

    """
    nodes, edges = graph
    if num_cycles <= 0:
        raise ValueError("num_cycles should be a positive integer")
    if R <= 0:
        raise ValueError("R should be a positive integer")
    if max_failed_cycles <= 0:
        raise ValueError("max_failed_cycles should be a positive integer")
    if planted_solution is None:
        planted_solution = {v: -1 for v in nodes}
    if seed is None:
        seed = numpy.random.randint(2**32, dtype=np.uint32)
    r = numpy.random.RandomState(seed)

    adj = {v: set() for v in nodes}
    for u, v in edges:
        if u in adj:
            adj[u].add(v)
        else:
            adj[u] = {v}
        if v in adj:
            adj[v].add(u)
        else:
            adj[v] = {u}
    bqm = BinaryQuadraticModel({v: 0.0
                                for v in nodes}, {edge: 0.0
                                                  for edge in edges}, 0.0,
                               SPIN)

    failed_cycles = 0
    good_cycles = 0
    while good_cycles < num_cycles and failed_cycles < max_failed_cycles:

        cycle = _random_cycle(adj, r)

        # if the cycle failed or it is otherwise invalid, mark as failed and continue
        if cycle is None or not all(pred(cycle) for pred in cycle_predicates):
            failed_cycles += 1
            continue
        good_cycles += 1

        # If its a good cycle, modify J with it, according to plated solution
        cycle_J = {}
        for i, c in enumerate(cycle):
            u, v = cycle[i - 1], cycle[i]
            J = -planted_solution[u] * planted_solution[v]
            cycle_J[(u, v)] = J

        # randomly select an edge and flip it
        idx = r.randint(len(cycle))
        cycle_J[(cycle[idx - 1], cycle[idx])] *= -1.

        # update the bqm
        bqm.add_interactions_from(cycle_J)
        for u, v in cycle_J:
            if abs(bqm.adj[u][v]) >= R:
                adj[u].remove(v)
                adj[v].remove(u)

    if good_cycles < num_cycles:
        raise RuntimeError

    return bqm
示例#2
0
文件: fcl.py 项目: sajidsaleem/dimod
def frustrated_loop(graph, num_cycles, R=float('inf'), cycle_predicates=tuple(), max_failed_cycles=100):
    """Generate a frustrated loop problem.

    A (generic) frustrated loop (FL) problem is a sum of Hamiltonians, each generated from a single
    "good" loop.
        1. Generate a loop by random walking on the support graph.
        2. If the cycle is "good" (according to provided predicates), continue, else go to 1.
        3. Choose one edge of the loop to be anti-ferromagnetic; all other edges are ferromagnetic.
        4. Add the loop's coupler values to the FL problem.
    If at any time the magnitude of a coupler in the FL problem exceeds a given precision `R`,
    remove that coupler from consideration in the loop generation procedure.

    This is a generic generator of FL problems that encompasses both the original FL problem
    definition from [HJARTL]_ and the limited FL problem definition from [KLH]_

    Args:
        graph (int/tuple[nodes, edges]/:obj:`~networkx.Graph`):
            The graph to build the frustrated loops on. Either an integer n, interpreted as a
            complete graph of size n, or a nodes/edges pair, or a NetworkX graph.

        num_cyles (int):
            Desired number of frustrated cycles.

        R (int, optional, default=inf):
            Maximum interaction weight.

        cycle_predicates (tuple[function], optional):
            An iterable of functions, which should accept a cycle and return a bool.

        max_failed_cycles (int, optional, default=100):
            Maximum number of failures to find a cycle before terminating.

    .. [HJARTL] Hen, I., J. Job, T. Albash, T.F. Rønnow, M. Troyer, D. Lidar. Probing for quantum
        speedup in spin glass problems with planted solutions. https://arxiv.org/abs/1502.01663v2

    .. [KLH] King, A.D., T. Lanting, R. Harris. Performance of a quantum annealer on range-limited
        constraint satisfaction problems. https://arxiv.org/abs/1502.02098

    """
    nodes, edges = graph
    assert num_cycles > 0
    assert R > 0
    assert max_failed_cycles > 0

    # G = nx.Graph(edges)
    # J = collections.defaultdict(int)
    adj = {v: set() for v in nodes}
    for u, v in edges:
        if u in adj:
            adj[u].add(v)
        else:
            adj[u] = {v}
        if v in adj:
            adj[v].add(u)
        else:
            adj[v] = {u}
    bqm = BinaryQuadraticModel({v: 0.0 for v in nodes}, {edge: 0.0 for edge in edges}, 0.0, SPIN)

    failed_cycles = 0
    good_cycles = 0
    while good_cycles < num_cycles and failed_cycles < max_failed_cycles:

        cycle = _random_cycle(adj)

        # if the cycle failed or it is otherwise invalid, mark as failed and continue
        if cycle is None or not all(pred(cycle) for pred in cycle_predicates):
            failed_cycles += 1
            continue

        # If its a good cycle, modify J with it.
        good_cycles += 1

        cycle_J = {(cycle[i - 1], cycle[i]): -1. for i in range(len(cycle))}

        # randomly select an edge and flip it
        idx = random.randrange(len(cycle))
        cycle_J[(cycle[idx - 1], cycle[idx])] *= -1.

        # update the bqm
        bqm.add_interactions_from(cycle_J)

        for u, v in cycle_J:
            if abs(bqm.adj[u][v]) >= R:
                adj[u].remove(v)
                adj[v].remove(u)

    if good_cycles < num_cycles:
        raise RuntimeError

    return bqm