示例#1
0
 def _append(self,
             vartype: VartypeLike,
             v: Optional[Variable] = None,
             permissive: bool = False) -> Variable:
     if permissive and v is not None and self.count(v):
         if as_vartype(vartype, extended=True) != self.vartype(v):
             raise ValueError("inconsistent vartype")
         return v
     else:
         v = super()._append(v)
         self.vartypes.append(as_vartype(vartype, extended=True))
         return v
示例#2
0
        def _enforce_single_arg(name, args, kwargs):
            try:
                vartype = kwargs[name]
            except KeyError:
                raise TypeError('vartype argument missing')

            kwargs[name] = as_vartype(vartype)
示例#3
0
    def _init_bqm(self, bqm, vartype=None):
        self.linear.update(bqm.linear)
        self.quadratic.update(bqm.quadratic)
        self.offset = bqm.offset
        self._vartype = bqm.vartype

        if vartype is not None:
            self.change_vartype(as_vartype(vartype), inplace=True)
示例#4
0
 def add_variable(self, v: Variable, vartype: VartypeLike):
     """Add a variable to the model."""
     if self.variables.count(v):
         if as_vartype(vartype, extended=True) != self.variables.vartype(v):
             raise ValueError(
                 "given variable has already been added with a different vartype"
             )
     else:
         return self.variables._append(vartype, v)
def test_sets_correct_vartype(vartype):
    """BQM loaded from coo should have correct vartype."""
    coo = make_coo("""0 1 2.5
        1 1 -3
        2 0 4
        4 3 -1.2
        0 3 0.1123456
        """)
    bqm = bqm_from_coo(coo, vartype=vartype)
    assert bqm.vartype == vartypes.as_vartype(vartype)
示例#6
0
    def change_vartype(self, vartype, inplace=True):
        """Return a binary quadratic model with the specified vartype.

        Args:
            vartype (:class:`.Vartype`/str/set, optional):
                Variable type for the changed model. Accepted input values:

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

            inplace (bool, optional, default=True):
                If True, the binary quadratic model is updated in-place;
                otherwise, a new binary quadratic model is returned.

        Returns:
            :obj:`.AdjDictBQM`: A binary quadratic model with the specified
            vartype.

        """
        if not inplace:
            return self.copy().change_vartype(vartype, inplace=True)

        vartype = as_vartype(vartype)

        # in place and we are already correct, so nothing to do
        if self.vartype == vartype:
            return self

        if vartype is Vartype.BINARY:
            lin_mp, lin_offset_mp = 2.0, -1.0
            quad_mp, lin_quad_mp, quad_offset_mp = 4.0, -2.0, 1.0
        elif vartype is Vartype.SPIN:
            lin_mp, lin_offset_mp = 0.5, .5
            quad_mp, lin_quad_mp, quad_offset_mp = 0.25, 0.25, 0.25
        else:
            raise ValueError("unkown vartype")

        for v, bias in self.linear.items():
            self.linear[v] = lin_mp * bias
            self.offset += lin_offset_mp * bias

        for (u, v), bias in self.quadratic.items():
            self.adj[u][v] = quad_mp * bias

            self.linear[u] += lin_quad_mp * bias
            self.linear[v] += lin_quad_mp * bias

            self.offset += quad_offset_mp * bias

        self._vartype = vartype

        return self
示例#7
0
def _init_quadratic_model(qm, vartype, qm_factory):
    if vartype is None:
        if qm is None:
            raise ValueError("one of vartype or qm must be provided")
        else:
            vartype = qm.vartype
    else:
        vartype = as_vartype(vartype)  # handle other vartype inputs
        if qm is None:
            qm = qm_factory(vartype)
        else:
            qm = qm.change_vartype(vartype, inplace=False)

    # for backwards compatibility, add an info field
    if not hasattr(qm, 'info'):
        qm.info = {}
    qm.info['reduction'] = {}
    return qm, vartype
示例#8
0
    def add_variables_from(self, vartype: VartypeLike, variables: Iterable[Variable]):
        """Add multiple variables of the same type to the quadratic model.

        Args:
            vartype: Variable type. One of:

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

            variables: Iterable of variable labels.

        Examples:
            >>> from dimod import QuadraticModel, Binary
            >>> qm = QuadraticModel()
            >>> qm.add_variables_from('BINARY', ['x', 'y'])

        """
        vartype = as_vartype(vartype, extended=True)
        for v in variables:
            self.add_variable(vartype, v)
示例#9
0
    def change_vartype(self, vartype: VartypeLike) -> 'pyBQM':
        vartype = as_vartype(vartype)

        # in place and we are already correct, so nothing to do
        if self._vartype == vartype:
            return self

        if vartype == Vartype.BINARY:
            lin_mp = 2.
            lin_offset_mp = -1.
            quad_mp = 4.
            lin_quad_mp = -2.
            quad_offset_mp = .5
        elif vartype == Vartype.SPIN:
            lin_mp = .5
            lin_offset_mp = .5
            quad_mp = .25
            lin_quad_mp = .25
            quad_offset_mp = .125
        else:
            raise RuntimeError("unexpected vartype")

        adj = self._adj

        for u, Nu in adj.items():
            lbias = Nu[u]

            self.offset += lin_offset_mp * lbias
            Nu[u] = lin_mp * lbias

            for v, qbias in Nu.items():
                if v == u:
                    continue
                Nu[v] = quad_mp * qbias
                Nu[u] += lin_quad_mp * qbias  # linear
                self.offset += quad_offset_mp * qbias

        self._vartype = vartype

        return self
示例#10
0
def as_bqm(*args, cls=None, copy=False):
    """Convert the input to a binary quadratic model.

    Converts the following input formats to a binary quadratic model (BQM):

        as_bqm(vartype)
            Creates an empty binary quadratic model.

        as_bqm(bqm)
            Creates a BQM from another BQM. See `copy` and `cls` kwargs below.

        as_bqm(bqm, vartype)
            Creates a BQM from another BQM, changing to the appropriate
            `vartype` if necessary. See `copy` and `cls` kwargs below.

        as_bqm(n, vartype)
            Creates a BQM with `n` variables, indexed linearly from zero,
            setting all biases to zero.

        as_bqm(quadratic, vartype)
            Creates a BQM from quadratic biases given as a square array_like_
            or a dictionary of the form `{(u, v): b, ...}`. Note that when
            formed with SPIN-variables, biases on the diagonal are added to the
            offset.

        as_bqm(linear, quadratic, vartype)
            Creates a BQM from linear and quadratic biases, where `linear` is a
            one-dimensional array_like_ or a dictionary of the form
            `{v: b, ...}`, and `quadratic` is a square array_like_ or a
            dictionary of the form `{(u, v): b, ...}`. Note that when formed
            with SPIN-variables, biases on the diagonal are added to the offset.

        as_bqm(linear, quadratic, offset, vartype)
            Creates a BQM from linear and quadratic biases, where `linear` is a
            one-dimensional array_like_ or a dictionary of the form
            `{v: b, ...}`, and `quadratic` is a square array_like_ or a
            dictionary of the form `{(u, v): b, ...}`, and `offset` is a
            numerical offset. Note that when formed with SPIN-variables, biases
            on the diagonal are added to the offset.

    Args:
        *args:
            See above.

        cls (type/list, optional):
            Class of the returned BQM. If given as a list,
            the returned BQM is of one of the types in the list. Default is
            :class:`.AdjVectorBQM`.

        copy (bool, optional, default=False):
            If False, a new BQM is only constructed when
            necessary.

    Returns:
        A binary quadratic model.

    .. _array_like: https://numpy.org/doc/stable/user/basics.creation.html

    """

    if cls is None:
        if isinstance(args[0], BQM):
            cls = type(args[0])
        else:
            cls = AdjVectorBQM
    elif isinstance(cls, (Sequence, Set)):  # want Collection, but not in 3.5
        classes = tuple(cls)
        if not classes:
            raise ValueError("cls kwarg should be a type or a list of types")
        if type(args[0]) in classes:
            cls = type(args[0])
        else:
            # otherwise just pick the first one
            cls = classes[0]

    if isinstance(args[0], cls) and not copy:
        # this is the only case (currently) in which copy matters
        if len(args) == 1:
            return args[0]
        elif len(args) == 2:
            bqm, vartype = args
            if bqm.vartype is as_vartype(vartype):
                return bqm
            # otherwise we're doing a copy
        # otherwise we don't have a well-formed bqm input so pass off the check
        # to cls(*args)

    return cls(*args)
示例#11
0
 def _init_number(self, n, vartype):
     self.linear.update((v, 0.0) for v in range(n))
     self.offset = 0.0
     self._vartype = as_vartype(vartype)
示例#12
0
    def _init_components(self, linear, quadratic, offset, vartype):
        self._vartype = vartype = as_vartype(vartype)

        if isinstance(linear, (abc.Mapping, abc.Iterator)):
            self.linear.update(linear)
        else:
            # assume a sequence
            self.linear.update(enumerate(linear))

        adj = self._adj

        if isinstance(quadratic, abc.Mapping):
            for (u, v), bias in quadratic.items():
                self.add_variable(u)
                self.add_variable(v)

                if u == v and vartype is Vartype.SPIN:
                    offset = offset + bias  # not += on off-chance it's mutable
                elif u in adj[v]:
                    adj[u][v] = adj[v][u] = adj[u][v] + bias
                else:
                    adj[u][v] = adj[v][u] = bias
        elif isinstance(quadratic, abc.Iterator):
            for u, v, bias in quadratic:
                self.add_variable(u)
                self.add_variable(v)

                if u == v and vartype is Vartype.SPIN:
                    offset = offset + bias  # not += on off-chance it's mutable
                elif u in adj[v]:
                    adj[u][v] = adj[v][u] = adj[u][v] + bias
                else:
                    adj[u][v] = adj[v][u] = bias
        else:
            # unlike the other BQM types we let numpy handle the typing
            if isinstance(quadratic, np.ndarray):
                dtype = quadratic.dtype
            else:
                quadratic = np.asarray(quadratic, dtype=object)

            D = np.atleast_2d(quadratic)

            num_variables = D.shape[0]

            if D.ndim != 2 or num_variables != D.shape[1]:
                raise ValueError("expected dense to be a 2 dim square array")

            # make sure all the variables are present
            for v in range(num_variables):
                self.add_variable(v)

            it = np.nditer(D,
                           flags=['multi_index', 'refs_ok'],
                           op_flags=['readonly'])
            while not it.finished:
                u, v = it.multi_index
                bias = it.value[()]

                if bias:
                    if u == v and vartype is Vartype.SPIN:
                        # not += on off-chance it's mutable
                        offset = offset + bias
                    elif u in adj[v]:
                        adj[u][v] = adj[v][u] = adj[u][v] + bias
                    else:
                        adj[u][v] = adj[v][u] = bias

                it.iternext()

        self.offset = offset
示例#13
0
    def __init__(self, vartype: VartypeLike):
        self._adj: Dict[Variable, Dict[Variable, Any]] = dict()
        self._vartype = as_vartype(vartype)

        self.offset = 0
示例#14
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
示例#15
0
 def change_vartype(self, vartype: VartypeLike):
     self._vartype = as_vartype(vartype)
示例#16
0
 def add_variables_from(self, vartype: VartypeLike,
                        variables: Iterable[Variable]):
     vartype = as_vartype(vartype, extended=True)
     for v in variables:
         self.add_variable(vartype, v)