Getting Started#


bauhaus can be installed with pip:

pip install bauhaus

Using Bauhaus#

First things first, import the bauhaus library:

from bauhaus import Encoding, proposition, constraint

Create Encoding objects that you intend to transform into a theory. Encoding objects will store your model’s propositional variables and constraints on the fly.

e = Encoding()

Create propositional variables by decorating class definitions with @proposition.

@proposition(e) # Each instance of A is stored as a proposition
class A(object):

Create constraints by decorating classes, methods, or invoking the constraint methods.

# Each instance of A implies the right side
@constraint.implies_all(e, right=['hello'])
# At most two of the A instances are true
@constraint.at_most_k(e, 2)
class A(object):

    def __init__(self, val):
        self.val = val

    def __repr__(self):
        return f"A.{self.val}"

    # Each instance of A implies the result of the method
    def method(self):
        return self.val

# At most one of the inputs is true.
constraint.add_at_most_one(e, A, A.method, Var('B'))

# Add arbitrary constraints on objects anotated with @proposition
x, y = A(1), A(2)
e.add_constraint(~x | y)

Compile your theory into conjunctive or negation normal form (note: the theory is truncated),

objects = [A(val) for val in range(1,4)]
theory = e.compile()
>> And({And({Or({Var(3), ~Var(A.3)}), Or({Var(1), ~Var(A.1)}),
        And({Or({~Var(A.1), ~Var(A.2), ~Var(A.3)})})})

And view the origin of each constraint, from the propositional object to the final constraint. (Note: the introspection is truncated)

constraint.at_most_k:  function = A  k = 2:

(~Var(A.3), ~Var(A.1)) =>
Or({~Var(A.1), ~Var(A.2), ~Var(A.3)})

(~Var(A.3), ~Var(A.2)) =>
Or({~Var(A.1), ~Var(A.2), ~Var(A.3)})

(~Var(A.1), ~Var(A.2)) =>
Or({~Var(A.1), ~Var(A.2), ~Var(A.3)})

Final at_most_k: And({Or({~Var(A.1), ~Var(A.2), ~Var(A.3)})})