def static(self, variables, *, order): variables = flatten(variables) checkType(variables, [Variable]) order = flatten(order) checkType(order, [int]) self.staticPart = (variables, order) return self
def NValues(term, *others, excepting=None, condition=None): terms = flatten(term, others) checkType(terms, [Variable]) if excepting is not None: excepting = flatten(excepting) checkType(excepting, [int]) return _wrapping_by_complete_or_partial_constraint(ConstraintNValues(terms, excepting, Condition.build_condition(condition)))
def Cumulative(*, origins, lengths, ends=None, heights, condition=None): origins = flatten(origins) checkType(origins, [Variable]) lengths = flatten(lengths) checkType(lengths, ([Variable], [int])) heights = flatten(heights) checkType(heights, ([Variable], [int])) if ends is not None: ends = flatten(ends) checkType(ends, ([Variable], type(None))) return _wrapping_by_complete_or_partial_constraint(ConstraintCumulative(origins, lengths, ends, heights, Condition.build_condition(condition)))
def Instantiation(*, variables, values): variables = flatten(variables) values = flatten(values) if not isinstance(values, range) else list(values) checkType(variables, [Variable]) checkType(values, (int, [int])) if len(variables) == 0: return None if len(values) == 1 and len(variables) > 1: values = [values[0]] * len(variables) return ConstraintInstantiation(variables, values)
def Channel(list1, list2=None, *, start_index1=0, start_index2=0): list1 = flatten(list1) checkType(list1, [Variable]) if list2: list2 = flatten(list2) checkType(list2, [Variable]) checkType(start_index1, int) checkType(start_index2, int) assert start_index2 == 0 or list2 is not None, "start_index2 is defined while list2 is not specified" return ECtr(ConstraintChannel(list1, start_index1, list2, start_index2))
def Clause(term, *others, phases=None): literals = flatten(term, others) phases = [False] * len(literals) if phases is None else flatten(phases) assert len(literals) == len(phases) checkType(literals, ([Variable, NotVariable])) checkType(phases, [bool]) for i, literal in enumerate(literals): if isinstance(literal, NotVariable): literals[i] = literal.variable phases[i] = True return ECtr(ConstraintClause(literals, phases))
def AllDifferent(term, *others, excepting=None, matrix=None): terms = flatten(term, others) if matrix is not None: assert excepting is None, "excepting values are currently not supported for AllDifferentMatrix" matrix = [flatten(row) for row in terms] assert all(len(row) == len(matrix[0]) for row in matrix), "The matrix id badly formed" assert all(checkType(l, [Variable]) for l in matrix) return ECtr(ConstraintAllDifferentMatrix(matrix)) excepting = list(excepting) if isinstance(excepting, (tuple, set)) else [excepting] if isinstance(excepting, int) else excepting checkType(terms, ([Variable, Node])) checkType(excepting, ([int], type(None))) return ECtr(ConstraintAllDifferent(terms, excepting))
def _lex(term, others, operator, matrix): if len(others) == 0: assert is_matrix(term, Variable) lists = [flatten(l) for l in term] else: assert is_1d_list(term, Variable) and all(is_1d_list(l, Variable) for l in others) lists = [flatten(term)] + [flatten(l) for l in others] assert is_matrix(lists, Variable) # new check because some null cells (variables) may have been discarded assert all(len(l) == len(lists[0]) for l in lists) checkType(lists, [Variable]) checkType(operator, TypeOrderedOperator) return ECtr(ConstraintLexMatrix(lists, operator)) if matrix else ECtr(ConstraintLex(lists, operator))
def __init__(self, variables, coefficients): variables = list(variables) if isinstance(variables, tuple) else variables coefficients = list(coefficients) if isinstance( coefficients, tuple) else coefficients assert isinstance(variables, list) and isinstance( coefficients, (int, list, range)), variables self.variables = flatten( variables) # for example, in order to remove None occurrences self.coeffs = flatten( [coefficients] * len(variables) if isinstance(coefficients, int) else coefficients) assert len(self.variables) == len( self.coeffs), str(self.variables) + " " + str(self.coeffs)
def Extension(*, scope, table, positive=True): scope = flatten(scope) checkType(scope, [Variable]) assert isinstance(table, list) if any(isinstance(v, ConditionValue) for t in table for v in t): # if smart table table = sorted(list(to_ordinary_table(table, [x.dom for x in scope], keep_any=True))) checkType(table, [str, int, float]) checkType(positive, bool) assert isinstance(table, list) and len(table) > 0, "A table must be a non-empty list of tuples or integers (or symbols)" assert isinstance(table[0], (tuple, int, str)), "Elements of tables are tuples or integers (or symbols)" #print(table) assert isinstance(table[0], (int, str)) or len(scope) == len(table[0]), ( "The length of each tuple must be the same as the arity." + "Maybe a problem with slicing: you must for example write x[i:i+3,0] instead of x[i:i+3][0]") # TODO: this ckecking don't pass on Waterbucket.py, but the xml file is the same that the java version ! # if options.checker: # if not hasattr(Extension, "checked_tables"): # Extension.checked_tables = set() # if id(table) not in checked_tables: # for t in table: # for i, v in enumerate(t): # if v not in variables[i].dom: # raise ValueError( # "Problem: Constraint extension : a value of table is not represented in a domain of a variable : " + str(domainElement)) # checked_tables.add(id(table)) return ECtr(ConstraintExtension(scope, table, positive))
def _expand(compact_form): assert " " not in compact_form, "The specified string must correspond to a single token; bad form : " + compact_form if compact_form[-1] == ")": return compact_form # // this means that we have an expression (predicate) here if "[" not in compact_form: return compact_form pos = compact_form.index("[") prefix, suffix = compact_form[:pos], compact_form[pos:] tokens = [int(v) if v.isdigit() else v for v in re.split("\]\[", suffix[1:-1])] var_array = VarEntities.prefixToEVarArray[prefix] assert var_array, "Pb with " + compact_form assert len(var_array.size) == len(tokens) mins, maxs, sizes = [0] * len(tokens), [0] * len(tokens), [0] * len(tokens) for i, value in enumerate(tokens): if isinstance(value, int): mins[i] = maxs[i] = value elif value == "": mins[i] = 0 maxs[i] = var_array.size[i] - 1 else: spl = value.split("..") mins[i], maxs[i] = int(spl[0]), int(spl[1]) sizes[i] = maxs[i] - mins[i] + 1 var_names = flatten(Variable.build_names_array(prefix, sizes, mins)) return " ".join(s for s in var_names)
def build(type, *args): type = TypeNode.value_of( type ) # for handling the cases where type is of type str or TypeConditionOperator if type is TypeNode.SET: assert len(args) == 1 elements = list(args[0]) sorted_sons = sorted( elements, key=lambda v: str(v)) if len(elements) > 0 and not isinstance( elements[0], int) else sorted(elements) return Node(type, Node._create_sons(*sorted_sons)) # *sorted(args[0]))) args = flatten( Node.build(TypeNode.SET, arg ) if isinstance(arg, (set, range, frozenset)) else arg for arg in args) assert type.is_valid_arity( len(args) ), "Problem: Bad arity for node " + type.name + ". It is " + str( len(args)) + " but it should be between " + str( type.min_arity) + " and " + str(type.max_arity) # Do we activate these simple modifications below? # if len(args) == 2 and isinstance(args[0], Variable) and isinstance(args[1], int): # if (args[1] == 1 and type in (TypeNode.MUL, TypeNode.DIV)) or (args[1] == 0 and type in (TypeNode.ADD, TypeNode.SUB)): # return Node(TypeNode.VAR,args[0]) node = Node(type, Node._create_sons(*args)) if type == TypeNode.EQ and all(son.type.is_predicate_operator() for son in node.sons): node = Node(TypeNode.IFF, node.sons) # Reducing the node for t in {TypeNode.ADD, TypeNode.MUL, TypeNode.OR, TypeNode.AND}: node.flatten_by_associativity(t) node.reduce_integers() return node
def _record_solution(roots, i): variables = [] for token in roots[i][0].text.split(): r = VarEntities.get_item_with_name(token) if isinstance(r, EVar): variables.append(r.variable) elif isinstance(r, Variable): variables.append(r) else: for x in flatten(r.variables, keep_none=True): variables.append(x) if i == 0: # reset the history in that case for x in variables: if x: x.values = [] values = [] for tok in roots[i][1].text.split(): if 'x' in tok: # in order to handle compact forms in solutions vk = tok.split('x') assert len(vk) == 2 for _ in range(int(vk[1])): values.append(vk[0]) else: values.append(tok) # values is a list with all values given as strings (possibly '*') assert len(variables) == len(values) for i, _ in enumerate(values): if variables[i]: if isinstance(variables[i], VariableInteger): values[i] = int(values[i]) if values[i] != "*" else ANY variables[i].value = values[i] # we record the last found solution value variables[i].values.append(values[i]) # we record it in the history return variables, values
def build(type, *args): if type is TypeNode.SET: assert len(args) == 1 elements = list(args[0]) sorted_sons = sorted( elements, key=lambda v: str(v)) if len(elements) > 0 and not isinstance( elements[0], int) else sorted(elements) return Node(type, Node._create_sons(*sorted_sons)) # *sorted(args[0]))) args = flatten( Node.build(TypeNode.SET, arg ) if isinstance(arg, (set, range, frozenset)) else arg for arg in args) assert type.is_valid_arity( len(args) ), "Problem: Bad arity for node " + type.name + ". It is " + str( len(args)) + " but it should be between " + str( type.arityMin) + " and " + str(type.arityMax) node = Node(type, Node._create_sons(*args)) if type == TypeNode.EQ and all(son.type.is_predicate_operator() for son in node.sons): node = Node(TypeNode.IFF, node.sons) # Reducing the node for t in { TypeNode.ADD, TypeNode.MUL, TypeNode.OR, TypeNode.AND, TypeNode.EQ, TypeNode.IFF }: node.flatten_by_associativity(t) node.reduce_integers() if options.debug: print("New node:", node) return node
def Cardinality(term, *others, occurrences, closed=False): terms = flatten(term, others) checkType(terms, [Variable]) assert isinstance(occurrences, dict) values = list(occurrences.keys()) assert all(isinstance(value, (int, Variable)) for value in values) occurs = list(occurrences.values()) checkType(closed, (bool, type(None))) for i, occ in enumerate(occurs): if isinstance(occ, range): occurs[i] = str(min(occ)) + ".." + str(max(occ)) if isinstance(occ, list): flat = flatten(occ) if all([isinstance(e, int) for e in flat]) and flat == list(range(min(flat), max(flat) + 1)): flat = str(min(flat)) + ".." + str(max(flat)) occurs[i] = flat return ECtr(ConstraintCardinality(terms, values, occurs, closed))
def _ordered(term, others, operator, lengths): terms = flatten(term, others) checkType(terms, [Variable]) checkType(operator, TypeOrderedOperator) checkType(lengths, ([int, Variable], type(None))) if lengths is not None: if len(terms) == len(lengths): lengths = lengths[:-1] # we assume that the last value is useless assert len(terms) == len(lengths) + 1 return ECtr(ConstraintOrdered(terms, operator, lengths))
def _opt(self, variables, type): if variables: variables = flatten(variables) checkType(variables, [Variable]) types = TypeVarHeuristic if isinstance( self, VarHeuristic) else TypeValHeuristic assert isinstance(type, str) and all( p in [t.name for t in types] for p in re.split(r'/|\+', type)), "Bad value for " + type return variables, type
def Count(term, *others, value=None, values=None, condition=None): terms = flatten(term, others) assert len(terms) > 0, "A count with an empty scope" checkType(terms, ([Variable], [Node])) if value is None and values is None: value = 1 assert value is None or values is None, str(value) + " " + str(values) values = list(values) if isinstance(values, (tuple, set)) else [value] if isinstance(value, (int, Variable)) else values checkType(values, ([int], [Variable])) return _wrapping_by_complete_or_partial_constraint(ConstraintCount(terms, values, Condition.build_condition(condition)))
def _extremum(term, others, index, start_index, type_rank, condition, maximum): terms = list(term) if isinstance(term, types.GeneratorType) else flatten(term, others) terms = [Sum(t) if isinstance(t, ScalarProduct) else t for t in terms] # to have PartialConstraints checkType(terms, ([Variable], [Node], [PartialConstraint])) auxiliary().replace_partial_constraints(terms) checkType(index, (Variable, type(None))) checkType(start_index, int) checkType(type_rank, TypeRank) assert index is not None or (start_index == 0 and type_rank is TypeRank.ANY) return ConstraintMaximum(terms, index, start_index, type_rank, condition) if maximum else ConstraintMinimum(terms, index, start_index, type_rank, condition)
def AllDifferentList(lists, *others, excepting=None): if isinstance(lists, types.GeneratorType): lists = [l for l in lists] elif len(others) > 0: lists = list((lists,) + others) lists = [flatten(l) for l in lists] assert all(checkType(l, [Variable]) for l in lists) excepting = list(excepting) if isinstance(excepting, (tuple, set)) else excepting checkType(excepting, ([int], type(None))) assert all(len(l) == len(lists[0]) for l in lists) and (excepting is None or len(excepting) == len(list[0])) return ECtr(ConstraintAllDifferentList(lists, excepting))
def Sum(term, *others, condition=None): def _get_terms_coeffs(terms): if len(terms) == 1 and isinstance(terms[0], ScalarProduct): return flatten(terms[0].variables), flatten(terms[0].coeffs) if all(isinstance(x, (Variable, PartialConstraint)) for x in terms): return terms, None t1, t2 = [], [] for tree in terms: if isinstance(tree, Variable): t1.append(tree) t2.append(1) else: assert isinstance(tree, (Node, NegVariable)) pair = (tree.variable, -1) if isinstance(tree, NegVariable) else tree.tree_val_if_binary_type(TypeNode.MUL) if pair is None: break t1.append(pair[0]) t2.append(pair[1]) if len(t1) == len(terms): for tree in terms: if isinstance(tree, Node): tree.mark_as_used() return t1, t2 return terms, None def _manage_coeffs(terms, coeffs): if coeffs: OpOverrider.disable() if len(coeffs) == 1 and isinstance(coeffs[0], (tuple, set, range)): coeffs = list(coeffs[0]) elif isinstance(coeffs, (tuple, set, range)): coeffs = list(coeffs) elif isinstance(coeffs, (int, Variable)): coeffs = [coeffs] assert len(terms) == len(coeffs), "Lists (vars and coeffs) should have the same length. Here, we have " + str(len(terms)) + "!=" + str(len(coeffs)) # if 0 in coeffs: # terms = [term for i, term in enumerate(terms) if coeffs[i] != 0] # coeffs = [coeff for coeff in coeffs if coeff != 0] # if all(c == 1 for c in coeffs): coeffs = None checkType(coeffs, ([Variable, int], type(None))) OpOverrider.enable() return terms, coeffs terms = list(term) if isinstance(term, types.GeneratorType) else flatten(term, others) checkType(terms, ([Variable], [Node], [PartialConstraint], [ScalarProduct])) auxiliary().replace_partial_constraints(terms) terms, coeffs = _get_terms_coeffs(terms) terms, coeffs = _manage_coeffs(terms, coeffs) # TODO control here some assumptions return _wrapping_by_complete_or_partial_constraint(ConstraintSum(terms, coeffs, Condition.build_condition(condition)))
def _get_terms_coeffs(terms): if len(terms) == 1 and isinstance(terms[0], ScalarProduct): return flatten(terms[0].variables), flatten(terms[0].coeffs) if all(isinstance(x, (Variable, PartialConstraint)) for x in terms): return terms, None t1, t2 = [], [] for tree in terms: if isinstance(tree, Variable): t1.append(tree) t2.append(1) else: assert isinstance(tree, (Node, NegVariable)) pair = (tree.variable, -1) if isinstance(tree, NegVariable) else tree.tree_val_if_binary_type(TypeNode.MUL) if pair is None: break t1.append(pair[0]) t2.append(pair[1]) if len(t1) == len(terms): for tree in terms: if isinstance(tree, Node): tree.mark_as_used() return t1, t2 return terms, None
def __eq__(self, other): other = self._simplify_with_auxiliary_variables(other) if isinstance( self.constraint, (ConstraintElement, ConstraintElementMatrix)) and isinstance( other, (int, Variable)): if isinstance(self.constraint, ConstraintElement): arg = self.constraint.arguments[TypeCtrArg.LIST] arg.content = flatten( arg.content ) # we need to flatten now because it has not been done before return ECtr(self.constraint.set_value( other)) # only value must be replaced for these constraints return self.add_condition(TypeConditionOperator.EQ, other)
def __init__(self, X, name, comment=None, tags=[]): super().__init__(name, comment, tags) self.name = name self.variables = X self.flatVars = flatten(X) assert len(self.flatVars) != 0, "Array of variable empty !" self.size = [] curr = self.variables while isinstance(curr, list): self.size.append(len(curr)) curr = curr[0] VarEntities.items.append(self) for x in self.flatVars: VarEntities.varToEVarArray[x] = self VarEntities.prefixToEVarArray[name] = self if options.debug: print("New VarArray entity: ", self)
def Var(term=None, *others, dom=None): assert (term is None) != (dom is None) if term is not None: dom = flatten(term, others) if not isinstance(dom, Domain): dom = Domain(dom) name = extract_declaration_for("Var") comment, tags = comment_and_tags_of(function_name="Var") assert isinstance(comment, (str, type(None))), "A comment must be a string (or None). Usually, they are given on plain lines preceding the declaration" assert name not in Variable.name2obj, "The identifier " + name + " is used twice. This is not possible" assert dom.get_type() in {TypeVar.INTEGER, TypeVar.SYMBOLIC}, "Currently, only integer and symbolic variables are supported. Problem with " + str(dom) assert str(name) not in Variable.name2obj, "The identifier " + name + " is used twice. This is not possible" var_object = VariableInteger(name, dom) if dom.get_type() == TypeVar.INTEGER else VariableSymbolic(name, dom) Variable.name2obj[name] = var_object EVar(var_object, comment, tags) # object wrapping the variable x return var_object
def __init__(self, X, name, comment=None, tags=[]): super().__init__(name, comment, tags) self.name = name self.variables = X self.flatVars = flatten(X) if len(self.flatVars) == 0: return # assert len(self.flatVars) != 0, "Array of variable empty !" self.containing_hole = None # undefined until we ask #flatVarsKeepingNone = flatten(X, keep_none=True) self.size = [] curr = self.variables while isinstance(curr, list): self.size.append(len(curr)) curr = curr[0] VarEntities.items.append(self) for x in self.flatVars: VarEntities.varToEVarArray[x] = self VarEntities.prefixToEVarArray[name] = self
def _set_contains(self, other): # for being able to use 'in' when expressing intension/extension constraints if not OpOverrider.activated: return self.__contains__(other) if isinstance(other, types.GeneratorType): other = list(other) tself = unique_type_in(self) # if isinstance(other, Variable) and len(self) > 0 and is_containing(self, int): # unary table constraint if isinstance(other, Variable) and tself in {int, str}: # unary table constraint queue_in.append((list(self), other)) return True # if isinstance(other, (Variable, PartialConstraint)) or isinstance(other, (int, str)) and is_containing(self, Variable): # intension constraint if isinstance(other, (Variable, PartialConstraint)) or isinstance(other, (int, str)) and tself and issubclass(tself, Variable): # intension constraint queue_in.append((self, other)) return True # if is_1d_tuple(other, Variable) or is_1d_list(other, Variable): # non-unary table constraint # queue_in.append((list(self), other)) # return True if is_containing(other, Variable): # non-unary table constraint queue_in.append((list(self), flatten(other))) return True return self.__contains__(other)
def checkType(obj, allowed_types, message=""): if options.checker == "none": return True if options.checker == "fast" and isinstance( obj, (list, tuple, set, frozenset)) and len(obj) > 100: obj = obj[:1] # TODO problem here: sets cannot be indexed allowed_types = (allowed_types, ) if not isinstance( allowed_types, tuple) else allowed_types for allowedType in allowed_types: if not isinstance(allowedType, list): if isinstance(obj, allowedType): return True elif isinstance(obj, (list, tuple, set)): for p in flatten(obj): if not any(isinstance(p, typ) for typ in allowedType): break # raise TypeMCSPError(inspector.getCalling(), p, allowedTypes, message, position) else: return True if message == "": message = "Wrong type for " + str(obj) + " (allowable types: " + str( allowed_types) + ")\n" raise TypeError(message)
def _compact_constraint_arguments(arguments): for arg in list(arguments.values()): if isinstance(arg.content, list) and len(arg.content) > 0 and arg.content_compressible: if not isinstance(arg.content[0], list): # It is only one list arg.content = compact(arg.content, preserve_order=arg.content_ordered) elif arg.lifted is True: arg.content = [compact(l, preserve_order=arg.content_ordered) for l in arg.content] elif arg.name == TypeCtrArg.MATRIX: # Special case for matrix sc = None if is_containing(arg.content_compressible, int) else _simple_compact(flatten(arg.content_compressible)) arg.content = sc if sc is not None else arg.content
def _compact_constraint_group(group): preserve_order = False cnt = 0 if isinstance(group.abstraction, dict): for key, value in group.abstraction.items(): if "%" in str(value): cnt += 1 argument = group.entities[0].constraint.arguments[key] if argument.content_compressible: if "%" not in str(value): if key == TypeCtrArg.MATRIX: # Special case for matrix sc = None if is_containing(argument.content_compressible, int) else _simple_compact(flatten(argument.content_compressible)) group.abstraction[key] = sc if sc is not None else group.abstraction[key] else: group.abstraction[key] = compact(value, preserve_order=argument.content_ordered) elif argument.content_ordered is True: preserve_order = True else: preserve_order = True if cnt > 1: preserve_order = True for i in range(len(group.all_args)): group.all_args[i] = compact(group.all_args[i], preserve_order=preserve_order, group_args=True)