# Constraint *Intension*

A constraint *Intension* corresponds to a Boolean expression that may involve variables, constants and various (arithmetic, relational and logical) operators. For example, the constraint $x+y = z$ corresponds to an equation, which is an expression evaluated to false or true according to the values assigned to the variables $x$, $y$ and $z$.

**Remark.** Note that for equality, we need to use ‘==’ in Python (the operator ‘=’ used for assignment cannot be redefined), and so, the previous constraint must be written $x+y == z$ in PyCSP$^3$.

**Remark.** Note that the integer values $0$ and $1$ are respectively equivalent to the Boolean values false and true. This allows us to combine Boolean expressions with arithmetic operators (for example, addition) without requiring any type conversions. For example, it is valid to write $(x < 5) + (y < z) == 1$ for stating that exactly one of the Boolean expressions $x<5$ and $y<z$ must be true, although it may be possible (and/or relevant) to write it differently.

To build predicates, classical arithmetic, relational and logical operators (and functions) are available; they are presented in the following tables.

Arithmetic operators | |
---|---|

$+$ | addition |

$-$ | subtraction |

$*$ | multiplication |

// | integer division |

% | remainder |

** | power |

Relational Operators | |
---|---|

$<$ | Less than |

$<=$ | Less than or equal |

$>=$ | Greater than or equal |

$>$ | Greater than |

$!=$ | Different from |

$==$ | Equal to |

Set Operators | |
---|---|

in | membership |

not in | non membership |

Logical Operators | |
---|---|

$\sim$ | logical not |

| | logical or |

\ | logical and |

^ | logical xor |

Functions | |
---|---|

abs() | absolute value of the argument |

min() | minimum value of 2 or more arguments |

max() | maximum value of 2 or more arguments |

dist() | distance between the 2 arguments |

both() | conjunction of 2 arguments |

either() | disjunction of 2 arguments |

conjunction() | conjunction of 2 or more arguments |

disjunction() | disjunction of 2 or more arguments |

imply() | implication between 2 arguments |

iff() | equivalence between 2 or more arguments |

ift() | ift(b,u,v) returns u if b is true, v otherwise |

Expressions | Observations |
---|---|

x + y < 10 | equivalent to 10 > x + y |

x*2 - 10*y + 5 == 100 | we need to use `==' in Python |

abs(z[0] - z[1]) >= 2 | equivalent to dist(z[0], z[1]) >= 2 |

(x == y) | (y == 0) | parentheses are required |

both(x == 0, y > 0) | equivalent to (x = 0) & (y > 0) |

disjunction(x < 2, y < 4, x > y) | equivalent to (x < 2) | (y < 4) | (x > y) |

imply(x == 0, y > 0) | equivalent to (x = 0) | (y > 0) |

iff(x > 0, y > 0) | equivalent to (x > 0) == (y > 0) |

(x == 0) ^ (y ==1) | use of the logical xor operator |

ift(x == 0, 5, 10) | the value is 5 if x is 0 else 10 |

Below, we give several illustrations corresponding to representative use cases of the constraint *Intension*.

## Case 1: Simple Arithmetic Constraint

The first case is a constraint *Intension* that corersponds to a simple arithmetic constraint.

To see how this constraint works, we need first to import the library PyCSP$^3$:

```
from pycsp3 import *
```

For our illustration, we introduce two stand-alone variables $x$ and $y$, each one with $0..9$ as domain

```
x = Var(range(10))
y = Var(range(10))
```

We simply post a single constraint *Intension* imposing that the sum of the values assigned to the variables $x$ and $y$ is strictly less than 10.

```
satisfy(
x + y < 10
);
```

We can display the internal representation of the posted constraint; although a little bit technical, we can see that the constraint is correctly recorded (note that ‘lt’ stands for ‘(strictly) less than’).

```
print(posted())
```

```
intension(function:lt(add(x,y),10))
```

By calling the function solve(), we can check that the problem (actually, the single constraint) is satisfiable (SAT). We can also print the values assigned to the variables in the found solution by calling the function *value()*.

```
if solve() is SAT:
print("Values of x and y: ", value(x), value(y))
```

```
Values of x and y: 0 0
```

On can anticipate that the number of solutions (supports) for this constraint is $\sum_{i=1}^{10} i = 55$

```
if solve(sols=ALL) is SAT:
print("Number of solutions: ", n_solutions())
```

```
Number of solutions: 55
```

## Case 2: Another Arithmetic Constraint

To test another constraint, we remove the one that has just been posted by calling the function *unpost()*.

```
unpost()
```

We control that there are no more constraints.

```
print(posted())
```

```
[]
```

We now post a single constraint *Intension* imposing that the distance between the values assigned to the variables $x$ and $y$ is strictly less than 10.

```
satisfy(
abs(x - y) >= 7
);
```

We can enumerate all solutions (supports) of this constraint as follows:

```
if solve(sols=ALL) is SAT:
for i in range(n_solutions()):
print(f"Solution {i+1}: {value(x, sol=i)} {value(y, sol=i)}")
```

```
Solution 1: 0 7
Solution 2: 0 8
Solution 3: 0 9
Solution 4: 1 9
Solution 5: 2 9
Solution 6: 7 0
Solution 7: 8 0
Solution 8: 9 0
Solution 9: 9 1
Solution 10: 8 1
Solution 11: 9 2
Solution 12: 1 8
```

## Case 3: A Logical Combination

The third case is about a constraint *Intension* involving a logical operator.

First, we need to remove the one that has just been posted by calling the function *unpost()*.

```
unpost()
```

We now post a single constraint *Intension* imposing that either the value assigned to $x$ is strictly less than 2 or the value assigned to $y$ is equal to 4.

If we post this inadequate form of constraint *Intension*, we obtain a warning.

```
satisfy(
x < 2 or y == 4
);
```

```
[93mWarning: [0mA node is evaluated as a Boolean (technically, __bool__ is called).
It is likely a problem with the use of logical operators.
For example, you must write (x[0] == x[1]) | (x[0] == x[2]) instead of (x[0] == x[1]) or (x[0] == x[2])
It is also possible that you write: If(cond, Then=...) while cond being not constraint-based
that is, not involving a variable of the model; and this is not possible.
See also the end of section about constraint Intension in chapter 'Twenty popular constraints' of the guide.
This is: lt(x,2)
```

By displaying the internal representation of the constraint, one can clearly see that there is a problem.

```
print(posted())
```

```
intension(function:lt(x,2))
```

The problem is that **we cannot use the keywords or, and and not**, when writing constraints *Intension*. This is because we cannot redefine these keywords. Instead, **we have to use the operators |, & and ~**.

Let us discard this flawed expression.

```
unpost()
```

Imagine now that we write the expression as follows:

```
satisfy(
x < 2 | y == 4
);
```

An error occurs because an int and a Variable cannot be combined by the operator |.

And if we try this equivalent expression, we obtain a warning.

```
satisfy(
2 > x | y == 4
);
```

```
[93mWarning: [0mA node is evaluated as a Boolean (technically, __bool__ is called).
It is likely a problem with the use of logical operators.
For example, you must write (x[0] == x[1]) | (x[0] == x[2]) instead of (x[0] == x[1]) or (x[0] == x[2])
It is also possible that you write: If(cond, Then=...) while cond being not constraint-based
that is, not involving a variable of the model; and this is not possible.
See also the end of section about constraint Intension in chapter 'Twenty popular constraints' of the guide.
This is: lt(or(x,y),2)
```

By displaying the internal representation of the constraint, one can clearly see that there is again a problem.

```
print(posted())
```

```
intension(function:eq(or(x,y),4))
```

The problem is that we need to surround subexpressions with brackets.

Let us discard this flawed expression.

```
unpost()
```

And, now let us write:

```
satisfy(
(x < 2) | (y == 4)
);
```

Let us print the posted constraint, to control it.

```
print(posted())
```

```
intension(function:or(lt(x,2),eq(y,4)))
```

That looks correct. So let us run the solver:

```
if solve() is SAT:
print("Values of x and y: ", value(x), value(y))
```

```
Values of x and y: 0 0
```

To be convinced that everything is ok, let us enumerate all solutions.

```
if solve(sols=ALL) is SAT:
for i in range(n_solutions()):
print(f"Solution {i+1}: {value(x, sol=i)} {value(y, sol=i)}")
```

```
Solution 1: 0 0
Solution 2: 0 1
Solution 3: 0 2
Solution 4: 0 3
Solution 5: 0 4
Solution 6: 0 5
Solution 7: 0 6
Solution 8: 0 7
Solution 9: 0 8
Solution 10: 0 9
Solution 11: 1 9
Solution 12: 1 0
Solution 13: 1 1
Solution 14: 1 2
Solution 15: 1 3
Solution 16: 1 4
Solution 17: 2 4
Solution 18: 3 4
Solution 19: 4 4
Solution 20: 5 4
Solution 21: 6 4
Solution 22: 7 4
Solution 23: 8 4
Solution 24: 9 4
Solution 25: 1 5
Solution 26: 1 6
Solution 27: 1 7
Solution 28: 1 8
```

Often it is possible to avoid using the logic operators by calling the functions *both* and *either* (and the more general functions *conjunction* and *disjunction*). For example:

```
unpost()
satisfy(
either(x < 2, y == 4)
)
print(posted())
```

```
intension(function:or(lt(x,2),eq(y,4)))
```