def __init__(self, *, failover: bool = False, retry_interval: Number = -1, **config): self.child = DWaveSampler(failover=False, **config) self.failover = failover self.retry_interval = retry_interval
def __init__(self, **config): # get the QPU with the most qubits available, subject to other # constraints specified in **config self.child = child = DWaveSampler(order_by='-num_active_qubits', **config) # do some topology checking try: topology_type = child.properties['topology']['type'] shape = child.properties['topology']['shape'] except KeyError: raise ValueError("given sampler has unknown topology format") # We need a networkx graph with certain properties. In the # future it would be good for DWaveSampler to handle this. # See https://github.com/dwavesystems/dimod/issues/647 if topology_type == 'chimera': G = dnx.chimera_graph(*shape, node_list=child.nodelist, edge_list=child.edgelist, ) elif topology_type == 'pegasus': G = dnx.pegasus_graph(shape[0], node_list=child.nodelist, edge_list=child.edgelist, ) else: raise ValueError("unknown topology type") self.target_graph = G # get the energy range try: self.qpu_linear_range = child.properties['h_range'] self.qpu_quadratic_range = child.properties.get( 'extended_j_range', child.properties['j_range']) except KeyError as err: # for backwards compatibility with old software solvers if child.solver.is_software: self.qpu_linear_range = [-2, 2] self.qpu_quadratic_range = [-1, 1] else: raise err
def solve_dwave(self): X = Array.create('X', shape=(self.n), vartype='SPIN') H0 = 0 for (u, v) in self.G.edges: H0 -= (1 - X[u] * X[v]) * self.G.edges[u, v]['w'] model = H0.compile() bqm = model.to_dimod_bqm() response = EmbeddingComposite(DWaveSampler()).sample(bqm, num_reads=100) result = model.decode_dimod_response(response, topk=1) optimized_v = result[0][0]['X'] self.v_group = [] for i in range(self.n): self.v_group.append(optimized_v[i]) self.cutted = [] for (u, v) in self.G.edges: if optimized_v[u] != optimized_v[v]: self.cutted.append([u, v])
class DWaveCliqueSampler(dimod.Sampler): """A sampler for solving clique binary quadratic models on the D-Wave system. This sampler wraps :func:`~minorminer.busclique.find_clique_embedding` to generate embeddings with even chain length. These embeddings work well for dense binary quadratic models. For sparse models, using :class:`.EmbeddingComposite` with :class:`.DWaveSampler` is preferred. Configuration such as :term:`solver` selection is similar to that of :class:`.DWaveSampler`. Args: failover (optional, default=False): Switch to a new QPU in the rare event that the currently connected system goes offline. Note that different QPUs may have different hardware graphs and a failover will result in a regenerated :attr:`.nodelist`, :attr:`.edgelist`, :attr:`.properties` and :attr:`.parameters`. retry_interval (optional, default=-1): The amount of time (in seconds) to wait to poll for a solver in the case that no solver is found. If `retry_interval` is negative then it will instead propogate the `SolverNotFoundError` to the user. **config: Keyword arguments, as accepted by :class:`.DWaveSampler` Examples: This example creates a BQM based on a 6-node clique (complete graph), with random :math:`\pm 1` values assigned to nodes, and submits it to a D-Wave system. Parameters for communication with the system, such as its URL and an autentication token, are implicitly set in a configuration file or as environment variables, as described in `Configuring Access to D-Wave Solvers <https://docs.ocean.dwavesys.com/en/stable/overview/sapi.html>`_. >>> from dwave.system import DWaveCliqueSampler >>> import dimod ... >>> bqm = dimod.generators.ran_r(1, 6) ... >>> sampler = DWaveCliqueSampler() # doctest: +SKIP >>> sampler.largest_clique_size > 5 # doctest: +SKIP True >>> sampleset = sampler.sample(bqm, num_reads=100) # doctest: +SKIP """ def __init__(self, *, failover: bool = False, retry_interval: Number = -1, **config): self.child = DWaveSampler(failover=False, **config) self.failover = failover self.retry_interval = retry_interval @property def parameters(self) -> dict: try: return self._parameters except AttributeError: pass self._parameters = parameters = self.child.parameters.copy() # this sampler handles scaling parameters.pop('auto_scale', None) parameters.pop('bias_range', None) parameters.pop('quadratic_range', None) return parameters @property def properties(self) -> dict: try: return self._properties except AttributeError: pass self._properties = dict(qpu_properties=self.child.properties) return self.properties @property def largest_clique_size(self) -> int: """The maximum number of variables that can be embedded.""" return len(self.largest_clique()) @property def qpu_linear_range(self) -> Tuple[float, float]: """Range of linear biases allowed by the QPU.""" try: return self._qpu_linear_range except AttributeError: pass # get the energy range try: energy_range = tuple(self.child.properties['h_range']) except KeyError as err: # for backwards compatibility with old software solvers if self.child.solver.is_software: energy_range = (-2, 2) else: raise err self._qpu_linear_range = energy_range return energy_range @property def qpu_quadratic_range(self) -> Tuple[float, float]: """Range of quadratic biases allowed by the QPU.""" try: return self._qpu_quadratic_range except AttributeError: pass # get the energy range try: energy_range = tuple( self.child.properties.get('extended_j_range', self.child.properties['j_range'])) except KeyError as err: # for backwards compatibility with old software solvers if self.child.solver.is_software: energy_range = (-1, 1) else: raise err self._qpu_quadratic_range = energy_range return energy_range @property def target_graph(self) -> nx.Graph: """The QPU topology.""" try: return self._target_graph except AttributeError: pass self._target_graph = G = self.child.to_networkx_graph() return G def clique(self, variables): """Return a clique embedding of the given size. Args: variables (int/collection): Source variables. If an integer, the variables embedded are labelled `[0,n)`. Returns: dict: The clique embedding. """ return find_clique_embedding(variables, self.target_graph) def largest_clique(self): """The clique embedding with the maximum number of source variables. Returns: dict: The clique embedding with the maximum number of source variables. """ return busgraph_cache(self.target_graph).largest_clique() def trigger_failover(self): """Trigger a failover and connect to a new solver. retry_interval (number, optional): The amount of time (in seconds) to wait to poll for a solver in the case that no solver is found. If `retry_interval` is negative then it will instead propogate the `SolverNotFoundError` to the user. Defaults to :attr:`DWaveSampler.retry_interval`. """ self.child.trigger_failover() try: del self._target_graph except AttributeError: pass try: del self._qpu_linear_range except AttributeError: pass try: del self._qpu_quadratic_range except AttributeError: pass @_failover def sample(self, bqm, chain_strength=None, **kwargs): """Sample from the specified binary quadratic model. Args: bqm (:class:`~dimod.BinaryQuadraticModel`): Any binary quadratic model with up to :attr:`.largest_clique_size` variables. This BQM is embedded using a clique embedding. 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`. **kwargs: Optional keyword arguments for the sampling method, specified per solver in :attr:`.parameters`. D-Wave System Documentation's `solver guide <https://docs.dwavesys.com/docs/latest/doc_solver_ref.html>`_ describes the parameters and properties supported on the D-Wave system. Note that `auto_scale` is not supported by this sampler, because it scales the problem as part of the embedding process. Returns: :class:`~dimod.SampleSet`: Sample set constructed from a (non-blocking) :class:`~concurrent.futures.Future`-like object. """ # some arguments should not be overwritten if 'auto_scale' in kwargs: raise TypeError("sample() got an unexpected keyword argument " "'auto_scale'") if 'bias_range' in kwargs: raise TypeError("sample() got an unexpected keyword argument " "'bias_range'") if 'quadratic_range' in kwargs: raise TypeError("sample() got an unexpected keyword argument " "'quadratic_range'") # handle circular import. todo: fix from dwave.system.composites.embedding import FixedEmbeddingComposite # get the embedding embedding = find_clique_embedding(bqm.variables, self.target_graph, use_cache=True) # returns an empty embedding when the BQM is too large if not embedding and bqm.num_variables: raise ValueError("Cannot embed given BQM (size {}), sampler can " "only handle problems of size {}".format( len(bqm.variables), self.largest_clique_size)) assert bqm.num_variables == len(embedding) # sanity check # scaling only make sense in Ising space original_bqm = bqm if bqm.vartype is not dimod.SPIN: bqm = bqm.change_vartype(dimod.SPIN, inplace=False) sampler = FixedEmbeddingComposite( ScaleComposite(_QubitCouplingComposite(self.child)), embedding) if 'auto_scale' in self.child.parameters: kwargs['auto_scale'] = False sampleset = sampler.sample(bqm, bias_range=self.qpu_linear_range, quadratic_range=self.qpu_quadratic_range, chain_strength=chain_strength, **kwargs) # change_vartype is non-blocking return sampleset.change_vartype(original_bqm.vartype)