2. Creating Decision Variables#

You must express the problem formulation in the program code to solve a combinatorial optimization problem with Amplify SDK. This page describes the first step in the formulation: generating the decision variables.

See also

To issue decision variables, you can also use the matrix() method, as well as the methods described on this page, scalar() and array() of the VariableGenerator class.

You can use the matrix() method to create a quadratic objective function in a coefficient matrix format. See “Objective function with a coefficient matrix” for details.

2.1. Creating a variable generator#

To formulate a combinatorial optimization problem, it is first necessary to create an instance of the VariableGenerator class. This class provides methods for issuing decision variables.

from amplify import VariableGenerator

gen = VariableGenerator()

2.2. Variable generation#

You can use the scalar() method of the VariableGenerator class to issue a new decision variable by specifying the variable type.

Variable type

Description

Binary

A variable taking the value 0 or 1

Ising

A variable taking the value -1 or 1

Integer

A variable taking an integer value

Real

A variable taking a real value

Following is an example of issuing a new binary variable. The variable is returned as an instance of the Poly class, which represents the polynomial of the variable.

>>> q = gen.scalar("Binary")
>>> print(q)
q_0

Issued variables are automatically named, but you can also specify a name.

>>> i = gen.scalar("Ising", name="i")
>>> print(i)
i

Integer or real variables can have a range of possible values.

>>> x = gen.scalar("Real", bounds=(2.0, 3.0))
>>> print(x)
x_0

2.3. Creating an array of variables#

If you want to create multiple variables at once, you can use the array() method of the VariableGenerator class. This method returns an array of variables in a NumPy-like multidimensional array format.

You can create an array of 3 binary variables as follows. Once the new variables are created, the variable names are automatically added with a string representing the array index.

>>> gen = VariableGenerator()
>>> q = gen.array("Binary", 3)
>>> print(q)
[q_0, q_1, q_2]

You can retrieve the elements and slices like a Python list or 1D NumPy array.

>>> print(q[0])
q_0
>>> print(q[:2])
[q_0, q_1]

The array() method of the VariableGenerator class can also create multidimensional arrays. The following example creates a two-dimensional \(2 \times 3\) variable array.

>>> gen = VariableGenerator()
>>> q = gen.array("Binary", shape=(2, 3))
>>> print(q)
[[q_{0,0}, q_{0,1}, q_{0,2}],
 [q_{1,0}, q_{1,1}, q_{1,2}]]

You can retrieve elements and slices in the same way as for a two-dimensional numpy.ndarray. The retrieved element or slice will be the view of the original array.

>>> print(q[0, 0])
q_{0,0}
>>> print(q[0, :])
[q_{0,0}, q_{0,1}, q_{0,2}]
>>> print(q[:, 0])
[q_{0,0}, q_{1,0}]

You can specify the lower and upper bounds for variables simultaneously at output time.

>>> n = gen.array("Integer", shape=(5,), bounds=(1, 3))
>>> print(n)
[n_0, n_1, n_2, n_3, n_4]

See also

The array() method returns an instance of the PolyArray class, representing a polynomial array. The PolyArray class provides a variety of methods compatible with ndarray, as well as element retrieval and slicing. See the PolyArray class reference for details.

2.4. Getting variable information#

To get variable information from a given variable, use the as_variable() method.

gen = VariableGenerator()
q = gen.scalar("Binary")
i = gen.scalar("Ising", name="i")
x = gen.scalar("Real", bounds=(2.0, 3.0))
>>> print(q.as_variable())
{name: q_0, id: 0, type: Binary}

The Amplify SDK provides variable information as an instance of the Variable class, which has the following attributes.

Attribute

Data type

Details

name

str

Variable name

id

int

Variable ID number
An integer value assigned starting from 0 in order of issuance

type

VariableType

Variable type

lower_bound

float

[Integer or real variable only] Variable lower bound
None means \(- \inf\).

upper_bound

float

[Integer or real variable only] Variable upper bound
None means \(+ \inf\).

In addition, information about variables issued by the VariableGenerator class can be obtained using the variables property.

>>> vars = gen.variables
>>> vars 
[Variable({name: q_0, id: 0, type: Binary}),
 Variable({name: i, id: 1, type: Ising}),
 Variable({name: x_0, id: 2, type: Real, lower_bound: 2, upper_bound: 3})]

You can change the variable’s name and lower and upper bounds later.

vars[2].name = "r"
vars[2].lower_bound = 0.0
vars[2].upper_bound = 1.0
>>> print(vars[2])
{name: r, id: 2, type: Real, lower_bound: 0, upper_bound: 1}

Attention

The variables returned by the methods such as the scalar() method are instances of the Poly class. Note that the Poly class can represent not only single variables but also general polynomials. However, the as_variable() method will only succeed if the Poly class instance represents a unary variable.

Tip

For convenience, when the is_variable() method returns True (as_variable() method succeeds), you can access the attributes of the Variable class directly.

>>> q.is_variable()
True
>>> q.name
'q_0'
>>> q.id
0
>>> q.type
Binary

Tip

For each instance of the Poly class, you can retrieve information about all of the variables contained in that instance with the variables property; see “Polynomials and Objective Functions” for details about the Poly class.

>>> q.variables
[Variable({name: q_0, id: 0, type: Binary})]