First order logic constraints¶
Builtin constraints may not be sufficient to cover the large number of use-cases user may encounter. Rather than extending more and more the builtin constraints, ProcessScheduler lets you build your own constraints using logical operators, implications and if-then-else statement between builtin constraints or class attributes.
Logical operators and (\(\wedge\)), or (\(\lor\)), xor (\(\oplus\)), not (\(\lnot\)) are provided through the functions
Take care of the trailing underscore character at the end of the function names. They are necessary because
not are python keywords that cannot be overloaded. This naming convention may conflict with functions from the
operator standard module.
Using builtin task constraints in combination with logical operators enables a rich expressivity. Imagine that you need a task \(t_1\) to NOT start at time 3. At a first glance, you can expect a
TaskDontStartAt to fit your needs, but it is not available from the builtin constraints library. The solution is to express this constraint in terms of first order logic, and state that you need the rule:
In python, this gives:
You can combine/nest any of these operators to express a complex constraint. For example, if you don’t want the task to start at 3, and also you don’t want it to end at 9, then the rule to implement is:
and_([not_(TaskStartAt(t_1, 3)), not_(TaskEndAt(t_1, 9))])
In a more general cas, those logical functions can take both task constraints or tasks attributes. For example, the following assertion is possible :
problem.add_constraint(t1.start == t_2.end + t_4.duration)
Logical Implication - Conditional expressions¶
The logical implication (\(\implies\)) is wrapped by the
implies() function. It takes two parameters: a condition, that always has to be
False, and a list of assertions that are to be implied if the condition is
True. For example, the following logical implication:
is written in Python:
consequence = implies(t_2.start == 4, [TasksEndSynced(t_3, t_4)] problem.add_constraint(consequence)
Finally, an if/then/else statement is available through the function
if_then_else() which takes 3 parameters: a condition and two lists of assertions that applies whether the condition is
ite = if_then_else(t_2.start == 4, # condition [TasksEndSynced(t_3, t_4)], # if the condition is True [TasksStartSynced(t_3, t_4)]) # if the condition is False problem.add_constraint(ite)
if_then_else() functions names do not conflict with any other function name from another package, thus dont have any underscore suffix.