Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add custom exceptions #101

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 23 additions & 32 deletions bauhaus/constraint_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from itertools import product, combinations
from .utils import ismethod, classname, flatten
from .utils import unpack_variables as unpack
from .errors import *
import warnings
from collections import defaultdict

Expand Down Expand Up @@ -172,33 +173,21 @@ def build(self, propositions) -> 'NNF':
# retrieve dictionary of inputs
inputs = self.get_implication_inputs(propositions)
if not any(inputs.values()) and not right_vars:
raise ValueError(f"The '{self}' cannot be built"
" as it is decorating a class and"
" the right implication variables are not"
" provided. If it is decorating a method,"
" ensure that the method's return is"
" valid for bauhaus or for the nnf library."
" Check your decorator signature and set"
" the 'right' keyword argument to such a value.")
raise ImplicationConstraintRightConditionBuildError(self)

constraints = []
for input_set in self.partition(inputs):
constraints.append(self._constraint(self,
input_set,
left_vars,
right_vars))
constraints.append(self._constraint(self, input_set, left_vars, right_vars))
return And(constraints)

inputs = self.get_inputs(propositions)
if not inputs:
raise ValueError(inputs)
raise EmptyInputsError(self)

constraints = []
for input_set in self.partition(inputs):
if self._constraint is _ConstraintBuilder.at_most_k:
constraints.append(self._constraint(self,
input_set,
k=self._k))
constraints.append(self._constraint(self, input_set, k=self._k))
else:
constraints.append(self._constraint(self, input_set))
return And(constraints)
Expand All @@ -211,7 +200,7 @@ def get_inputs(self, propositions) -> list:
Encoding.propositions and return its instances.

If the ConstraintBuilder does not have the '_func' attribute,
then it was invoked as a function call.
then it was directly added from a 'add_constraint' function
We gather and validate its arguments (self._vars).

Arguments
Expand All @@ -225,14 +214,14 @@ def get_inputs(self, propositions) -> list:

"""

# Constraint from function
# Constraint from direct constraint addition
if not self._func:
return unpack(self._vars, propositions)
# Constraint from decorator
else:
ret = unpack([self._func], propositions)
if not ret:
raise ValueError(f"The {self} resulted in an empty {ret}")
raise EmptyPropositionalVariablesFromDecorationError(self)
return ret

def get_implication_inputs(self, propositions) -> dict:
Expand Down Expand Up @@ -324,7 +313,7 @@ def at_least_one(self, inputs: list) -> NNF:

"""
if not inputs:
raise ValueError(f"Inputs are empty for {self}")
raise EmptyInputsError(self)

return Or(inputs)

Expand All @@ -342,7 +331,7 @@ def at_most_one(self, inputs: list) -> NNF:

"""
if not inputs:
raise ValueError(f"Inputs are empty for {self}")
raise EmptyInputsError(self)

clauses = []
for var in inputs:
Expand All @@ -366,20 +355,19 @@ def at_most_k(self, inputs: list, k: int) -> NNF:
nnf.NNF

"""
if not inputs:
raise EmptyInputsError(self)
if not 1 <= k <= len(inputs):
raise ValueError(f"The provided k={k} is greater"
" than the number of propositional"
f" variables (i.e. {len(inputs)} variables)"
f" for {self}.")
raise InvalidConstraintSizeK(self, k, len(inputs))
elif k == 1:
return _ConstraintBuilder.at_most_one(inputs)
if k >= len(inputs):
warnings.warn(f"The provided k={k} for building the at most K"
" constraint is greater than or equal to"
f" the number of variables, which is {len(inputs)}."
f" We're setting k = {len(inputs) - 1} as a result.")
warnings.warn(f"The provided k={k} for building the at most K \
constraint is greater than or equal to the number \
of variables, which is {len(inputs)}. We're setting \
k = {len(inputs) - 1} as a result.")
k = len(inputs) - 1

clauses = set() # avoid adding duplicate clauses
inputs = list(map(lambda var: ~var, inputs))
# combinations from choosing k from n inputs for 1 <= k <n
Expand All @@ -406,11 +394,14 @@ def exactly_one(self, inputs: list) -> NNF:
nnf.NNF: And(at_most_one, at_least_one)

"""
if not inputs:
raise EmptyInputsError(self)

at_most_one = _ConstraintBuilder.at_most_one(self, inputs)
at_least_one = _ConstraintBuilder.at_least_one(self, inputs)

if not(at_most_one and at_least_one):
raise ValueError
raise EmptyUnderlyingConstraintError(at_least_one, at_most_one, inputs)
return And({at_most_one, at_least_one})

def implies_all(self, inputs: dict, left: list, right: list) -> NNF:
Expand Down Expand Up @@ -465,6 +456,6 @@ def none_of(self, inputs: list) -> NNF:

"""
if not inputs:
raise ValueError(f"Inputs are empty for {self}")
raise EmptyInputsError(self)

return Or(inputs).negate()
45 changes: 13 additions & 32 deletions bauhaus/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import warnings
from .constraint_builder import _ConstraintBuilder as cbuilder
from .utils import flatten, ismethod, classname
from .errors import *


class Encoding:
Expand Down Expand Up @@ -60,8 +61,8 @@ def __repr__(self) -> str:
f" propositions::{self.propositions.keys()} \n"
f" constraints::{self.constraints}")

def purge_propositions(self):
""" Purges the propositional variables of an Encoding object """
def clear_propositions(self):
""" Clears the propositional variables of an Encoding object """
self.propositions = defaultdict(weakref.WeakValueDictionary)

def clear_constraints(self):
Expand All @@ -80,8 +81,8 @@ def add_constraint(self, constraint: nnf.NNF):
cons : NNF
Constraint to be added.
"""
assert self._custom_constraints is not None, \
"Error: You can't add custom_constraints when objects have overloaded one of the boolean operators."
if self._custom_constraints is not None:
raise CustomConstraintOperatorOverloadError(constraint)
self._custom_constraints.add(constraint)

def disable_custom_constraints(self):
Expand All @@ -105,16 +106,10 @@ def compile(self, CNF=True) -> 'nnf.NNF':

"""
if not self.constraints and not self._custom_constraints:
raise ValueError(f"Constraints in {self} are empty."
" This can happen if no objects from"
" decorated classes are instantiated,"
" if no classes/methods are decorated"
" with @constraint or no function"
" calls of the form constraint.add_method")
raise EmptyConstraintsError(self)

if not self.propositions.values():
raise ValueError(f"Constraints in {self} are empty."
" This can happen if no objects from"
" decorated classes are instantiated.")
raise EmptyPropositionalVariablesError(self)

theory = []
self.clear_debug_constraints()
Expand Down Expand Up @@ -425,18 +420,12 @@ def _is_valid_grouby(decorated_class, parameter):
True if a valid groupby, and raises an Exception if not.

"""
classname = classname(decorated_class)
clsname = classname(decorated_class)
if ismethod(decorated_class):
raise Exception("You can only use groupby on a class and not a method"
f", as you have tried on {decorated_class.__qualname__}."
f" Try using groupby on the {classname}"
" class instead.")
raise GroupbyOnMethodError(decorated_class.__qualname__, clsname)
if not (isinstance(parameter, str) or callable(parameter)):
value_type = type(parameter).__name__
raise ValueError(f"The provided groupby value, {parameter},"
f" is of type {value_type}. To use groupby,"
f" a function or object attribute (string) must be provided"
f" to partition the {classname} objects.")
raise GroupbyWithIncorrectTypeError(parameter, value_type, clsname)
return True


Expand Down Expand Up @@ -486,12 +475,7 @@ def _constraint_by_function(cls,
encoding.constraints.add(constraint)
return
else:
raise ValueError("Some or more of your provided"
f" arguments for the {constraint_type.__name__}"
" constraint were empty or invalid. Your"
" provided arguments were: \n"
f" args: {args}, "
f" left: {left}, right: {right}")
raise ConstraintCreationError(constraint_type.__name__, args, left, right)

@classmethod
def _decorate(cls,
Expand Down Expand Up @@ -817,10 +801,7 @@ def add_implies_all(encoding: Encoding, left, right):

"""
if not (left and right):
raise ValueError(f"You are trying to create an implies all"
" constraint without providing either the"
" left or right sides of the implication.\n"
f" Your left: {left} and right: {right}")
raise ImplicationConstraintCreationError(left, right)
left = tuple(flatten([left]))
right = tuple(flatten([right]))
return constraint._constraint_by_function(encoding,
Expand Down
Loading