def sample(self, bqm, *args, **kwargs): msg = ("PolySampler.sample is deprecated and will be removed in dimod " "0.9.0. In the future, when using PolySamplers, you should use " ".sample_poly") warnings.warn(msg, DeprecationWarning) poly = BinaryPolynomial(bqm.quadratic) poly.update(((v, ), bias) for v, bias in bqm.linear.items()) return self.sample_poly(poly, *args, **kwargs)
def sample_hubo(self, H, **kwargs): """Sample from a higher-order unconstrained binary optimization problem. Convert the given higher-order unconstrained binary optimization problem to a :obj:`.BinaryPolynomial` and then call :meth:`.sample_poly`. Args: H (dict): Coefficients of the HUBO as a dict of the form {(u, v, ...): bias, ...}, where `u`, `v`, are binary-valued variables in the polynomial and `bias` their associated coefficient. **kwargs: See :meth:`.sample_poly` for additional keyword definitions. Returns: :obj:`.SampleSet` See also: :meth:`.sample_poly`, :meth:`.sample_hising` """ return self.sample_poly(BinaryPolynomial.from_hubo(H), **kwargs)
def sample_hising(self, h, J, **kwargs): """Sample from a higher-order Ising model. Convert the given higher-order Ising model to a :obj:`.BinaryPolynomial` and call :meth:`.sample_poly`. Args: h (dict): Variable biases of the Ising problem as a dict of the form `{v: bias, ...}`, where `v` is a variable in the polynomial and `bias` its associated coefficient. J (dict): Interaction biases of the Ising problem as a dict of the form `{(u, v, ...): bias}`, where `u`, `v`, are spin-valued variables in the polynomial and `bias` their associated coefficient. **kwargs: See :meth:`.sample_poly` for additional keyword definitions. Returns: :obj:`.SampleSet` See also: :meth:`.sample_poly`, :meth:`.sample_hubo` """ return self.sample_poly(BinaryPolynomial.from_hising(h, J), **kwargs)
def poly_energy(sample_like, poly): """Calculates energy of a sample from a higher order polynomial. Args: sample (samples_like): A raw sample. `samples_like` is an extension of NumPy's array_like structure. See :func:`.as_samples`. poly (dict): Polynomial as a dict of form {term: bias, ...}, where `term` is a tuple of variables and `bias` the associated bias. Returns: float: The energy of the sample. """ return BinaryPolynomial(poly, 'SPIN').energy(sample_like)
def poly_energies(samples_like, poly): """Calculates energy of samples from a higher order polynomial. Args: sample (samples_like): A collection of raw samples. `samples_like` is an extension of NumPy's array_like structure. See :func:`.as_samples`. poly (dict): Polynomial as a dict of form {term: bias, ...}, where `term` is a tuple of variables and `bias` the associated bias. Variable labeling/indexing of terms in poly dict must match that of the sample(s). Returns: list/:obj:`numpy.ndarray`: The energy of the sample(s). """ return BinaryPolynomial(poly, 'SPIN').energies(samples_like)
def fix_variables(poly, fixed_variables): if () in poly.keys(): offset = poly[()] else: offset = 0.0 poly_copy = defaultdict(float) for k, v in poly.items(): k = set(k) for var, value in fixed_variables.items(): if var in k: k -= {var} v *= value k = frozenset(k) if len(k) > 0: poly_copy[k] += v else: offset += v poly_copy[()] = offset return BinaryPolynomial(poly_copy, poly.vartype)
def poly_energy(sample_like, poly): """Calculates energy of a sample from a higher order polynomial. Args: sample (samples_like): A raw sample. `samples_like` is an extension of NumPy's array_like structure. See :func:`.as_samples`. poly (dict): Polynomial as a dict of form {term: bias, ...}, where `term` is a tuple of variables and `bias` the associated bias. Returns: float: The energy of the sample. """ msg = ("poly_energy is deprecated and will be removed in dimod 0.9.0." "In the future, use BinaryPolynomial.energy") warnings.warn(msg, DeprecationWarning) # dev note the vartype is not used in the energy calculation and this will # be deprecated in the future return BinaryPolynomial(poly, 'SPIN').energy(sample_like)
def poly_energies(samples_like, poly): """Calculates energy of samples from a higher order polynomial. Args: sample (samples_like): A collection of raw samples. `samples_like` is an extension of NumPy's array_like structure. See :func:`.as_samples`. poly (dict): Polynomial as a dict of form {term: bias, ...}, where `term` is a tuple of variables and `bias` the associated bias. Variable labeling/indexing of terms in poly dict must match that of the sample(s). Returns: list/:obj:`numpy.ndarray`: The energy of the sample(s). """ msg = ("poly_energies is deprecated and will be removed in dimod 0.9.0." "In the future, use BinaryPolynomial.energies") warnings.warn(msg, DeprecationWarning) # dev note the vartype is not used in the energy calculation and this will # be deprecated in the future return BinaryPolynomial(poly, 'SPIN').energies(samples_like)
def sample_ising(self, h, J, offset=0, scalar=None, bias_range=1, quadratic_range=None, ignored_variables=None, ignored_interactions=None, ignore_offset=False, **parameters): """ Scale and sample from the problem provided by h, J, offset if scalar is not given, problem is scaled based on bias and quadratic ranges. Args: h (dict): linear biases J (dict): quadratic or higher order biases offset (float, optional): constant energy offset scalar (number): Value by which to scale the energy range of the binary quadratic model. bias_range (number/pair): Value/range by which to normalize the all the biases, or if `quadratic_range` is provided, just the linear biases. quadratic_range (number/pair): Value/range by which to normalize the quadratic biases. ignored_variables (iterable, optional): Biases associated with these variables are not scaled. ignored_interactions (iterable[tuple], optional): As an iterable of 2-tuples. Biases associated with these interactions are not scaled. ignore_offset (bool, default=False): If True, the offset is not scaled. **parameters: Parameters for the sampling method, specified by the child sampler. Returns: :obj:`dimod.SampleSet` """ if any(len(inter) > 2 for inter in J): # handle HUBO import warnings msg = ("Support for higher order Ising models in ScaleComposite is " "deprecated and will be removed in dimod 0.9.0. Please use " "PolyScaleComposite.sample_hising instead.") warnings.warn(msg, DeprecationWarning) from dimod.reference.composites.higherordercomposites import PolyScaleComposite from dimod.higherorder.polynomial import BinaryPolynomial poly = BinaryPolynomial.from_hising(h, J, offset=offset) ignored_terms = set() if ignored_variables is not None: ignored_terms.update(frozenset(v) for v in ignored_variables) if ignored_interactions is not None: ignored_terms.update(frozenset(inter) for inter in ignored_interactions) if ignore_offset: ignored_terms.add(frozenset()) return PolyScaleComposite(self.child).sample_poly(poly, scalar=scalar, bias_range=bias_range, poly_range=quadratic_range, ignored_terms=ignored_terms, **parameters) bqm = BinaryQuadraticModel.from_ising(h, J, offset=offset) return self.sample(bqm, scalar=scalar, bias_range=bias_range, quadratic_range=quadratic_range, ignored_variables=ignored_variables, ignored_interactions=ignored_interactions, ignore_offset=ignore_offset, **parameters)
def _init_binary_polynomial(poly, vartype): if not (isinstance(poly, BinaryPolynomial) and (vartype in (poly.vartype, None))): poly = BinaryPolynomial(poly, vartype=vartype) return poly
def reduce_binary_polynomial( poly: BinaryPolynomial ) -> Tuple[List[Tuple[FrozenSet[Hashable], Number]], List[Tuple[ FrozenSet[Hashable], Hashable]]]: """ Reduce a Binary polynomial to a list of quadratic terms and constraints by introducing auxillary variables and creating costraints. Args: poly: BinaryPolynomial Returns: ([(term, bias)*], [((orig_var1, orig_var2), aux_var)*]) Example: >>> poly = dimod.BinaryPolynomial({(0,): -1, (1,): 1, (2,): 1.5, (0, 1): -1, (0, 1, 2): -2}, dimod.BINARY) >>> reduce_binary_polynomial(poly) # doctest: +SKIP ([(frozenset({0}), -1), (frozenset({1}), 1), (frozenset({2}), 1.5), (frozenset({0, 1}), -1), (frozenset({'0*1', 2}), -2)], [(frozenset({0, 1}), '0*1')]) """ variables = poly.variables constraints = [] reduced_terms = [] idx = defaultdict(dict) for item in poly.items(): term, bias = item if len(term) <= 2: reduced_terms.append(item) else: for pair in itertools.combinations(term, 2): idx[frozenset(pair)][term] = bias que = defaultdict(set) for pair, terms in idx.items(): que[len(terms)].add(pair) while idx: new_pairs = set() most = max(que) que_most = que[most] pair = que_most.pop() if not que_most: del que[most] terms = idx.pop(pair) prod_var = _new_product(variables, *pair) constraints.append((pair, prod_var)) prod_var_set = {prod_var} for old_term, bias in terms.items(): common_subterm = (old_term - pair) new_term = common_subterm | prod_var_set for old_pair in map(frozenset, itertools.product(pair, common_subterm)): _decrement_count(idx, que, old_pair) _remove_old(idx, old_term, old_pair) for common_pair in map(frozenset, itertools.combinations(common_subterm, 2)): idx[common_pair][new_term] = bias _remove_old(idx, old_term, common_pair) if len(new_term) > 2: for new_pair in (frozenset((prod_var, v)) for v in common_subterm): idx[new_pair][new_term] = bias new_pairs.add(new_pair) else: reduced_terms.append((new_term, bias)) for new_pair in new_pairs: que[len(idx[new_pair])].add(new_pair) return reduced_terms, constraints
def make_quadratic(poly, strength, vartype=None, bqm=None): """Create a binary quadratic model from a higher order polynomial. Args: poly (dict): Polynomial as a dict of form {term: bias, ...}, where `term` is a tuple of variables and `bias` the associated bias. strength (float): The energy penalty for violating the prodcut constraint. Insufficient strength can result in the binary quadratic model not having the same minimizations as the polynomial. vartype (:class:`.Vartype`/str/set, optional): Variable type for the binary quadratic model. Accepted input values: * :class:`.Vartype.SPIN`, ``'SPIN'``, ``{-1, 1}`` * :class:`.Vartype.BINARY`, ``'BINARY'``, ``{0, 1}`` If `bqm` is provided, `vartype` is not required. bqm (:class:`.BinaryQuadraticModel`, optional): The terms of the reduced polynomial are added to this binary quadratic model. If not provided, a new binary quadratic model is created. Returns: :class:`.BinaryQuadraticModel` Examples: >>> poly = {(0,): -1, (1,): 1, (2,): 1.5, (0, 1): -1, (0, 1, 2): -2} >>> bqm = dimod.make_quadratic(poly, 5.0, dimod.SPIN) """ if vartype is None: if bqm is None: raise ValueError("one of vartype or bqm must be provided") else: vartype = bqm.vartype else: vartype = as_vartype(vartype) # handle other vartype inputs if bqm is None: bqm = BinaryQuadraticModel.empty(vartype) else: bqm = bqm.change_vartype(vartype, inplace=False) bqm.info['reduction'] = {} # we want to be able to mutate the polynomial so copy. We treat this as a # dict but by using BinaryPolynomial we also get automatic handling of # square terms poly = BinaryPolynomial(poly, vartype=bqm.vartype) variables = set().union(*poly) while any(len(term) > 2 for term in poly): # determine which pair of variables appear most often paircounter = Counter() for term in poly: if len(term) <= 2: # we could leave these in but it can lead to cases like # {'ab': -1, 'cdef': 1} where ab keeps being chosen for # elimination. So we just ignore all the pairs continue for u, v in itertools.combinations(term, 2): pair = frozenset((u, v)) # so order invarient paircounter[pair] += 1 pair, __ = paircounter.most_common(1)[0] u, v = pair # make a new product variable p == u*v and replace all (u, v) with p p = _new_product(variables, u, v) terms = [term for term in poly if u in term and v in term] for term in terms: new = tuple(w for w in term if w != u and w != v) + (p, ) poly[new] = poly.pop(term) # add a constraint enforcing the relationship between p == u*v if vartype is Vartype.BINARY: constraint = _binary_product([u, v, p]) bqm.info['reduction'][(u, v)] = {'product': p} elif vartype is Vartype.SPIN: aux = _new_aux(variables, u, v) # need an aux in SPIN-space constraint = _spin_product([u, v, p, aux]) bqm.info['reduction'][(u, v)] = {'product': p, 'auxiliary': aux} else: raise RuntimeError("unknown vartype: {!r}".format(vartype)) # scale constraint and update the polynomial with it constraint.scale(strength) for v, bias in constraint.linear.items(): try: poly[v, ] += bias except KeyError: poly[v, ] = bias for uv, bias in constraint.quadratic.items(): try: poly[uv] += bias except KeyError: poly[uv] = bias try: poly[()] += constraint.offset except KeyError: poly[()] = constraint.offset # convert poly to a bqm (it already is one) for term, bias in poly.items(): if len(term) == 2: u, v = term bqm.add_interaction(u, v, bias) elif len(term) == 1: v, = term bqm.add_variable(v, bias) elif len(term) == 0: bqm.add_offset(bias) else: # still has higher order terms, this shouldn't happen msg = ('Internal error: not all higher-order terms were reduced. ' 'Please file a bug report.') raise RuntimeError(msg) return bqm