# Declaring Variables

Below, we show how to declare stand-alone variables, and multi-dimensional arrays of variables.

**Remark.** In PyCSP$^3$, which is currently targeted to XCSP$^3$-core, we can only define integer and symbolic variables with finite domains, i.e., variables with a finite set of integers or symbols (strings).

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

```
from pycsp3 import *
```

## Stand-alone Variables

Stand-alone variables can be declared by means of the PyCSP$^3$ function *Var()*. To define the domain of a variable, we can simply list values, or use range(). For example, we can define three integer variables w, x and y, as well as a symbolic variable z as follows:

```
w = Var(range(15))
x = Var(0, 1)
y = Var(0, 2, 4, 6, 8)
z = Var("a", "b", "c")
```

The domain of a variable is given by a field called *dom*. So, we can control the domain of the declared variables:

```
print("Domain of w: ", w.dom)
print("Domain of x: ", x.dom)
print("Domain of y: ", y.dom)
print("Domain of z: ", z.dom)
```

```
Domain of w: 0..14
Domain of x: 0 1
Domain of y: 0 2 4 6 8
Domain of z: a b c
```

Note that values can be directly listed as above, or given in a set as follows:

```
clear() # to discard previously posted variables
w = Var(set(range(15)))
x = Var({0, 1})
y = Var({0, 2, 4, 6, 8})
z = Var({"a", "b", "c"})
```

We can check that we obtain similar domains:

```
print("Domain of w: ", w.dom)
print("Domain of x: ", x.dom)
print("Domain of y: ", y.dom)
print("Domain of z: ", z.dom)
```

```
Domain of w: 0..14
Domain of x: 0 1
Domain of y: 0 2 4 6 8
Domain of z: a b c
```

It is also possible to name the parameter *dom* when defining the domain:

```
clear() # to discard previously posted variables
w = Var(dom=range(15)) # or equivalently, w = Var(dom=set(range(15)))
x = Var(dom={0, 1})
y = Var(dom={0, 2, 4, 6, 8})
z = Var(dom={"a", "b", "c"})
```

We can check that we obtain similar domains:

```
print("Domain of w: ", w.dom)
print("Domain of x: ", x.dom)
print("Domain of y: ", y.dom)
print("Domain of z: ", z.dom)
```

```
Domain of w: 0..14
Domain of x: 0 1
Domain of y: 0 2 4 6 8
Domain of z: a b c
```

Finally, it is of course possible to use generators and comprehension sets. For example, for y, we can write:

```
clear()
y = Var(i for i in range (10) if i % 2 == 0)
print("Domain of y: ", y.dom)
clear()
y = Var({i for i in range (10) if i % 2 == 0})
print("Domain of y: ", y.dom)
clear()
y = Var(dom={i for i in range(10) if i % 2 == 0})
print("Domain of y: ", y.dom)
```

```
Domain of y: 0 2 4 6 8
Domain of y: 0 2 4 6 8
Domain of y: 0 2 4 6 8
```

## Arrays of Variables

The PyCSP$^3$ function for declaring an array of variables is *VarArray()* that requires two named parameters *size* and *dom*. For declaring a one-dimensional array of variables, the value of *size* must be an integer (or a list containing only one integer), for declaring a two-dimensional array of variables, the value of *size* must be a list containing exactly two integers, and so on. The named parameter *dom* indicates the domain of each variable in the array.

For example, to declare:

- x, a one-dimensional array of 10 variables with domain {0,1}
- y, a two-dimensional array of $5 \times 5$ variables with domain {0,1,…, 9}
- z, a three-dimensional array of $4 \times 3 \times 4$ variables with domain {1, 5, 10, 20}

we write:

```
clear() # to discard previously posted variables
x = VarArray(size=10, dom={0, 1})
y = VarArray(size=[5, 5], dom=range(10))
z = VarArray(size=[4, 3, 4], dom={1, 5, 10, 20})
```

We can display the (structure of the) arrays, as well as the domains of the variables.

```
print("Array x: ", x)
print("Array y: ", y)
print("Array z: ", z)
print("Domain of variables of x: ", x[0].dom)
print("Domain of variables of y: ", y[0][0].dom)
print("Domain of variables of z: ", z[0][0][0].dom)
```

```
Array x: [x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8], x[9]]
Array y: [
[y[0][0], y[0][1], y[0][2], y[0][3], y[0][4]]
[y[1][0], y[1][1], y[1][2], y[1][3], y[1][4]]
[y[2][0], y[2][1], y[2][2], y[2][3], y[2][4]]
[y[3][0], y[3][1], y[3][2], y[3][3], y[3][4]]
[y[4][0], y[4][1], y[4][2], y[4][3], y[4][4]]
]
Array z: [
[
[z[0][0][0], z[0][0][1], z[0][0][2], z[0][0][3]]
[z[0][1][0], z[0][1][1], z[0][1][2], z[0][1][3]]
[z[0][2][0], z[0][2][1], z[0][2][2], z[0][2][3]]
],
[
[z[1][0][0], z[1][0][1], z[1][0][2], z[1][0][3]]
[z[1][1][0], z[1][1][1], z[1][1][2], z[1][1][3]]
[z[1][2][0], z[1][2][1], z[1][2][2], z[1][2][3]]
],
[
[z[2][0][0], z[2][0][1], z[2][0][2], z[2][0][3]]
[z[2][1][0], z[2][1][1], z[2][1][2], z[2][1][3]]
[z[2][2][0], z[2][2][1], z[2][2][2], z[2][2][3]]
],
[
[z[3][0][0], z[3][0][1], z[3][0][2], z[3][0][3]]
[z[3][1][0], z[3][1][1], z[3][1][2], z[3][1][3]]
[z[3][2][0], z[3][2][1], z[3][2][2], z[3][2][3]]
],
]
Domain of variables of x: 0 1
Domain of variables of y: 0..9
Domain of variables of z: 1 5 10 20
```

**Important:** Indexing starts at 0. For example, x[2] is the third variable of x, and y[1] is the second row of y. Technically, variable arrays are objects that are instances of *ListVar*, a subclass of *list*; additional functionalities of such objects are useful, for example, when posting constraints *Element*.

### Arrays with Variables of Different Domains

In some situations, you may want to declare variables in an array with different domains. For a one-dimensional array, you can give the name of a function that accepts an integer i and returns the domain to be associated with the variable at index i in the array. For a two-dimensional array, you can give the name of a function that accepts a pair of integers (i,j) and returns the domain to be associated with the variable at indexes i, j in the array. And so on.

For example, suppose that the domain of all variables of the first column of y is range(5) instead of range(10). We can write:

```
clear() # to discard previously posted variables
def domain_y(i,j):
return range(5) if j == 0 else range(10)
y = VarArray(size=[5, 5], dom=domain_y)
```

We can observe that domains of variables in y are not all the same:

```
for i in range(5):
print(f"Domains on row {i}: {[y[i][j].dom for j in range(5)]}")
```

```
Domains on row 0: [0..4, 0..9, 0..9, 0..9, 0..9]
Domains on row 1: [0..4, 0..9, 0..9, 0..9, 0..9]
Domains on row 2: [0..4, 0..9, 0..9, 0..9, 0..9]
Domains on row 3: [0..4, 0..9, 0..9, 0..9, 0..9]
Domains on row 4: [0..4, 0..9, 0..9, 0..9, 0..9]
```

We can also use a lambda function:

```
clear() # to discard previously posted variables
y = VarArray(size=[5, 5], dom=lambda i,j: range(5) if j == 0 else range(10))
```

We can check:

```
for i in range(5):
print(f"Domains on row {i}: {[y[i][j].dom for j in range(5)]}")
```

```
Domains on row 0: [0..4, 0..9, 0..9, 0..9, 0..9]
Domains on row 1: [0..4, 0..9, 0..9, 0..9, 0..9]
Domains on row 2: [0..4, 0..9, 0..9, 0..9, 0..9]
Domains on row 3: [0..4, 0..9, 0..9, 0..9, 0..9]
Domains on row 4: [0..4, 0..9, 0..9, 0..9, 0..9]
```

Sometimes, not all variables in an array are relevant. For example, you may only want to use the variables in the lower part of a two-dimensional array (matrix). In that case, the value *None* must be used.

For example, if one wanted to introduce an auxiliary array y for the problem *Golomb Ruler* to compute distances between any two pairs of the main array x, we could write:

```
clear() # to discard previously posted variables
# y[i][j] is the distance between x[i] and x[j] for i strictly less than j
y = VarArray(size=[5, 5], dom=lambda i, j: range(1, 5 * 5) if i < j else None)
```

One can see that only half of the array is really used (i.e., really contains variables): the lower part (below the main downward diagonal) only contains *None*. For example, y[1][0] is equal to *None*.

```
print("Array y: ", y)
```

```
Array y: [
[None, y[0][1], y[0][2], y[0][3], y[0][4]]
[None, None, y[1][2], y[1][3], y[1][4]]
[None, None, None, y[2][3], y[2][4]]
[None, None, None, None, y[3][4]]
[None, None, None, None, None]
]
```