Beispiel #1
0
def embed_bqm(source_bqm,
              embedding,
              target_adjacency,
              chain_strength=1.0,
              smear_vartype=None):
    """Embed a binary quadratic model onto a target graph.
    Args:
        source_bqm (:obj:`.BinaryQuadraticModel`):
            Binary quadratic model to embed.
        embedding (dict):
            Mapping from source graph to target graph as a dict of form {s: {t, ...}, ...},
            where s is a source-model variable and t is a target-model variable.
        target_adjacency (dict/:obj:`networkx.Graph`):
            Adjacency of the target graph as a dict of form {t: Nt, ...},
            where t is a variable in the target graph and Nt is its set of neighbours.
        chain_strength (float, optional):
            Magnitude of the quadratic bias (in SPIN-space) applied between
            variables to create chains, with the energy penalty of chain breaks
            set to 2 * `chain_strength`.
        smear_vartype (:class:`.Vartype`, optional, default=None):
            Determines whether the linear bias of embedded variables is smeared
            (the specified value is evenly divided as biases of a chain in the
            target graph) in SPIN or BINARY space. Defaults to the
            :class:`.Vartype` of `source_bqm`.
    Returns:
        :obj:`.BinaryQuadraticModel`: Target binary quadratic model.
    Examples:
        This example embeds a triangular binary quadratic model representing
        a :math:`K_3` clique into a square target graph by mapping variable `c`
        in the source to nodes `2` and `3` in the target.
        >>> import networkx as nx
        ...
        >>> target = nx.cycle_graph(4)
        >>> # Binary quadratic model for a triangular source graph
        >>> h = {'a': 0, 'b': 0, 'c': 0}
        >>> J = {('a', 'b'): 1, ('b', 'c'): 1, ('a', 'c'): 1}
        >>> bqm = dimod.BinaryQuadraticModel.from_ising(h, J)
        >>> # Variable c is a chain
        >>> embedding = {'a': {0}, 'b': {1}, 'c': {2, 3}}
        >>> # Embed and show the chain strength
        >>> target_bqm = dwave.embedding.embed_bqm(bqm, embedding, target)
        >>> target_bqm.quadratic[(2, 3)]
        -1.0
        >>> print(target_bqm.quadratic)  # doctest: +SKIP
        {(0, 1): 1.0, (0, 3): 1.0, (1, 2): 1.0, (2, 3): -1.0}
    See also:
        :func:`.embed_ising`, :func:`.embed_qubo`
    """
    if smear_vartype is dimod.SPIN and source_bqm.vartype is dimod.BINARY:
        return embed_bqm(source_bqm.spin,
                         embedding,
                         target_adjacency,
                         chain_strength=chain_strength,
                         smear_vartype=None).binary
    elif smear_vartype is dimod.BINARY and source_bqm.vartype is dimod.SPIN:
        return embed_bqm(source_bqm.binary,
                         embedding,
                         target_adjacency,
                         chain_strength=chain_strength,
                         smear_vartype=None).spin

    # create a new empty binary quadratic model with the same class as source_bqm
    try:
        target_bqm = source_bqm.base.empty(source_bqm.vartype)
    except AttributeError:
        # dimod < 0.9.0
        target_bqm = source_bqm.empty(source_bqm.vartype)

    # add the offset
    target_bqm.add_offset(source_bqm.offset)

    # start with the linear biases, spreading the source bias equally over the target variables in
    # the chain
    for v, bias in iteritems(source_bqm.linear):

        if v in embedding:
            chain = embedding[v]
        else:
            raise MissingChainError(v)

        if any(u not in target_adjacency for u in chain):
            raise InvalidNodeError(
                v, next(u not in target_adjacency for u in chain))

        try:
            b = bias / len(chain)
        except ZeroDivisionError:
            raise MissingChainError(v)

        target_bqm.add_variables_from({u: b for u in chain})

    # next up the quadratic biases, spread the quadratic biases evenly over the available
    # interactions
    for (u, v), bias in iteritems(source_bqm.quadratic):
        available_interactions = {(s, t)
                                  for s in embedding[u] for t in embedding[v]
                                  if s in target_adjacency[t]}

        if not available_interactions:
            raise MissingEdgeError(u, v)

        b = bias / len(available_interactions)

        target_bqm.add_interactions_from(
            (u, v, b) for u, v in available_interactions)

    for chain in itervalues(embedding):

        # in the case where the chain has length 1, there are no chain quadratic biases, but we
        # none-the-less want the chain variables to appear in the target_bqm
        if len(chain) == 1:
            v, = chain
            target_bqm.add_variable(v, 0.0)
            continue

        quadratic_chain_biases = chain_to_quadratic(chain, target_adjacency,
                                                    chain_strength)
        # this is in spin, but we need to respect the vartype
        if target_bqm.vartype is dimod.SPIN:
            target_bqm.add_interactions_from(quadratic_chain_biases)
        else:
            # do the vartype converstion
            for (u, v), bias in quadratic_chain_biases.items():
                target_bqm.add_interaction(u, v, 4 * bias)
                target_bqm.add_variable(u, -2 * bias)
                target_bqm.add_variable(v, -2 * bias)
                target_bqm.add_offset(bias)

        # add the energy for satisfied chains to the offset
        energy_diff = -sum(itervalues(quadratic_chain_biases))
        target_bqm.add_offset(energy_diff)

    return target_bqm
Beispiel #2
0
    def embed_bqm(self, source_bqm, chain_strength=None, smear_vartype=None):
        """Embed a binary quadratic model onto a target graph.

        Args:
            source_bqm (:class:`~dimod.BinaryQuadraticModel`):
                Binary quadratic model to embed.

            chain_strength (float/mapping/callable, optional):
                Sets the coupling strength between qubits representing variables 
                that form a :term:`chain`. Mappings should specify the required 
                chain strength for each variable. Callables should accept the BQM 
                and embedding and return a float or mapping. By default, 
                `chain_strength` is calculated with
                :func:`~dwave.embedding.chain_strength.uniform_torque_compensation`.

            smear_vartype (:class:`.Vartype`, optional, default=None):
                Determines whether the linear bias of embedded variables is
                smeared (the specified value is evenly divided as biases of a
                chain in the target graph) in SPIN or BINARY space. Defaults to
                the :class:`.Vartype` of `source_bqm`.

        Returns:
            :obj:`.BinaryQuadraticModel`: Target binary quadratic model.

        Examples:
            This example embeds a triangular binary quadratic model representing
            a :math:`K_3` clique into a square target graph by mapping variable
            `c` in the source to nodes `2` and `3` in the target.

            >>> import networkx as nx
            ...
            >>> target = nx.cycle_graph(4)
            >>> # Binary quadratic model for a triangular source graph
            >>> h = {'a': 0, 'b': 0, 'c': 0}
            >>> J = {('a', 'b'): 1, ('b', 'c'): 1, ('a', 'c'): 1}
            >>> bqm = dimod.BinaryQuadraticModel.from_ising(h, J)
            >>> # Variable c is a chain
            >>> embedding = {'a': {0}, 'b': {1}, 'c': {2, 3}}
            >>> # Embed and show the chain strength
            >>> target_bqm = dwave.embedding.embed_bqm(bqm, embedding, target)
            >>> target_bqm.quadratic[(2, 3)]
            -1.9996979771955565
            >>> print(target_bqm.quadratic)  # doctest: +SKIP
            {(0, 1): 1.0, (0, 3): 1.0, (1, 2): 1.0, (2, 3): -1.9996979771955565}


        See also:
            :func:`.embed_ising`, :func:`.embed_qubo`

        """
        return_vartype = source_bqm.vartype
        if smear_vartype is dimod.SPIN:
            source_bqm = source_bqm.spin
        elif smear_vartype is dimod.BINARY:
            source_bqm = source_bqm.binary
        else:
            smear_vartype = source_bqm.vartype

        # we need this check to support dimod 0.9.x, where there was one bqm
        # type that was not shapeable
        if (isinstance(source_bqm, (AdjArrayBQM, SpinView, BinaryView))
                and not source_bqm.shapeable()):
            # this will never happen in dimod>=0.10.0
            target_bqm = dimod.AdjVectorBQM.empty(smear_vartype)
        else:
            try:
                # dimod 0.9.x
                target_bqm = source_bqm.base.empty(smear_vartype)
            except AttributeError:
                target_bqm = source_bqm.empty(smear_vartype)

        # add the offset
        target_bqm.offset += source_bqm.offset

        if chain_strength is None:
            chain_strength = uniform_torque_compensation

        if callable(chain_strength):
            chain_strength = chain_strength(source_bqm, self)

        self._chain_strength = chain_strength

        if isinstance(chain_strength, abc.Mapping):
            strength_iter = (chain_strength[v] for v in source_bqm.linear)
        else:
            strength_iter = itertools.repeat(chain_strength)

        offset = 0
        # spread the linear source bias equally over the target variables in the
        # chain and add chain edges as necessary
        for (v, bias), strength in zip(source_bqm.linear.items(),
                                       strength_iter):
            chain = self.get(v)
            if chain is None:
                raise MissingChainError(v)

            #we check that this is nonzero in __init__
            b = bias / len(chain)

            target_bqm.add_variables_from({u: b for u in chain})

            if len(chain) == 1:
                # in the case where the chain has length 1, there are no chain
                # quadratic biases, but we none-the-less want the chain
                # variables to appear in the target_bqm
                q, = chain
                target_bqm.add_variable(q, 0.0)
            elif smear_vartype is dimod.SPIN:
                for p, q in self.chain_edges(v):
                    target_bqm.add_interaction(p, q, -strength)
                    offset += strength
            else:
                # this is in spin, but we need to respect the vartype
                for p, q in self.chain_edges(v):
                    target_bqm.add_interaction(p, q, -4 * strength)
                    target_bqm.add_variable(p, 2 * strength)
                    target_bqm.add_variable(q, 2 * strength)

        target_bqm.offset += offset

        # next up the quadratic biases, spread the quadratic biases evenly over
        # the available interactions
        for (u, v), bias in source_bqm.quadratic.items():
            interactions = list(self.interaction_edges(u, v))

            if not interactions:
                raise MissingEdgeError(u, v)

            b = bias / len(interactions)

            target_bqm.add_interactions_from(
                (u, v, b) for u, v in interactions)

        # we made the target BQM so we can safely mutate it in-place
        return target_bqm.change_vartype(return_vartype, inplace=True)
Beispiel #3
0
    def embed_bqm(self, source_bqm, chain_strength = 1.0, smear_vartype = None):
        """Embed a binary quadratic model onto a target graph.

        Args:
            source_bqm (:obj:`.BinaryQuadraticModel`):
                Binary quadratic model to embed.

            chain_strength (float/mapping/callable, optional):
                Magnitude of the quadratic bias (in SPIN-space) applied between
                variables to create chains, with the energy penalty of chain
                breaks set to 2 * `chain_strength`.  If a mapping is passed, a 
                chain-specific strength is applied.  If a callable is passed, it
                will be called on `chain_strength(source_bqm, self)` and should
                return a float or mapping, to be interpreted as above.

            smear_vartype (:class:`.Vartype`, optional, default=None):
                Determines whether the linear bias of embedded variables is
                smeared (the specified value is evenly divided as biases of a
                chain in the target graph) in SPIN or BINARY space. Defaults to
                the :class:`.Vartype` of `source_bqm`.

        Returns:
            :obj:`.BinaryQuadraticModel`: Target binary quadratic model.

        Examples:
            This example embeds a triangular binary quadratic model representing
            a :math:`K_3` clique into a square target graph by mapping variable 
            `c` in the source to nodes `2` and `3` in the target.

            >>> import networkx as nx
            ...
            >>> target = nx.cycle_graph(4)
            >>> # Binary quadratic model for a triangular source graph
            >>> h = {'a': 0, 'b': 0, 'c': 0}
            >>> J = {('a', 'b'): 1, ('b', 'c'): 1, ('a', 'c'): 1}
            >>> bqm = dimod.BinaryQuadraticModel.from_ising(h, J)
            >>> # Variable c is a chain
            >>> embedding = {'a': {0}, 'b': {1}, 'c': {2, 3}}
            >>> # Embed and show the chain strength
            >>> target_bqm = dwave.embedding.embed_bqm(bqm, embedding, target)
            >>> target_bqm.quadratic[(2, 3)]
            -1.0
            >>> print(target_bqm.quadratic)  # doctest: +SKIP
            {(0, 1): 1.0, (0, 3): 1.0, (1, 2): 1.0, (2, 3): -1.0}


        See also:
            :func:`.embed_ising`, :func:`.embed_qubo`

        """
        return_vartype = source_bqm.vartype
        if smear_vartype is dimod.SPIN:
            source_bqm = source_bqm.spin
        elif smear_vartype is dimod.BINARY:
            source_bqm = source_bqm.binary
        else:
            smear_vartype = source_bqm.vartype

        # create a new empty binary quadratic model with the same class as
        # source_bqm if it's shapeable, otherwise use AdjVectorBQM
        if source_bqm.shapeable():
            target_bqm = source_bqm.base.empty(smear_vartype)
        else:
            target_bqm = dimod.AdjVectorBQM.empty(smear_vartype)

        # add the offset
        target_bqm.add_offset(source_bqm.offset)

        if callable(chain_strength):
            chain_strength = chain_strength(source_bqm, self)

        if isinstance(chain_strength, abc.Mapping):
            strength_iter = (chain_strength[v] for v in source_bqm.linear)
        else:
            strength_iter = itertools.repeat(chain_strength)

        offset = 0
        # spread the linear source bias equally over the target variables in the
        # chain and add chain edges as necessary
        for (v, bias), strength in zip(source_bqm.linear.items(), strength_iter):
            chain = self.get(v)
            if chain is None:
                raise MissingChainError(v)

            #we check that this is nonzero in __init__
            b = bias / len(chain)

            target_bqm.add_variables_from({u: b for u in chain})

            if len(chain) == 1:
                # in the case where the chain has length 1, there are no chain
                # quadratic biases, but we none-the-less want the chain
                # variables to appear in the target_bqm
                q, = chain
                target_bqm.add_variable(q, 0.0)
            elif smear_vartype is dimod.SPIN:
                for p, q in self.chain_edges(v):
                    target_bqm.add_interaction(p, q, -strength)
                    offset += strength
            else:
                # this is in spin, but we need to respect the vartype
                for p, q in self.chain_edges(v):
                    target_bqm.add_interaction(p, q, -4*strength)
                    target_bqm.add_variable(p, 2*strength)
                    target_bqm.add_variable(q, 2*strength)

        target_bqm.add_offset(offset)

        # next up the quadratic biases, spread the quadratic biases evenly over
        # the available interactions
        for (u, v), bias in source_bqm.quadratic.items():
            interactions = list(self.interaction_edges(u, v))

            if not interactions:
                raise MissingEdgeError(u, v)

            b = bias / len(interactions)

            target_bqm.add_interactions_from((u, v, b) for u, v in interactions)

        if return_vartype is dimod.BINARY:
            return target_bqm.binary
        elif return_vartype is dimod.SPIN:
            return target_bqm.spin