示例#1
0
def _bqm_from_2sat(constraint):
    """create a bqm for a constraint with two variables.

    bqm will have exactly classical gap 2.
    """
    configurations = constraint.configurations
    variables = constraint.variables
    vartype = constraint.vartype
    u, v = constraint.variables

    # if all configurations are present, then nothing is infeasible and the bqm is just all
    # 0.0s
    if len(configurations) == 4:
        return dimod.BinaryQuadraticModel.empty(constraint.vartype)

    # check if the constraint is irreducible, and if so, build the bqm for its two
    # components
    components = irreducible_components(constraint)
    if len(components) > 1:
        const0 = Constraint.from_configurations(
            ((config[0], ) for config in configurations), (u, ), vartype)
        const1 = Constraint.from_configurations(
            ((config[1], ) for config in configurations), (v, ), vartype)
        bqm = _bqm_from_1sat(const0)
        bqm.update(_bqm_from_1sat(const1))
        return bqm

    assert len(
        configurations) > 1, "single configurations should be irreducible"

    # if it is not irreducible, and there are infeasible configurations, then it is time to
    # start building a bqm
    bqm = dimod.BinaryQuadraticModel.empty(vartype)

    # if the constraint is not irreducible and has two configurations, then it is either eq or ne
    if all(operator.eq(*config) for config in configurations):
        bqm.add_interaction(u, v, -1, vartype=dimod.SPIN)  # equality
    elif all(operator.ne(*config) for config in configurations):
        bqm.add_interaction(u, v, +1, vartype=dimod.SPIN)  # inequality
    elif (1, 1) not in configurations:
        bqm.add_interaction(u, v, 2, vartype=dimod.BINARY)  # penalize (1, 1)
    elif (-1, +1) not in configurations and (0, 1) not in configurations:
        bqm.add_interaction(u, v, -2, vartype=dimod.BINARY)
        bqm.add_variable(v, 2, vartype=dimod.BINARY)
    elif (+1, -1) not in configurations and (1, 0) not in configurations:
        bqm.add_interaction(u, v, -2, vartype=dimod.BINARY)
        bqm.add_variable(u, 2, vartype=dimod.BINARY)
    else:
        # (0, 0) not in configurations
        bqm.add_interaction(u, v, 2, vartype=dimod.BINARY)
        bqm.add_variable(u, -2, vartype=dimod.BINARY)
        bqm.add_variable(v, -2, vartype=dimod.BINARY)

    return bqm
示例#2
0
def halfadder_gate(variables, vartype=dimod.BINARY, name='HALF_ADDER'):
    """HALF_ADDER adder constraint."""

    variables = tuple(variables)

    if vartype is dimod.BINARY:
        configs = frozenset([(0, 0, 0, 0), (0, 1, 1, 0), (1, 0, 1, 0),
                             (1, 1, 0, 1)])

    else:
        # SPIN, vartype is checked by the decorator
        configs = frozenset([(-1, -1, -1, -1), (-1, +1, +1, -1),
                             (+1, -1, +1, -1), (+1, +1, -1, +1)])

    def func(augend, addend, sum_, carry):
        total = (augend > 0) + (addend > 0)
        if total == 0:
            return (sum_ <= 0) and (carry <= 0)
        elif total == 1:
            return (sum_ > 0) and (carry <= 0)
        elif total == 2:
            return (sum_ <= 0) and (carry > 0)
        else:
            raise ValueError("func recieved unexpected values")

    return Constraint(func, configs, variables, vartype=vartype, name=name)
示例#3
0
def and_gate(variables, vartype=dimod.BINARY, name='AND'):
    """AND gate."""

    variables = tuple(variables)

    if vartype is dimod.BINARY:
        configurations = frozenset([(0, 0, 0), (0, 1, 0), (1, 0, 0),
                                    (1, 1, 1)])

        def func(in1, in2, out):
            return (in1 and in2) == out

    else:
        # SPIN, vartype is checked by the decorator
        configurations = frozenset([(-1, -1, -1), (-1, +1, -1), (+1, -1, -1),
                                    (+1, +1, +1)])

        def func(in1, in2, out):
            return ((in1 > 0) and (in2 > 0)) == (out > 0)

    return Constraint(func,
                      configurations,
                      variables,
                      vartype=vartype,
                      name=name)
示例#4
0
def fulladder_gate(variables, vartype=dimod.BINARY, name='FULL_ADDER'):
    """Full adder.

    Args:
        variables (list): Variable labels for the and gate as `[in1, in2, in3, sum, carry]`,
            where `in1, in2, in3` are inputs to be added and `sum` and 'carry' the resultant
            outputs.
        vartype (Vartype, optional, default='BINARY'): Variable type. Accepted
            input values:

            * Vartype.SPIN, 'SPIN', {-1, 1}
            * Vartype.BINARY, 'BINARY', {0, 1}
        name (str, optional, default='FULL_ADDER'): Name for the constraint.

    Returns:
        Constraint(:obj:`.Constraint`): Constraint that is satisfied when its variables are
        assigned values that match the valid states of a Boolean full adder.

    Examples:
        >>> import dwavebinarycsp
        >>> import dwavebinarycsp.factories.constraint.gates as gates
        >>> csp = dwavebinarycsp.ConstraintSatisfactionProblem(dwavebinarycsp.BINARY)
        >>> csp.add_constraint(gates.fulladder_gate(['a', 'b', 'c_in', 'total', 'c_out'], name='FA1'))
        >>> csp.check({'a': 1, 'b': 0, 'c_in': 1, 'total': 0, 'c_out': 1})
        True

    """

    variables = tuple(variables)

    if vartype is dimod.BINARY:
        configs = frozenset([(0, 0, 0, 0, 0), (0, 0, 1, 1, 0), (0, 1, 0, 1, 0),
                             (0, 1, 1, 0, 1), (1, 0, 0, 1, 0), (1, 0, 1, 0, 1),
                             (1, 1, 0, 0, 1), (1, 1, 1, 1, 1)])

    else:
        # SPIN, vartype is checked by the decorator
        configs = frozenset([(-1, -1, -1, -1, -1), (-1, -1, +1, +1, -1),
                             (-1, +1, -1, +1, -1), (-1, +1, +1, -1, +1),
                             (+1, -1, -1, +1, -1), (+1, -1, +1, -1, +1),
                             (+1, +1, -1, -1, +1), (+1, +1, +1, +1, +1)])

    def func(in1, in2, in3, sum_, carry):
        total = (in1 > 0) + (in2 > 0) + (in3 > 0)
        if total == 0:
            return (sum_ <= 0) and (carry <= 0)
        elif total == 1:
            return (sum_ > 0) and (carry <= 0)
        elif total == 2:
            return (sum_ <= 0) and (carry > 0)
        elif total == 3:
            return (sum_ > 0) and (carry > 0)
        else:
            raise ValueError("func recieved unexpected values")

    return Constraint(func, configs, variables, vartype=vartype, name=name)
示例#5
0
def sat2in4(pos, neg=tuple(), vartype=dimod.BINARY, name='2-in-4'):
    """Return a 2-in-4 constraint.

    Args:
        pos (iterable):
            An iterable of variable labels.

    """
    pos = tuple(pos)
    neg = tuple(neg)

    variables = pos + neg

    if len(variables) != 4:
        raise ValueError("")

    if neg and (len(neg) < 4):
        # because 2-in-4 sat is symmetric, all negated is the same as none negated

        const = sat2in4(pos=variables,
                        vartype=vartype)  # make one that has no negations
        for v in neg:
            const.flip_variable(v)
            const.name = name  # overwrite the name directly

        return const

    # we can just construct them directly for speed
    if vartype is dimod.BINARY:
        configurations = frozenset([(0, 0, 1, 1), (0, 1, 0, 1), (1, 0, 0, 1),
                                    (0, 1, 1, 0), (1, 0, 1, 0), (1, 1, 0, 0)])
    else:
        # SPIN, vartype is checked by the decorator
        configurations = frozenset([(-1, -1, +1, +1), (-1, +1, -1, +1),
                                    (+1, -1, -1, +1), (-1, +1, +1, -1),
                                    (+1, -1, +1, -1), (+1, +1, -1, -1)])

    def func(a, b, c, d):
        if a == b:
            return (b != c) and (c == d)
        elif a == c:
            # a != b
            return b == d
        else:
            # a != b, a != c => b == c
            return a == d

    return Constraint(func,
                      configurations,
                      variables,
                      vartype=vartype,
                      name=name)
示例#6
0
def and_gate(variables, vartype=dimod.BINARY, name='AND'):
    """AND gate.

    Args:
        variables (list): Variable labels for the and gate as `[in1, in2, out]`,
            where `in1, in2` are inputs and `out` the gate's output.
        vartype (Vartype, optional, default='BINARY'): Variable type. Accepted
            input values:

            * Vartype.SPIN, 'SPIN', {-1, 1}
            * Vartype.BINARY, 'BINARY', {0, 1}
        name (str, optional, default='AND'): Name for the constraint.

    Returns:
        Constraint(:obj:`.Constraint`): Constraint that is satisfied when its variables are
        assigned values that match the valid states of an AND gate.

    Examples:
        >>> import dwavebinarycsp
        >>> import dwavebinarycsp.factories.constraint.gates as gates
        >>> csp = dwavebinarycsp.ConstraintSatisfactionProblem(dwavebinarycsp.BINARY)
        >>> csp.add_constraint(gates.and_gate(['a', 'b', 'c'], name='AND1'))
        >>> csp.check({'a': 1, 'b': 0, 'c': 0})
        True
    """

    variables = tuple(variables)

    if vartype is dimod.BINARY:
        configurations = frozenset([(0, 0, 0), (0, 1, 0), (1, 0, 0),
                                    (1, 1, 1)])

        def func(in1, in2, out):
            return (in1 and in2) == out

    else:
        # SPIN, vartype is checked by the decorator
        configurations = frozenset([(-1, -1, -1), (-1, +1, -1), (+1, -1, -1),
                                    (+1, +1, +1)])

        def func(in1, in2, out):
            return ((in1 > 0) and (in2 > 0)) == (out > 0)

    return Constraint(func,
                      configurations,
                      variables,
                      vartype=vartype,
                      name=name)
示例#7
0
def xor_gate(variables, vartype=dimod.BINARY, name='XOR'):
    """XOR constraint."""

    variables = tuple(variables)
    if vartype is dimod.BINARY:
        configs = frozenset([(0, 0, 0), (0, 1, 1), (1, 0, 1), (1, 1, 0)])

        def func(in1, in2, out):
            return (in1 != in2) == out

    else:
        # SPIN, vartype is checked by the decorator
        configs = frozenset([(-1, -1, -1), (-1, +1, +1), (+1, -1, +1),
                             (+1, +1, -1)])

        def func(in1, in2, out):
            return ((in1 > 0) != (in2 > 0)) == (out > 0)

    return Constraint(func, configs, variables, vartype=vartype, name=name)
示例#8
0
def sat2in4(pos, neg=tuple(), vartype=dimod.BINARY, name='2-in-4'):
    """Two-in-four (2-in-4) satisfiability.

    Args:
        pos (iterable):
            Variable labels, as an iterable, for non-negated variables of the constraint.
            Exactly four variables are specified by `pos` and `neg` together.
        neg (tuple):
            Variable labels, as an iterable, for negated variables of the constraint.
            Exactly four variables are specified by `pos` and `neg` together.
        vartype (Vartype, optional, default='BINARY'): Variable type. Accepted
            input values:

            * Vartype.SPIN, 'SPIN', {-1, 1}
            * Vartype.BINARY, 'BINARY', {0, 1}
        name (str, optional, default='2-in-4'): Name for the constraint.

    Returns:
        Constraint(:obj:`.Constraint`): Constraint that is satisfied when its variables are
        assigned values that satisfy a two-in-four satisfiability problem.

    Examples:
        >>> import dwavebinarycsp
        >>> import dwavebinarycsp.factories.constraint.sat as sat
        >>> csp = dwavebinarycsp.ConstraintSatisfactionProblem(dwavebinarycsp.BINARY)
        >>> csp.add_constraint(sat.sat2in4(['w', 'x', 'y', 'z'], vartype='BINARY', name='sat1'))
        >>> csp.check({'w': 1, 'x': 1, 'y': 0, 'z': 0})
        True

    """
    pos = tuple(pos)
    neg = tuple(neg)

    variables = pos + neg

    if len(variables) != 4:
        raise ValueError("")

    if neg and (len(neg) < 4):
        # because 2-in-4 sat is symmetric, all negated is the same as none negated

        const = sat2in4(pos=variables, vartype=vartype)  # make one that has no negations
        for v in neg:
            const.flip_variable(v)
            const.name = name  # overwrite the name directly

        return const

    # we can just construct them directly for speed
    if vartype is dimod.BINARY:
        configurations = frozenset([(0, 0, 1, 1),
                                    (0, 1, 0, 1),
                                    (1, 0, 0, 1),
                                    (0, 1, 1, 0),
                                    (1, 0, 1, 0),
                                    (1, 1, 0, 0)])
    else:
        # SPIN, vartype is checked by the decorator
        configurations = frozenset([(-1, -1, +1, +1),
                                    (-1, +1, -1, +1),
                                    (+1, -1, -1, +1),
                                    (-1, +1, +1, -1),
                                    (+1, -1, +1, -1),
                                    (+1, +1, -1, -1)])

    def func(a, b, c, d):
        if a == b:
            return (b != c) and (c == d)
        elif a == c:
            # a != b
            return b == d
        else:
            # a != b, a != c => b == c
            return a == d

    return Constraint(func, configurations, variables, vartype=vartype, name=name)
示例#9
0
    def add_constraint(self, constraint, variables=tuple()):
        """Add a constraint.

        Args:
            constraint (function/iterable/:obj:`.Constraint`):
                Constraint definition in one of the supported formats:

                1. Function, with input arguments matching the order and
                   :attr:`~.ConstraintSatisfactionProblem.vartype` type of the `variables`
                   argument, that evaluates True when the constraint is satisfied.
                2. List explicitly specifying each allowed configuration as a tuple.
                3. :obj:`.Constraint` object built either explicitly or by :mod:`dwavebinarycsp.factories`.

            variables(iterable):
                Variables associated with the constraint. Not required when `constraint` is
                a :obj:`.Constraint` object.

        Examples:
            This example defines a function that evaluates True when the constraint is satisfied.
            The function's input arguments match the order and type of the `variables` argument.

            >>> csp = dwavebinarycsp.ConstraintSatisfactionProblem(dwavebinarycsp.BINARY)
            >>> def all_equal(a, b, c):  # works for both dwavebinarycsp.BINARY and dwavebinarycsp.SPIN
            ...     return (a == b) and (b == c)
            >>> csp.add_constraint(all_equal, ['a', 'b', 'c'])
            >>> csp.check({'a': 0, 'b': 0, 'c': 0})
            True
            >>> csp.check({'a': 0, 'b': 0, 'c': 1})
            False

            This example explicitly lists allowed configurations.

            >>> csp = dwavebinarycsp.ConstraintSatisfactionProblem(dwavebinarycsp.SPIN)
            >>> eq_configurations = {(-1, -1), (1, 1)}
            >>> csp.add_constraint(eq_configurations, ['v0', 'v1'])
            >>> csp.check({'v0': -1, 'v1': +1})
            False
            >>> csp.check({'v0': -1, 'v1': -1})
            True

            This example uses a :obj:`.Constraint` object built by :mod:`dwavebinarycsp.factories`.

            >>> import dwavebinarycsp.factories.constraint.gates as gates
            >>> csp = dwavebinarycsp.ConstraintSatisfactionProblem(dwavebinarycsp.BINARY)
            >>> csp.add_constraint(gates.and_gate(['a', 'b', 'c']))  # add an AND gate
            >>> csp.add_constraint(gates.xor_gate(['a', 'c', 'd']))  # add an XOR gate
            >>> csp.check({'a': 1, 'b': 0, 'c': 0, 'd': 1})
            True

        """
        if isinstance(constraint, Constraint):
            if variables and (tuple(variables) != constraint.variables):
                raise ValueError("mismatched variables and Constraint")
        elif isinstance(constraint, Callable):
            constraint = Constraint.from_func(constraint, variables,
                                              self.vartype)
        elif isinstance(constraint, Iterable):
            constraint = Constraint.from_configurations(
                constraint, variables, self.vartype)
        else:
            raise TypeError("Unknown constraint type given")

        self.constraints.append(constraint)
        for v in constraint.variables:
            self.variables[v].append(constraint)