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
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)
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