Source code for dwave.system.samplers.leap_hybrid_sampler

# Copyright 2020 D-Wave Systems Inc.
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.

"""
A :std:doc:`dimod sampler <oceandocs:docs_dimod/reference/samplers>` for Leap's hybrid solvers.
"""

from typing import Any, Dict, List, Optional

import numpy as np
from warnings import warn
from numbers import Number
from collections import abc

import dimod

try:
    # dimod 0.10.x
    from dimod.binary.binary_quadratic_model import BQM

    bqm_to_file = BQM.to_file
except ImportError:
    # dimod 0.9.x
    from dimod import AdjVectorBQM as BQM
    from dimod.serialization.fileview import FileView

    bqm_to_file = FileView

from dwave.cloud import Client
from dwave.system.utilities import classproperty, FeatureFlags


__all__ = ['LeapHybridSampler',
           'LeapHybridBQMSampler',
           'LeapHybridDQMSampler',
           'LeapHybridCQMSampler',
           ]


[docs]class LeapHybridSampler(dimod.Sampler): """A class for using Leap's cloud-based hybrid BQM solvers. Leap’s quantum-classical hybrid BQM solvers are intended to solve arbitrary application problems formulated as binary quadratic models (BQM). You can configure your :term:`solver` selection and usage by setting parameters, hierarchically, in a configuration file, as environment variables, or explicitly as input arguments, as described in `D-Wave Cloud Client <https://docs.ocean.dwavesys.com/en/stable/docs_cloud/sdk_index.html>`_. :ref:`dwave-cloud-client <sdk_index_cloud>`'s :meth:`~dwave.cloud.client.Client.get_solvers` method filters solvers you have access to by `solver properties <https://docs.dwavesys.com/docs/latest/c_solver_properties.html>`_ ``category=hybrid`` and ``supported_problem_type=bqm``. By default, online hybrid BQM solvers are returned ordered by latest ``version``. The default specification for filtering and ordering solvers by features is available as :attr:`.default_solver` property. Explicitly specifying a solver in a configuration file, an environment variable, or keyword arguments overrides this specification. See the example below on how to extend it instead. Args: **config: Keyword arguments passed to :meth:`dwave.cloud.client.Client.from_config`. Examples: This example builds a random sparse graph and uses a hybrid solver to find a maximum independent set. >>> import dimod >>> import networkx as nx >>> import dwave_networkx as dnx >>> import numpy as np >>> from dwave.system import LeapHybridSampler ... >>> # Create a maximum-independent set problem from a random graph >>> problem_node_count = 300 >>> G = nx.random_geometric_graph(problem_node_count, radius=0.0005*problem_node_count) >>> qubo = dnx.algorithms.independent_set.maximum_weighted_independent_set_qubo(G) >>> bqm = dimod.BQM.from_qubo(qubo) ... >>> # Find a good solution >>> sampler = LeapHybridSampler() # doctest: +SKIP >>> sampleset = sampler.sample(bqm) # doctest: +SKIP This example specializes the default solver selection by filtering out bulk BQM solvers. (Bulk solvers are throughput-optimal for heavy/batch workloads, have a higher start-up latency, and are not well suited for live workloads. Not all Leap accounts have access to bulk solvers.) >>> from dwave.system import LeapHybridSampler ... >>> solver = LeapHybridSampler.default_solver >>> solver.update(name__regex=".*(?<!bulk)$") # name shouldn't end with "bulk" >>> sampler = LeapHybridSampler(solver=solver) # doctest: +SKIP >>> sampler.solver # doctest: +SKIP BQMSolver(id='hybrid_binary_quadratic_model_version2') """ _INTEGER_BQM_SIZE_THRESHOLD = 10000 @classproperty def default_solver(cls): """dict: Features used to select the latest accessible hybrid BQM solver.""" return dict(supported_problem_types__contains='bqm', order_by='-properties.version') def __init__(self, **config): # strongly prefer hybrid solvers; requires kwarg-level override config.setdefault('client', 'hybrid') # default to short-lived session to prevent resets on slow uploads config.setdefault('connection_close', True) if FeatureFlags.hss_solver_config_override: # use legacy behavior (override solver config from env/file) solver = config.setdefault('solver', {}) if isinstance(solver, abc.Mapping): solver.update(self.default_solver) # prefer the latest hybrid BQM solver available, but allow for an easy # override on any config level above the defaults (file/env/kwarg) defaults = config.setdefault('defaults', {}) if not isinstance(defaults, abc.Mapping): raise TypeError("mapping expected for 'defaults'") defaults.update(solver=self.default_solver) self.client = Client.from_config(**config) self.solver = self.client.get_solver() # check user-specified solver conforms to our requirements if self.properties.get('category') != 'hybrid': raise ValueError("selected solver is not a hybrid solver.") if 'bqm' not in self.solver.supported_problem_types: raise ValueError("selected solver does not support the 'bqm' problem type.") @property def properties(self) -> Dict[str, Any]: """Solver properties as returned by a SAPI query. `Solver properties <https://docs.dwavesys.com/docs/latest/c_solver_properties.html>`_ are dependent on the selected solver and subject to change. """ try: return self._properties except AttributeError: self._properties = properties = self.solver.properties.copy() return properties @property def parameters(self) -> Dict[str, list]: """Solver parameters in the form of a dict, where keys are keyword parameters accepted by a SAPI query and values are lists of properties in :attr:`~dwave.system.samplers.LeapHybridSampler.properties` for each key. `Solver parameters <https://docs.dwavesys.com/docs/latest/c_solver_parameters.html>`_ are dependent on the selected solver and subject to change. """ try: return self._parameters except AttributeError: parameters = {param: ['parameters'] for param in self.properties['parameters']} parameters.update(label=[]) self._parameters = parameters return parameters
[docs] def sample(self, bqm, time_limit=None, **kwargs): """Sample from the specified binary quadratic model. Args: bqm (:obj:`dimod.BinaryQuadraticModel`): Binary quadratic model. time_limit (int): Maximum run time, in seconds, to allow the solver to work on the problem. Must be at least the minimum required for the number of problem variables, which is calculated and set by default. :meth:`~dwave.system.samplers.LeapHybridSampler.min_time_limit` calculates (and describes) the minimum time for your problem. **kwargs: Optional keyword arguments for the solver, specified in :attr:`~dwave.system.samplers.LeapHybridSampler.parameters`. Returns: :class:`~dimod.SampleSet`: Sample set constructed from a (non-blocking) :class:`~concurrent.futures.Future`-like object. Examples: This example builds a random sparse graph and uses a hybrid solver to find a maximum independent set. >>> import dimod >>> import networkx as nx >>> import dwave_networkx as dnx >>> import numpy as np ... >>> # Create a maximum-independent set problem from a random graph >>> problem_node_count = 300 >>> G = nx.random_geometric_graph(problem_node_count, radius=0.0005*problem_node_count) >>> qubo = dnx.algorithms.independent_set.maximum_weighted_independent_set_qubo(G) >>> bqm = dimod.BQM.from_qubo(qubo) ... >>> # Find a good solution >>> sampler = LeapHybridSampler() # doctest: +SKIP >>> sampleset = sampler.sample(bqm) # doctest: +SKIP """ if not isinstance(bqm, BQM): bqm = BQM(bqm) num_vars = bqm.num_variables if time_limit is None: time_limit = self.min_time_limit(bqm) if not isinstance(time_limit, Number): raise TypeError("time limit must be a number") if time_limit < self.min_time_limit(bqm): msg = ("time limit for problem size {} must be at least {}" ).format(num_vars, self.min_time_limit(bqm)) raise ValueError(msg) # for very large BQMs, it is better to send the unlabelled version, # to save on serializating the labels in both directions. # Note that different hybrid solvers accept different numbers of # variables and they might be lower than this threshold if num_vars > self._INTEGER_BQM_SIZE_THRESHOLD: return self._sample_large(bqm, time_limit=time_limit, **kwargs) return self._sample(bqm, time_limit=time_limit, **kwargs)
def _sample(self, bqm, **kwargs): """Sample from the given BQM.""" with bqm_to_file(bqm, version=2) as fv: sapi_problem_id = self.solver.upload_bqm(fv).result() return self.solver.sample_bqm(sapi_problem_id, **kwargs).sampleset def _sample_large(self, bqm, **kwargs): """Sample from the unlabelled version of the BQM, then apply the labels to the returned sampleset. """ with bqm_to_file(bqm, version=2, ignore_labels=True) as fv: sapi_problem_id = self.solver.upload_bqm(fv).result() sampleset = self.solver.sample_bqm(sapi_problem_id, **kwargs).sampleset # relabel, as of dimod 0.9.5+ this is not blocking mapping = dict(enumerate(bqm.iter_variables())) return sampleset.relabel_variables(mapping)
[docs] def min_time_limit(self, bqm): """Return the minimum `time_limit` accepted for the given problem. The minimum time for a hybrid BQM solver is specified as a piecewise-linear curve defined by a set of floating-point pairs, the `minimum_time_limit` field under :attr:`~dwave.system.samplers.LeapHybridSampler.properties`. The first element in each pair is the number of problem variables; the second is the minimum required time. The minimum time for any number of variables is a linear interpolation calculated on two pairs that represent the relevant range for the given number of variables. Examples: For a solver where `LeapHybridSampler().properties["minimum_time_limit"]` returns `[[1, 0.1], [100, 10.0], [1000, 20.0]]`, the minimum time for a problem 50 variales is 5 seconds (the linear interpolation of the first two pairs that represent problems with between 1 to 100 variables). """ xx, yy = zip(*self.properties["minimum_time_limit"]) return np.interp([bqm.num_variables], xx, yy)[0]
LeapHybridBQMSampler = LeapHybridSampler
[docs]class LeapHybridDQMSampler: """A class for using Leap's cloud-based hybrid DQM solvers. Leap’s quantum-classical hybrid DQM solvers are intended to solve arbitrary application problems formulated as **discrete** quadratic models (DQM). You can configure your :term:`solver` selection and usage by setting parameters, hierarchically, in a configuration file, as environment variables, or explicitly as input arguments, as described in `D-Wave Cloud Client <https://docs.ocean.dwavesys.com/en/stable/docs_cloud/sdk_index.html>`_. :ref:`dwave-cloud-client <sdk_index_cloud>`'s :meth:`~dwave.cloud.client.Client.get_solvers` method filters solvers you have access to by `solver properties <https://docs.dwavesys.com/docs/latest/c_solver_properties.html>`_ ``category=hybrid`` and ``supported_problem_type=dqm``. By default, online hybrid DQM solvers are returned ordered by latest ``version``. The default specification for filtering and ordering solvers by features is available as :attr:`.default_solver` property. Explicitly specifying a solver in a configuration file, an environment variable, or keyword arguments overrides this specification. See the example in :class:`.LeapHybridSampler` on how to extend it instead. Args: **config: Keyword arguments passed to :meth:`dwave.cloud.client.Client.from_config`. Examples: This example solves a small, illustrative problem: a game of rock-paper-scissors. The DQM has two variables representing two hands, with cases for rock, paper, scissors. Quadratic biases are set to produce a lower value of the DQM for cases of variable ``my_hand`` interacting with cases of variable ``their_hand`` such that the former wins over the latter; for example, the interaction of ``rock-scissors`` is set to -1 while ``scissors-rock`` is set to +1. >>> import dimod >>> from dwave.system import LeapHybridDQMSampler ... >>> cases = ["rock", "paper", "scissors"] >>> win = {"rock": "scissors", "paper": "rock", "scissors": "paper"} ... >>> dqm = dimod.DiscreteQuadraticModel() >>> dqm.add_variable(3, label='my_hand') 'my_hand' >>> dqm.add_variable(3, label='their_hand') 'their_hand' >>> for my_idx, my_case in enumerate(cases): ... for their_idx, their_case in enumerate(cases): ... if win[my_case] == their_case: ... dqm.set_quadratic('my_hand', 'their_hand', ... {(my_idx, their_idx): -1}) ... if win[their_case] == my_case: ... dqm.set_quadratic('my_hand', 'their_hand', ... {(my_idx, their_idx): 1}) ... >>> dqm_sampler = LeapHybridDQMSampler() # doctest: +SKIP ... >>> sampleset = dqm_sampler.sample_dqm(dqm) # doctest: +SKIP >>> print("{} beats {}".format(cases[sampleset.first.sample['my_hand']], ... cases[sampleset.first.sample['their_hand']])) # doctest: +SKIP rock beats scissors """ @classproperty def default_solver(self): """dict: Features used to select the latest accessible hybrid DQM solver.""" return dict(supported_problem_types__contains='dqm', order_by='-properties.version') def __init__(self, **config): # strongly prefer hybrid solvers; requires kwarg-level override config.setdefault('client', 'hybrid') # default to short-lived session to prevent resets on slow uploads config.setdefault('connection_close', True) if FeatureFlags.hss_solver_config_override: # use legacy behavior (override solver config from env/file) solver = config.setdefault('solver', {}) if isinstance(solver, abc.Mapping): solver.update(self.default_solver) # prefer the latest hybrid DQM solver available, but allow for an easy # override on any config level above the defaults (file/env/kwarg) defaults = config.setdefault('defaults', {}) if not isinstance(defaults, abc.Mapping): raise TypeError("mapping expected for 'defaults'") defaults.update(solver=self.default_solver) self.client = Client.from_config(**config) self.solver = self.client.get_solver() # check user-specified solver conforms to our requirements if self.properties.get('category') != 'hybrid': raise ValueError("selected solver is not a hybrid solver.") if 'dqm' not in self.solver.supported_problem_types: raise ValueError("selected solver does not support the 'dqm' problem type.") @property def properties(self) -> Dict[str, Any]: """Solver properties as returned by a SAPI query. `Solver properties <https://docs.dwavesys.com/docs/latest/c_solver_properties.html>`_ are dependent on the selected solver and subject to change. """ try: return self._properties except AttributeError: self._properties = properties = self.solver.properties.copy() return properties @property def parameters(self) -> Dict[str, list]: """Solver parameters in the form of a dict, where keys are keyword parameters accepted by a SAPI query and values are lists of properties in :attr:`~dwave.system.samplers.LeapHybridDQMSampler.properties` for each key. `Solver parameters <https://docs.dwavesys.com/docs/latest/c_solver_parameters.html>`_ are dependent on the selected solver and subject to change. """ try: return self._parameters except AttributeError: parameters = {param: ['parameters'] for param in self.properties['parameters']} parameters.update(label=[]) self._parameters = parameters return parameters
[docs] @dimod.decorators.nonblocking_sample_method def sample_dqm(self, dqm, time_limit=None, compress=False, compressed=None, **kwargs): """Sample from the specified discrete quadratic model. Args: dqm (:obj:`dimod.DiscreteQuadraticModel`): Discrete quadratic model (DQM). Note that if `dqm` is a :class:`dimod.CaseLabelDQM`, then :meth:`~dimod.CaseLabelDQM.map_sample` will need to be used to restore the case labels in the returned sample set. time_limit (int, optional): Maximum run time, in seconds, to allow the solver to work on the problem. Must be at least the minimum required for the number of problem variables, which is calculated and set by default. :meth:`~dwave.system.samplers.LeapHybridDQMSampler.min_time_limit` calculates (and describes) the minimum time for your problem. compress (binary, optional): Compresses the DQM data when set to True. Use if your problem somewhat exceeds the maximum allowed size. Compression tends to be slow and more effective on homogenous data, which in this case means it is more likely to help on DQMs with many identical integer-valued biases than ones with random float-valued biases, for example. compressed (binary, optional): Deprecated; please use ``compress`` instead. **kwargs: Optional keyword arguments for the solver, specified in :attr:`~dwave.system.samplers.LeapHybridDQMSampler.parameters`. Returns: :class:`~dimod.SampleSet`: Sample set constructed from a (non-blocking) :class:`~concurrent.futures.Future`-like object. Examples: See the example in :class:`LeapHybridDQMSampler`. """ if time_limit is None: time_limit = self.min_time_limit(dqm) elif time_limit < self.min_time_limit(dqm): raise ValueError("the minimum time limit is {}s ({}s provided)" "".format(self.min_time_limit(dqm), time_limit)) # check the max time_limit if it's available if 'maximum_time_limit_hrs' in self.properties: if time_limit > 60*60*self.properties['maximum_time_limit_hrs']: raise ValueError("time_limit cannot exceed the solver maximum " "of {} hours ({} seconds given)".format( self.properties['maximum_time_limit_hrs'], time_limit)) # we convert to a file here rather than let the cloud-client handle # it because we want to strip the labels and let the user handle # note: SpooledTemporaryFile currently returned by DQM.to_file # does not implement io.BaseIO interface, so we use the underlying # (and internal) file-like object for now if compressed is not None: warn( "Argument 'compressed' is deprecated and in future will raise an " "exception; please use 'compress' instead.", DeprecationWarning, stacklevel=2 ) compress = compressed or compress try: f = dqm.to_file(compress=compress, ignore_labels=True)._file except NotImplementedError: f = dimod.DQM.to_file(dqm, compress=compress, ignore_labels=True)._file future = self.solver.sample_dqm(f, time_limit=time_limit, **kwargs) yield future sampleset = future.sampleset.relabel_variables(dict(enumerate(dqm.variables))) if hasattr(dqm, 'offset') and dqm.offset: # dimod 0.10+ # some versions of HSS don't account for the offset and it's hard # to tell which sampleset.record.energy = dqm.energies(sampleset) yield sampleset
[docs] def min_time_limit(self, dqm): """Return the minimum `time_limit` accepted for the given problem. The minimum time for a hybrid DQM solver is specified as a piecewise-linear curve defined by a set of floating-point pairs, the `minimum_time_limit` field under :attr:`~dwave.system.samplers.LeapHybridDQMSampler.properties`. The first element in each pair is a combination of the numbers of interactions, variables, and cases that reflects the "density" of connectivity between the problem's variables; the second is the minimum required time. The minimum time for any particular problem size is a linear interpolation calculated on two pairs that represent the relevant range for the given problem. Examples: For a solver where `LeapHybridDQMSampler().properties["minimum_time_limit"]` returns `[[1, 0.1], [100, 10.0], [1000, 20.0]]`, the minimum time for a problem of "density" 50 is 5 seconds (the linear interpolation of the first two pairs that represent problems with "density" between 1 to 100). """ ec = (dqm.num_variable_interactions() * dqm.num_cases() / max(dqm.num_variables(), 1)) limits = np.array(self.properties['minimum_time_limit']) t = np.interp(ec, limits[:, 0], limits[:, 1]) return max([5, t])
[docs]class LeapHybridCQMSampler: """A class for using Leap's cloud-based hybrid CQM solvers. Leap’s quantum-classical hybrid CQM solvers are intended to solve application problems formulated as :ref:`constrained quadratic models (CQM) <cqm_sdk>`. You can configure your :term:`solver` selection and usage by setting parameters, hierarchically, in a configuration file, as environment variables, or explicitly as input arguments, as described in `D-Wave Cloud Client <https://docs.ocean.dwavesys.com/en/stable/docs_cloud/sdk_index.html>`_. :ref:`dwave-cloud-client <sdk_index_cloud>`'s :meth:`~dwave.cloud.client.Client.get_solvers` method filters solvers you have access to by `solver properties <https://docs.dwavesys.com/docs/latest/c_solver_properties.html>`_ ``category=hybrid`` and ``supported_problem_type=cqm``. By default, online hybrid CQM solvers are returned ordered by latest ``version``. Args: **config: Keyword arguments passed to :meth:`dwave.cloud.client.Client.from_config`. Examples: This example solves a simple problem of finding the rectangle with the greatest area when the perimeter is limited. In this example, the perimeter of the rectangle is set to 8 (meaning the largest area is for the :math:`2X2` square). A CQM is created that will have two integer variables, :math:`i, j`, each limited to half the maximum perimeter length of 8, to represent the lengths of the rectangle's sides: >>> from dimod import ConstrainedQuadraticModel, Integer >>> i = Integer('i', upper_bound=4) >>> j = Integer('j', upper_bound=4) >>> cqm = ConstrainedQuadraticModel() The area of the rectangle is given by the multiplication of side :math:`i` by side :math:`j`. The goal is to maximize the area, :math:`i*j`. Because D-Wave samplers minimize, the objective should have its lowest value when this goal is met. Objective :math:`-i*j` has its minimum value when :math:`i*j`, the area, is greatest: >>> cqm.set_objective(-i*j) Finally, the requirement that the sum of both sides must not exceed the perimeter is represented as constraint :math:`2i + 2j <= 8`: >>> cqm.add_constraint(2*i+2*j <= 8, "Max perimeter") 'Max perimeter' Instantiate a hybrid CQM sampler and submit the problem for solution by a remote solver provided by the Leap quantum cloud service: >>> from dwave.system import LeapHybridCQMSampler >>> sampler = LeapHybridCQMSampler() >>> sampleset = sampler.sample_cqm(cqm) >>> print(sampleset.first) # doctest: +SKIP Sample(sample={'i': 2.0, 'j': 2.0}, energy=-4.0, num_occurrences=1, ... is_feasible=True, is_satisfied=array([ True])) The best (lowest-energy) solution found has :math:`i=j=2` as expected, a solution that is feasible because all the constraints (one in this example) are satisfied. """ def __init__(self, **config): # strongly prefer hybrid solvers; requires kwarg-level override config.setdefault('client', 'hybrid') # default to short-lived session to prevent resets on slow uploads config.setdefault('connection_close', True) if FeatureFlags.hss_solver_config_override: # use legacy behavior (override solver config from env/file) solver = config.setdefault('solver', {}) if isinstance(solver, abc.Mapping): solver.update(self.default_solver) # prefer the latest hybrid CQM solver available, but allow for an easy # override on any config level above the defaults (file/env/kwarg) defaults = config.setdefault('defaults', {}) if not isinstance(defaults, abc.Mapping): raise TypeError("mapping expected for 'defaults'") defaults.update(solver=self.default_solver) self.client = Client.from_config(**config) self.solver = self.client.get_solver() # For explicitly named solvers: if self.properties.get('category') != 'hybrid': raise ValueError("selected solver is not a hybrid solver.") if 'cqm' not in self.solver.supported_problem_types: raise ValueError("selected solver does not support the 'cqm' problem type.") @classproperty def default_solver(cls) -> Dict[str, str]: """Features used to select the latest accessible hybrid CQM solver.""" return dict(supported_problem_types__contains='cqm', order_by='-properties.version') @property def properties(self) -> Dict[str, Any]: """Solver properties as returned by a SAPI query. `Solver properties <https://docs.dwavesys.com/docs/latest/c_solver_properties.html>`_ are dependent on the selected solver and subject to change. """ try: return self._properties except AttributeError: self._properties = properties = self.solver.properties.copy() return properties @property def parameters(self) -> Dict[str, List[str]]: """Solver parameters in the form of a dict, where keys are keyword parameters accepted by a SAPI query and values are lists of properties in :attr:`~dwave.system.samplers.LeapHybridCQMSampler.properties` for each key. `Solver parameters <https://docs.dwavesys.com/docs/latest/c_solver_parameters.html>`_ are dependent on the selected solver and subject to change. """ try: return self._parameters except AttributeError: parameters = {param: ['parameters'] for param in self.properties['parameters']} parameters.update(label=[]) self._parameters = parameters return parameters
[docs] def sample_cqm(self, cqm: dimod.ConstrainedQuadraticModel, time_limit: Optional[float] = None, **kwargs): """Sample from the specified constrained quadratic model. Args: cqm (:obj:`dimod.ConstrainedQuadraticModel`): Constrained quadratic model (CQM). time_limit (int, optional): Maximum run time, in seconds, to allow the solver to work on the problem. Must be at least the minimum required for the problem, which is calculated and set by default. :meth:`~dwave.system.samplers.LeapHybridCQMSampler.min_time_limit` calculates (and describes) the minimum time for your problem. **kwargs: Optional keyword arguments for the solver, specified in :attr:`~dwave.system.samplers.LeapHybridCQMSampler.parameters`. Returns: :class:`~dimod.SampleSet`: Sample set constructed from a (non-blocking) :class:`~concurrent.futures.Future`-like object. Examples: See the example in :class:`LeapHybridCQMSampler`. """ if not isinstance(cqm, dimod.ConstrainedQuadraticModel): raise TypeError("first argument 'cqm' must be a ConstrainedQuadraticModel, " f"recieved {type(cqm).__name__}") if time_limit is None: time_limit = self.min_time_limit(cqm) elif time_limit < self.min_time_limit(cqm): raise ValueError("the minimum time limit for this problem is " f"{self.min_time_limit(cqm)} seconds " f"({time_limit}s provided), " "see .min_time_limit method") if len(cqm.constraints) > self.properties['maximum_number_of_constraints']: raise ValueError( "constrained quadratic model must have " f"{self.properties['maximum_number_of_constraints']} or fewer " f"constraints, given model has {len(cqm.constraints)}") if len(cqm.variables) > self.properties['maximum_number_of_variables']: raise ValueError( "constrained quadratic model must have " f"{self.properties['maximum_number_of_variables']} or fewer " f"variables, given model has {len(cqm.variables)}") if cqm.num_biases() > self.properties['maximum_number_of_biases']: raise ValueError( "constrained quadratic model must have " f"{self.properties['maximum_number_of_biases']} or fewer " f"biases, given model has {cqm.num_biases()}") if cqm.num_quadratic_variables() > self.properties['maximum_number_of_quadratic_variables']: raise ValueError( "constrained quadratic model must have " f"{self.properties['maximum_number_of_quadratic_variables']} " "or fewer variables with at least one quadratic bias across " "all constraints, given model has " f"{cqm.num_quadratic_variables()}") return self.solver.sample_cqm(cqm, time_limit=time_limit, **kwargs).sampleset
[docs] def min_time_limit(self, cqm: dimod.ConstrainedQuadraticModel) -> float: """Return the minimum `time_limit` accepted for the given problem.""" # todo: remove the hard-coded defaults num_variables_multiplier = self.properties.get('num_variables_multiplier', 1.57e-04) num_biases_multiplier = self.properties.get('num_biases_multiplier', 4.65e-06) num_constraints_multiplier = self.properties.get('num_constraints_multiplier', 6.44e-09) minimum_time_limit = self.properties['minimum_time_limit_s'] num_variables = len(cqm.variables) num_constraints = len(cqm.constraints) num_biases = cqm.num_biases() return max( num_variables_multiplier * num_variables + num_biases_multiplier * num_biases + num_constraints_multiplier * num_variables * num_constraints, minimum_time_limit )