# Boolean Logic¶

Tensor networks make for a great representation of Boolean expressions. Let’s have a look at how to build tensor Boolean formulas and what we can do with them.

```
[1]:
```

```
import tntorch as tn
import torch
p, q, r = tn.symbols(3)
```

These symbols are plain *tntorch* tensors (in this case, 3D ones) and have rank 1 each:

```
[2]:
```

```
p
```

```
[2]:
```

```
3D TT tensor:
2 2 2
| | |
(0) (1) (2)
/ \ / \ / \
1 1 1 1
```

The fun starts with overloaded Boolean operators:

```
[3]:
```

```
tn.is_tautology(p | ~p) # Formula is always true
```

```
[3]:
```

```
True
```

```
[4]:
```

```
tn.is_contradiction(p & ~p) # Formula is always false
```

```
[4]:
```

```
True
```

```
[5]:
```

```
tn.is_satisfiable(p ^ q | r) # Formula is true for some values
```

```
[5]:
```

```
True
```

```
[6]:
```

```
tn.implies(p & q | q & r, q) # First formula implies the second
```

```
[6]:
```

```
True
```

```
[7]:
```

```
tn.equiv(p & q, ~(~p | ~q)) # A double implication (De Morgan's law)
```

```
[7]:
```

```
True
```

Check out the quantifiers too:

```
[8]:
```

```
tn.equiv(tn.all(3), p & q & r) # "for all"
```

```
[8]:
```

```
True
```

```
[9]:
```

```
tn.equiv(tn.any(3), p | q | r) # "exists"
```

```
[9]:
```

```
True
```

```
[10]:
```

```
tn.equiv(tn.none(3), ~tn.any(3)) # "does not exist"
```

```
[10]:
```

```
True
```

```
[11]:
```

```
tn.equiv(tn.one(3), p&~q&~r | ~p&q&~r | ~p&~q&r) # "exists and is unique"
```

```
[11]:
```

```
True
```

To find out the relevant symbols of a formula (those whose values can affect its result), do the following:

```
[12]:
```

```
tn.relevant_symbols(p & q | ~p & q)
```

```
[12]:
```

```
[1]
```

The ranks grow exponentially after each binary operation. It is recommended to run `round()`

to try to reduce those ranks, often saving up memory and computational costs:

```
[13]:
```

```
formula = ~p | (p & q) | (q & r)
print(formula)
formula.round()
print(formula)
```

```
3D TT tensor:
2 2 2
| | |
(0) (1) (2)
/ \ / \ / \
1 11 11 1
3D TT tensor:
2 2 2
| | |
(0) (1) (2)
/ \ / \ / \
1 2 2 1
```

How many ways are there to satisfy a given Boolean formula? This is known as the #SAT problem. For us, it’s just the sum of elements of the tensor:

```
[14]:
```

```
tn.sum(formula)
```

```
[14]:
```

```
tensor(6.0000)
```

We can look at all the inputs that satisfy this formula with the function `accepted_inputs()`

:

```
[15]:
```

```
tn.accepted_inputs(formula) # One row per accepted input
```

```
[15]:
```

```
tensor([[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[0, 1, 1],
[1, 1, 0],
[1, 1, 1]])
```

The function `only()`

is useful to enforce that *only* certain variables play a role:

```
[16]:
```

```
tn.accepted_inputs(tn.only(p) | tn.only(r))
```

```
[16]:
```

```
tensor([[0, 0, 1],
[1, 0, 0]])
```

## Applications¶

Boolean tensors can be applied in ANOVA and variance-based sensitivity analysis as well as for compact derivative computation.