Matrix
This section describes the matrix formulation. For the matrix formulation, the Amplify SDK provides matrix classes that are equivalents of quadratic polynomials on binary and Ising variables.
Note
The matrix form is used when dealing with tightly coupled problems where the number of terms is so large that it is inefficient to use polynomials. If you just want to have a general idea of how to use the Amplify SDK, you may skip this section.
Matrix Representation of Quadratic Polynomials
The matrix representation of a quadratic polynomial is defined as an upper triangular matrix as follows:
Quadratic polynomials of binary variables
Define the matrix \(Q\) by placing the linear terms in the diagonal elements and the quadratic terms in the off-diagonal elements.
The relation between a polynomial and a matrix \(Q\) is expressed as follows:
Here, the variables are represented in vector form \(q^{\mathrm{T}} = \left(q_0, q_1, \cdots \right)\).
Quadratic polynomial of Ising variables
As in the binary variable case, define the matrix \(J\) by placing the linear terms in the diagonal elements and the quadratic terms in the off-diagonal elements.
The relation between a polynomial and a matrix \(J\) is expressed as follows:
Here, the variables are represented in vector form \(s^{\mathrm{T}} = \left(s_0, s_1, \cdots \right)\).
The following matrix classes are provided to represent \(Q\) and \(J\) above:
BinaryMatrix
and IsingMatrix
are matrices whose variables are the binary variables \(\left\{0, 1\right\}\) and the Ising variables \(\left\{-1, 1\right\}\), respectively.
Note
BinaryIntMatrix
and IsingIntMatrix
are used when restricting the matrix elements to integer values. Since the matrix elements are limited to integers during calculations, unintended results may occur. For this reason, it is recommended to use BinaryMatrix
and IsingMatrix
which can handle real numbers.
Creating a Matrix Class
A matrix class is created as a size
\(\times\) size
zero matrix if size
is specified as follows (henceforth, BinaryMatrix
is used as an example, but the same is true for other matrix classes including Ising ones).
from amplify import BinaryMatrix
m = BinaryMatrix(3)
>>> m
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
The diagonal and off-diagonal elements correspond to the coefficients of the linear and quadratic terms of a quadratic polynomial, respectively.
If an object of the same matrix class is given as the constructor argument, it will be copied.
from amplify import BinaryMatrix
m1 = BinaryMatrix(3)
m2 = BinaryMatrix(m1)
>>> m2
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
You can also construct a matrix class with a list of the initial values. The matrix elements can be given in the upper triangular parts or all elements as row major.
For a one-dimensional list, you need to specify the matrix size.
from amplify import BinaryMatrix
import numpy as np
m1 = BinaryMatrix(3, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
m2 = BinaryMatrix(3, [1.0, 2.0, 3.0, 0.0, 4.0, 5.0, 0.0, 0.0, 6.0])
>>> m1
[[1, 2, 3],
[0, 4, 5],
[0, 0, 6]]
>>> m2
[[1, 2, 3],
[0, 4, 5],
[0, 0, 6]]
If a two-dimensional list is given, the matrix size will be determined automatically.
from amplify import BinaryMatrix
import numpy as np
m1 = BinaryMatrix([[1.0, 2.0, 3.0], [4.0, 5.0], [6.0]])
m2 = BinaryMatrix([[1.0, 2.0, 3.0], [0.0, 4.0, 5.0], [0.0, 0.0, 6.0]])
>>> m1
[[1, 2, 3],
[0, 4, 5],
[0, 0, 6]]
>>> m2
[[1, 2, 3],
[0, 4, 5],
[0, 0, 6]]
Matrix Operators
The most important operation on a matrix object is element access. We will create a matrix corresponding to a quadratic polynomial as follows:
from amplify import BinaryMatrix
m = BinaryMatrix(3)
m[0, 0] = -2
m[0, 1] = 1
m[1, 2] = -1
m[2, 2] = 1
>>> m
[[-2, 1, 0],
[0, 0, -1],
[0, 0, 1]]
The value of the m[i, j]
element corresponds to the coefficient of \(q_i q_j\). Note that it is an upper triangular matrix, so \(i \le j\). We do not have access to the lower triangular part of the matrix.
Operators are defined for equivalence, addition, and subtraction between matrix objects, and for multiplication and division between matrix objects and constants. Compound substitution operations are also possible.
from amplify import BinaryMatrix
m1 = BinaryMatrix(3)
m1[0, 1] = 1
m1[1, 2] = -1
m1[0, 0] = -2
m1[2, 2] = 1
m2 = BinaryMatrix(m1)
>>> m1 == m2
True
>>> m1 == [[-2, 1, 0], [0, 0, -1], [0, 0, 1]]
True
>>> m1 + m2
[[-4, 2, 0],
[0, 0, -2],
[0, 0, 2]]
>>> m1 - m2
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
>>> m1 * 2
[[-4, 2, 0],
[0, 0, -2],
[0, 0, 2]]
>>> m1 / 2
[[-1, 0.5, 0],
[0, 0, -0.5],
[0, 0, 0.5]]
Methods in Matrix Classes
Evaluating Matrices
You can evaluate the polynomial expressed in terms of binary and Ising variables (given below), using the corresponding matrix object and variable vector.
For quadratic polynomial on binary variables;
For quadratic polynomial on Ising variables;
Lists and numpy.ndarray
can be treated as vectors.
from amplify import BinaryMatrix
import numpy
m = BinaryMatrix(3)
m[0, 0] = -2
m[0, 1] = 1
m[1, 2] = -1
m[2, 2] = 1
>>> m
[[-2, 1, 0],
[0, 0, -1],
[0, 0, 1]]
>>> m.evaluate([1, 0, 1])
-1.0
Matrix Resize
The size of a matrix object can be rescaled. When enlarged, the elements in the enlarged part are filled with \(0\) elements. When reduced, the elements in the reduced part are truncated.
from amplify import BinaryMatrix
m = BinaryMatrix(3)
m[0, 0] = -2
m[0, 1] = 1
m[1, 2] = -1
m[2, 2] = 1
>>> m
[[-2, 1, 0],
[0, 0, -1],
[0, 0, 1]]
m.resize(6)
>>> m
[[-2, 1, 0, 0, 0, 0],
[0, 0, -1, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]]
Note
This method is destructive.
Matrix Size
The size of a matrix object can be obtained as follows:
from amplify import BinaryMatrix
m = BinaryMatrix(3)
m[0, 0] = -2
m[0, 1] = 1
m[1, 2] = -1
m[2, 2] = 1
>>> m.size()
3
Conversion to Binary Matrix
An Ising matrix can be converted to a binary matrix. A tuple of the transformed binary matrix and a constant term is returned.
from amplify import IsingMatrix
m = IsingMatrix(3)
m[0, 0] = -0.75
m[0, 1] = 0.25
m[1, 2] = -0.25
m[2, 2] = 0.25
>>> m.to_BinaryMatrix()
([[-2, 1, 0],
[0, 0, -1],
[0, 0, 1]], 0.5)
By default, the binary variables \(0, 1\) correspond to the Ising variables \(-1, 1\), respectively.
You can flip this correspondence by giving False
to the keyword argument ascending
.
>>> m.to_BinaryMatrix(ascending=False)
([[1, 1, 0],
[0, 0, -1],
[0, 0, 0]], -0.5)
Conversion to Ising Matrix
A binary matrix can be converted to an Ising matrix. A tuple of the transformed Ising matrix and a constant term is returned.
from amplify import BinaryMatrix
m = BinaryMatrix(3)
m[0, 0] = -2
m[0, 1] = 1
m[1, 2] = -1
m[2, 2] = 1
>>> m.to_IsingMatrix()
([[-0.75, 0.25, 0],
[0, 0, -0.25],
[0, 0, 0.25]], -0.5)
By default, the binary variables \(0, 1\) correspond to the Ising variables \(-1, 1\), respectively.
You can flip this correspondence by giving False
to the keyword argument ascending
.
>>> m.to_IsingMatrix(ascending=False)
([[0.75, 0.25, 0],
[0, 0, -0.25],
[0, 0, -0.25]], -0.5)
Conversion to Quadratic Polynomial
A matrix object can be converted to the corresponding polynomial class.
from amplify import BinaryMatrix
m = BinaryMatrix(3)
m[0, 0] = -2
m[0, 1] = 1
m[1, 2] = -1
m[2, 2] = 1
>>> m.to_Poly()
q_0 q_1 - q_1 q_2 - 2.000000 q_0 + q_2
The correspondence between the matrix classes from which they are converted and the converted polynomial classes is as follows:
Matrix class |
polynomial class |
---|---|
NumPy Integration
The interconversion between a matrix class and a NumPy array is possible. Like polynomial arrays for polynomial forms, matrix classes are recommended to be constructed using the powerful array operations of NumPy ndarray
.
A matrix class can be constructed from numpy.ndarray
.
from amplify import BinaryMatrix
import numpy as np
a1 = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
m1 = BinaryMatrix(3, a1)
a2 = np.array([1.0, 2.0, 3.0, 0.0, 4.0, 5.0, 0.0, 0.0, 6.0])
m2 = BinaryMatrix(3, a2)
a3 = np.array([[1.0, 2.0, 3.0], [0.0, 4.0, 5.0], [0.0, 0.0, 6.0]])
m3 = BinaryMatrix(a3)
>>> m1
[[1, 2, 3],
[0, 4, 5],
[0, 0, 6]]
>>> m2
[[1, 2, 3],
[0, 4, 5],
[0, 0, 6]]
>>> m3
[[1, 2, 3],
[0, 4, 5],
[0, 0, 6]]
>>> m1 == a3
True
>>> m2 == a3
True
>>> m3 == a3
True
A matrix class can be converted to numpy.ndarray
.
from amplify import BinaryMatrix
import numpy as np
a1 = np.array([[1.0, 2.0, 3.0], [0.0, 4.0, 5.0], [0.0, 0.0, 6.0]])
m1 = BinaryMatrix(a)
a2 = m1.to_numpy()
>>> a2
[[1. 2. 3.]
[0. 4. 5.]
[0. 0. 6.]]
>>> type(a2).__name__
ndarray