Beispiel #1
0
def make_quadratic(poly, strength, vartype=None, bqm=None):
    """Create a binary quadratic model from a higher order polynomial.

    Args:
        poly (dict):
            A polynomial. Should be a dict of the form {term: bias, ...} where term is a tuple of
            variables and bias is their associated bias.

        strength (float):
            The strength of the reduction constraint. Insufficient strength can result in the
            binary quadratic model not having the same minimizations as the polynomial.

        vartype (:class:`.Vartype`, optional):
            The vartype of the polynomial. If a bqm is provided, then vartype is not required.

        bqm (:class:`.BinaryQuadraticModel`, optional):
            The terms of the reduced polynomial are added to this bqm. If not provided a new
            empty binary quadratic model is created.

    Returns:
        :class:`.BinaryQuadraticModel`

    Examples:

        >>> import dimod
        ...
        >>> poly = {(0,): -1, (1,): 1, (2,): 1.5, (0, 1): -1, (0, 1, 2): -2}
        >>> bqm = make_quadratic(poly, 5.0, dimod.SPIN)

    """

    if bqm is None:
        if vartype is None:
            raise ValueError("one of vartype and create_using must be provided")
        bqm = BinaryQuadraticModel.empty(vartype)
    else:
        if not isinstance(bqm, BinaryQuadraticModel):
            raise TypeError('create_using must be a BinaryQuadraticModel')
        if vartype is not None and vartype is not bqm.vartype:
            raise ValueError("one of vartype and create_using must be provided")
    bqm.info['reduction'] = {}

    new_poly = {}
    for term, bias in iteritems(poly):
        if len(term) == 0:
            bqm.add_offset(bias)
        elif len(term) == 1:
            v, = term
            bqm.add_variable(v, bias)
        else:
            new_poly[term] = bias

    return _reduce_degree(bqm, new_poly, vartype, strength)
Beispiel #2
0
def combinations(n, k, strength=1, vartype=BINARY):
    r"""Generate a BQM that is minimized when k of n variables are selected.

    More fully, generates a binary quadratic model (BQM) that is minimized for
    each of the k-combinations of its variables.

    The energy for the BQM is given by
    :math:`(\sum_{i} x_i - k)^2`.

    Args:
        n (int/list/set):
            If n is an integer, variables are labelled [0, n-1]. If n is a list
            or set, variables are labelled accordingly.

        k (int):
            The generated BQM has 0 energy when any k of its variables are 1.

        strength (number, optional, default=1):
            The energy of the first excited state of the BQM.

        vartype (:class:`.Vartype`/str/set):
            Variable type for the BQM. Accepted input values:

            * :class:`.Vartype.SPIN`, ``'SPIN'``, ``{-1, 1}``
            * :class:`.Vartype.BINARY`, ``'BINARY'``, ``{0, 1}``

    Returns:
        :obj:`.BinaryQuadraticModel`

    Examples:

        >>> bqm = dimod.generators.combinations(['a', 'b', 'c'], 2)
        >>> bqm.energy({'a': 1, 'b': 0, 'c': 1})
        0.0
        >>> bqm.energy({'a': 1, 'b': 1, 'c': 1})
        1.0

        >>> bqm = dimod.generators.combinations(5, 1)
        >>> bqm.energy({0: 0, 1: 0, 2: 1, 3: 0, 4: 0})
        0.0
        >>> bqm.energy({0: 0, 1: 0, 2: 1, 3: 1, 4: 0})
        1.0

        >>> bqm = dimod.generators.combinations(['a', 'b', 'c'], 2, strength=3.0)
        >>> bqm.energy({'a': 1, 'b': 0, 'c': 1})
        0.0
        >>> bqm.energy({'a': 1, 'b': 1, 'c': 1})
        3.0

    """
    if isinstance(n, abc.Sized) and isinstance(n, abc.Iterable):
        # what we actually want is abc.Collection but that doesn't exist in
        # python2
        variables = n
    else:
        try:
            variables = range(n)
        except TypeError:
            raise TypeError('n should be a collection or an integer')

    if k > len(variables) or k < 0:
        raise ValueError("cannot select k={} from {} variables".format(k, len(variables)))

    # (\sum_i x_i - k)^2
    #     = \sum_i x_i \sum_j x_j - 2k\sum_i x_i + k^2
    #     = \sum_{i,j} x_ix_j + (1 - 2k)\sum_i x_i + k^2
    lbias = float(strength*(1 - 2*k))
    qbias = float(2*strength)

    bqm = BinaryQuadraticModel.empty(BINARY)
    bqm.add_variables_from(((v, lbias) for v in variables))
    bqm.add_interactions_from(((u, v, qbias) for u, v in itertools.combinations(variables, 2)))
    bqm.offset += strength*(k**2)

    return bqm.change_vartype(vartype, inplace=True)
Beispiel #3
0
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