def Intension(node): checkType(node, Node) ctr = ECtr(ConstraintIntension(node)) if ctr.blank_basic_attributes(): ctr.copy_basic_attributes_of(node) node.mark_as_used() return ctr
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 static(self, variables, *, order): variables = flatten(variables) checkType(variables, [Variable]) order = flatten(order) checkType(order, [int]) self.staticPart = (variables, order) return self
def build_condition(condition): if condition is None: return None # it may occur when building a partial constraint condition = tuple(condition) if isinstance( condition, list ) else condition # we expect a condition to be given as a tuple (or a list) assert isinstance(condition, tuple) and len( condition ) == 2, "a condition must a pair, given as a tuple (or a list)" operator = TypeConditionOperator.value_of(condition[0]) if isinstance( condition[0], str) else condition[0] assert isinstance( operator, TypeConditionOperator), "the operator " + str( operator ) + " is not correct (should be of type TypeConditionOperator)" right_operand = list(condition[1]) if isinstance( condition[1], (set, frozenset, GeneratorType)) else condition[1] checkType(right_operand, (int, Variable, range, [int, Variable])) if isinstance(right_operand, range) and right_operand.step != 1: right_operand = list(right_operand) if isinstance(right_operand, int): return ConditionValue(operator, right_operand) if isinstance(right_operand, Variable): return ConditionVariable(operator, right_operand) if isinstance(right_operand, range): return ConditionInterval(operator, right_operand.start, right_operand.stop - 1) if isinstance(right_operand, list): return ConditionSet(operator, right_operand)
def __init__(self, h): super().__init__(TypeXML.VAR_HEURISTIC) checkType(h, VarHeuristic) self.attributes.append((TypeAnnArg.LC, h.lc)) if h.staticData: self.arg(TypeAnnArg.STATIC, h.staticData) self.add_arguments(h.randomPart, h.minPart, h.maxPart)
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 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 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 __init__(self, name, constraints, min_arity, max_arity=None): super().__init__(name) # no need to have an id here assert isinstance(constraints, list) self.entities = [c for c in constraints if c is not None] checkType(self.entities, [ECtr, EMetaCtr]) assert len(self.entities) >= min_arity, "At least " + str( min_arity) + " components must be specified in the meta-constraint" assert max_arity is None or len( self.entities) <= max_arity, "At most " + str( max_arity ) + " components must be specified in the meta-constraint"
def __init__(self, h): super().__init__(TypeXML.VAL_HEURISTIC) checkType(h, ValHeuristic) if h.staticData: self.arg(TypeAnnArg.STATIC, h.staticData[0], attributes=[ (TypeAnnArg.ORDER, " ".join(str(ele) for ele in h.staticData[1])) ]) self.add_arguments(h.randomData, h.minData, h.maxData)
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 _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 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 _optimize(term, minimization): if isinstance(term, ScalarProduct): term = Sum(term) # to have a PartialConstraint checkType(term, (Variable, Node, PartialConstraint)), "Did you forget to use Sum, e.g., as in Sum(x[i]*3 for i in range(10))" satisfy(pc == var for (pc, var) in auxiliary().collected()) comment, _, tag, _ = comments_and_tags_of_parameters_of(function_name="minimize" if minimization else "maximize", args=[term]) type = TypeCtr.MINIMIZE if minimization else TypeCtr.MAXIMIZE if isinstance(term, (Variable, Node)): if isinstance(term, Node): term.mark_as_used() return EObjective(ObjectiveExpression(type, term)).note(comment[0]).tag(tag[0]) # TODO why only one tag? else: return EObjective(ObjectivePartial(type, term)).note(comment[0]).tag(tag[0])
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 _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
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 _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 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 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 _block(*_args): def _reorder(_entities): reordered_entities = [] g = [] for c in _entities: if isinstance(c, ECtr): g.append(c) else: if len(g) != 0: reordered_entities.append(_group(g)) g.clear() reordered_entities.append(c) if len(g) != 0: reordered_entities.append(_group(g)) return reordered_entities entities = _wrap_intension_constraints(_complete_partial_forms_of_constraints(flatten(*_args))) checkType(entities, [ECtr, ECtrs]) return EBlock(_reorder(entities))
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 VarArray(*, size, dom, comment=None): size = [size] if isinstance(size, int) else size assert all(dimension != 0 for dimension in size), "No dimension must not be equal to 0" checkType(size, [int]) checkType(dom, (range, Domain, [int, range, str, Domain, type(None)], type(lambda: 0))) name = extract_declaration_for("VarArray") if comment is None and not isinstance(name, list): comment, tags = comment_and_tags_of(function_name="VarArray") 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 not isinstance(dom, type(lambda: 0)) or len(size) == len(inspect.signature(dom).parameters), \ "The number of arguments of the lambda must be equal to the number of dimensions of the multidimensional array" 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 str(name) not in Variable.name2obj, "The identifier " + name + " is used twice. This is not possible" var_objects = Variable.build_variables_array(name, size, dom) if isinstance(name, list): for variable in var_objects: EVar(variable, None, None) # object wrapping the variables return tuple(var_objects) else: EVarArray(var_objects, name, comment, tags) # object wrapping the array of variables return ListVar(var_objects)
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 __init__(self, constraints): checkType(constraints, [ECtr]) assert len( constraints ) == 3, "Error: three components must be specified in ifThenElse" super().__init__(constraints)
def __init__(self, variables): super().__init__(TypeXML.OUTPUT) variables = flatten(variables) checkType(variables, [Variable]) self.arg(TypeXML.OUTPUT, variables)
def __init__(self, variables): super().__init__(TypeXML.DECISION) variables = flatten(variables) checkType(variables, [Variable]) self.arg(TypeXML.DECISION, variables)
def random(self, variables=None): variables = flatten(variables) checkType(variables, ([Variable], type(None))) self.randomPart = (variables, ) return self
def static(self, variables): variables = flatten(variables) checkType(variables, [Variable]) self.staticPart = variables return self