# Symbolic Math#

*dimod*’s support for symbolic math can simplify your coding of problems. For
example, consider a problem of finding the rectangle with the greatest area for
a given perimeter, which you can formulate mathematically as,

where the components are,

**Variables**: \(i\) and \(j\) are the lengths of two sides of the rectangle and the length of the perimeter is arbitrarily selected to be 8.**Objective**: maximize the area, which is given by the standard geometric formula \(ij\).**Constraint**: subject to not exceeding the given perimeter length; that is, \(2i+2j\), the summation of the rectangle’s four sides, is constrained to a maximum value of 8.

*dimod*’s symbolic math enables an intuitive coding of such problems:

```
>>> i, j = dimod.Integers(["i", "j"])
>>> objective = -i * j
>>> constraint = 2 * i + 2 * j <= 8
>>> print(objective.to_polystring())
-i*j
>>> print(constraint.to_polystring())
2*i + 2*j <= 8
```

Here variables `i,j`

are of type integer, perhaps representing the number
of tiles laid horizontally and vertically in creating a rectangular floor, and
the coded `objective`

is set to negative because D-Wave samplers minimize
rather than maximize.

The foundation for this symbolic representation is single-variable models.

## Variables as Models#

To symbolically represent an objective or constraint, you first need symbolic representations of variables.

Quadratic models with a single variable can represent your variables; for example, if the type of variable you need is integer:

```
>>> i = dimod.Integer('i')
>>> i
QuadraticModel({'i': 1.0}, {}, 0.0, {'i': 'INTEGER'}, dtype='float64')
```

Here, variable `i`

is a `QuadraticModel`

that contains one variable with the label `'i'`

.

This works because quadratic models (QMs) have the form,

where \(\{ x_i\}_{i=1, \dots, N}\) can be integer variables (\(a_{i}, b_{ij}, c\) are real values). If you set \(a_1=1\) and all remaining coefficients to zero, the model represents a single variable, \(x_1\).

When your variable is used in a linear term of a polynomial, such as \(3.75i\),
the coefficient (\(3.75\)) is represented in this same model by the value of
the linear bias on the `'i'`

–labeled variable:

```
>>> 3.75 * i
QuadraticModel({'i': 3.75}, {}, 0.0, {'i': 'INTEGER'}, dtype='float64')
```

Similarly, when your variable is in a quadratic term, such as \(2.2i^2\), the coefficient (\(2.2\)) is represented in this same model by the value of the quadratic bias, \(b_{1, 1} = 2.2\):

```
>>> 3.75 * i + 2.2 * i * i
QuadraticModel({'i': 3.75}, {('i', 'i'): 2.2}, 0.0, {'i': 'INTEGER'}, dtype='float64')
```

You can see the various methods of creating such variables in the Generators reference documentation.

Typically, you have more than a single variable, and your variables interact.

## Operations on Variables#

Consider a simple problem of a NOT operation between two binary variables. For \(\{-1, 1\}\)–valued binary variables, the NOT operation is equivalent to multiplication of the two variables:

```
>>> s1, s2 = dimod.Spins(["s1", "s2"])
>>> bqm_not = s1*s2
>>> bqm_not
BinaryQuadraticModel({'s1': 0.0, 's2': 0.0}, {('s2', 's1'): 1.0}, 0.0, 'SPIN')
>>> print(dimod.ExactSolver().sample(bqm_not))
s1 s2 energy num_oc.
1 +1 -1 -1.0 1
3 -1 +1 -1.0 1
0 -1 -1 1.0 1
2 +1 +1 1.0 1
['SPIN', 4 rows, 4 samples, 2 variables]
```

The symbolic multiplication between variables above executes a multiplication between the models representing each variable. Binary quadratic models (BQMs) are of the form:

\[\sum_{i=1} a_i v_i + \sum_{i<j} b_{i,j} v_i v_j + c \qquad\qquad v_i \in\{-1,+1\} \text{ or } \{0,1\}\]

where \(a_{i}, b_{ij}, c\) are real values. The multiplication of two such models, with linear terms \(a_1 = 1\), reduces to \(\sum_{i=1} 1 v_1 * \sum_{i=1} 1 u_1 = v_1 u_1\), a multiplication of two variables.

In this NOT example, because all the variables are the same `Vartype`

,
dimod represents each binary variable, and their multiplication, with
`BinaryQuadraticModel`

objects.

```
>>> bqm_not.vartype is dimod.Vartype.SPIN
True
```

If an operation includes more than one type of variable, the representation is
always a `QuadraticModel`

and the
`Vartype`

is per variable:

```
>>> qm = bqm_not + 3.75 * i
>>> print(type(qm))
<class 'dimod.quadratic.quadratic_model.QuadraticModel'>
>>> qm.vartype("s1") == dimod.Vartype.SPIN
True
>>> qm.vartype("i") == dimod.Vartype.INTEGER
True
```

Note

An important distinction is that `x = dimod.Binary('x')`

, for example,
instantiates a model with a variable label `'x'`

and not a free-floating variable
labeled `x`

. Consequently, you can add `x`

to another model by adding the two
models,

```
>>> x = dimod.Binary("x")
>>> bqm = dimod.BinaryQuadraticModel('BINARY')
>>> bqm += x
```

which adds the variable labeled `'x'`

in the single-variable BQM, `x`

, to
model `bqm`

. You cannot add `x`

to a model—as though it were variable
`'x'`

—by doing `bqm.add_variable(x)`

.

## Representing Constraints#

Many real-world problems include constraints. Typically constraints are either
equality or inequality, in the form of a left-hand side(`lhs`

), right-hand side
(`rhs`

), and the `dimod.sym.Sense`

(\(\le\), \(\ge\), or
\(==\)). For example, the constraint of the rectangle problem above,

has a `lhs`

of \(2i+2j\) less or equal to a `rhs`

of a some real number
(\(8\)):

```
>>> print(constraint.lhs.to_polystring(), constraint.sense.value, constraint.rhs)
2*i + 2*j <= 8
```

You can create such an equality or inequality symbolically, and it is shown with the model:

```
>>> print(type(3.75 * i <= 4))
<class 'dimod.sym.Le'>
>>> 3.75 * i <= 4
Le(QuadraticModel({'i': 3.75}, {}, 0.0, {'i': 'INTEGER'}, dtype='float64'), 4)
```

Note

*dimod* requires that the right-hand side of any equation to be a `float`

or an `int`

. For example,

can be transformed into a form supported by *dimod* by subtracting the right-hand side
from both sides.

You can then create the inequality symbolically.

```
i, j = dimod.Integers(['i', 'j'])
i + j - i*j <= 0
```

## Performance#

*dimod*’s symbolic math is very useful for small models used for experimenting
and formulating problems. It also offers some more performant functionality; for
example, methods such as `IntegerArray()`

for creating multiple
variables with NumPy arrays or `quicksum()`

as a replacement
for the Python `sum()`

.

See the examples of `BinaryArray()`

, `IntegerArray()`

,
and `SpinArray()`

for usage.