# Custom Mixer for QAOA: Error 'Operator' object has no attribute 'primitive_strings'

I like to use as custom Mixer Hamiltonian for solving the TSP using QAOA. The mixer and cost hamiltonians are described here: https://arxiv.org/pdf/1709.03489.pdf - Chapter 5.1.

Therefore I need to write the custom mixer hamiltonian from equations 54 -58.

Here is the code:

import numpy as np
from qiskit import Aer
from qiskit.aqua import QuantumInstance
from qiskit.quantum_info.operators import Operator, Pauli
from qiskit.aqua.operators.list_ops import SummedOp
from qiskit.aqua.algorithms import QAOA
from qiskit.aqua.components.optimizers import SPSA
from qiskit.optimization.applications.ising importtsp

def pauli(pos, num_qubits, label):
label = 'I'*(pos) + label + 'I'*(num_qubits-pos-1)
assert(len(label) == num_qubits)
return Operator(Pauli(label=label))

def s_plus(number_of_nodes, city, time):
num_qubits = number_of_nodes**2
qubit = time * number_of_nodes + city
return pauli(qubit, num_qubits, "X") + pauli(qubit, num_qubits, "Y")

def s_minus(number_of_nodes, city, time):
num_qubits = number_of_nodes**2
qubit = time * number_of_nodes + city
return pauli(qubit, num_qubits, "X") - pauli(qubit, num_qubits, "Y")

def create_mixer_operators(n):
"""
Creates mixer operators for the QAOA.
It's based on equations 54 - 58 from https://arxiv.org/pdf/1709.03489.pdf
Indexing here comes directly from section 4.1.2 from paper 1709.03489, equations 54 - 58.
"""
mixer_operators = []
for t in range(n - 1):
for city_1 in range(n):
for city_2 in range(n):
i = t
u = city_1
v = city_2
first_part = 1
first_part *= s_plus(n, u, i)
first_part *= s_plus(n, v, i+1)
first_part *= s_minus(n, u, i+1)
first_part *= s_minus(n, v, i)

second_part = 1
second_part *= s_minus(n, u, i)
second_part *= s_minus(n, v, i+1)
second_part *= s_plus(n, u, i+1)
second_part *= s_plus(n, v, i)
mixer_operators.append(first_part + second_part)
return mixer_operators

seed = 10598
n = 3
p = 2
num_qubits = n ** 2

# Generate random tsp
ins = tsp.random_tsp(n, seed=seed)

qubitOp, offset = tsp.get_operator(ins)

# Running in quantum simulation
aqua_globals.random_seed = np.random.default_rng(seed)
backend = Aer.get_backend('qasm_simulator')
quantum_instance = QuantumInstance(backend, seed_simulator=seed, seed_transpiler=seed)
mixer = create_mixer_operators(n)
mixer_op = SummedOp(mixer)

spsa = SPSA(maxiter=300)
qaoa = QAOA(operator=qubitOp, mixer=mixer_op, p=p, optimizer=spsa, quantum_instance=quantum_instance)

circuits = qaoa.construct_circuit([0]*(2*p))


When runnning it I get:

    ---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-113-bd68aaa30bfc> in <module>
----> 1 circuits = qaoa.construct_circuit([0]*(2*p))

~/masterarbeit/code/venv/lib/python3.6/site-packages/qiskit/aqua/algorithms/minimum_eigen_solvers/vqe.py in construct_circuit(self, parameter)
358             A list of the circuits used to compute the expectation value.
359         """
--> 360         expect_op = self.construct_expectation(parameter).to_circuit_op()
361
362         circuits = []

~/masterarbeit/code/venv/lib/python3.6/site-packages/qiskit/aqua/algorithms/minimum_eigen_solvers/vqe.py in construct_expectation(self, parameter)
331             wave_function = self.var_form.assign_parameters(param_dict)
332         else:
--> 333             wave_function = self.var_form.construct_circuit(parameter)
334
335         # Expectation was never created, try to create one

~/masterarbeit/code/venv/lib/python3.6/site-packages/qiskit/aqua/algorithms/minimum_eigen_solvers/qaoa/var_form.py in construct_circuit(self, parameters, q)
96
97         evolution = EvolutionFactory.build(self._cost_operator)
---> 98         circuit = evolution.convert(circuit)
99         return circuit.to_circuit()
100

~/masterarbeit/code/venv/lib/python3.6/site-packages/qiskit/aqua/operators/evolutions/pauli_trotter_evolution.py in convert(self, operator)
100         #     # Sort into commuting groups
101         #     operator = self._grouper.convert(operator).reduce()
--> 102         return self._recursive_convert(operator)
103
104     def _recursive_convert(self, operator: OperatorBase) -> OperatorBase:

~/masterarbeit/code/venv/lib/python3.6/site-packages/qiskit/aqua/operators/evolutions/pauli_trotter_evolution.py in _recursive_convert(self, operator)
127                 return operator.primitive.__class__(converted_ops, coeff=operator.coeff)
128         elif isinstance(operator, ListOp):
--> 129             return operator.traverse(self.convert).reduce()
130
131         return operator

~/masterarbeit/code/venv/lib/python3.6/site-packages/qiskit/aqua/operators/list_ops/list_op.py in traverse(self, convert_fn, coeff)
161             return ListOp([convert_fn(op) for op in self.oplist],  # type: ignore
162                           combo_fn=self.combo_fn, coeff=coeff, abelian=self.abelian)
--> 163         return self.__class__([convert_fn(op) for op in self.oplist],  # type: ignore
164                               coeff=coeff, abelian=self.abelian)
165

~/masterarbeit/code/venv/lib/python3.6/site-packages/qiskit/aqua/operators/list_ops/list_op.py in <listcomp>(.0)
161             return ListOp([convert_fn(op) for op in self.oplist],  # type: ignore
162                           combo_fn=self.combo_fn, coeff=coeff, abelian=self.abelian)
--> 163         return self.__class__([convert_fn(op) for op in self.oplist],  # type: ignore
164                               coeff=coeff, abelian=self.abelian)
165

~/masterarbeit/code/venv/lib/python3.6/site-packages/qiskit/aqua/operators/evolutions/pauli_trotter_evolution.py in convert(self, operator)
100         #     # Sort into commuting groups
101         #     operator = self._grouper.convert(operator).reduce()
--> 102         return self._recursive_convert(operator)
103
104     def _recursive_convert(self, operator: OperatorBase) -> OperatorBase:

~/masterarbeit/code/venv/lib/python3.6/site-packages/qiskit/aqua/operators/evolutions/pauli_trotter_evolution.py in _recursive_convert(self, operator)
104     def _recursive_convert(self, operator: OperatorBase) -> OperatorBase:
105         if isinstance(operator, EvolvedOp):
--> 106             if not {'Pauli'} == operator.primitive_strings():
107                 logger.warning('Evolved Hamiltonian is not composed of only Paulis, converting to '
108                                'Pauli representation, which can be expensive.')

~/masterarbeit/code/venv/lib/python3.6/site-packages/qiskit/aqua/operators/evolutions/evolved_op.py in primitive_strings(self)
53
54     def primitive_strings(self) -> Set[str]:
---> 55         return self.primitive.primitive_strings()  # type: ignore
56
57     @property

~/masterarbeit/code/venv/lib/python3.6/site-packages/qiskit/aqua/operators/list_ops/list_op.py in primitive_strings(self)
126
127     def primitive_strings(self) -> Set[str]:
--> 128         return reduce(set.union, [op.primitive_strings() for op in self.oplist])
129
130     @property

~/masterarbeit/code/venv/lib/python3.6/site-packages/qiskit/aqua/operators/list_ops/list_op.py in <listcomp>(.0)
126
127     def primitive_strings(self) -> Set[str]:
--> 128         return reduce(set.union, [op.primitive_strings() for op in self.oplist])
129
130     @property

AttributeError: 'Operator' object has no attribute 'primitive_strings'


How do I resolve the error? This is my first project with qiskit and I am not sure where to start.

• You didn't define tsp anywhere in your code. Could you also post complete error message? Commented Oct 2, 2020 at 13:32
• I updated the post as requested
– D.D.
Commented Oct 2, 2020 at 14:08

Operator is part of Qiskit-Terra's module quantum-info. You should use PrimitiveOp from Qiskit-Aqua.

from qiskit.aqua.operators import PrimitiveOp

def pauli(pos, num_qubits, label):
label = 'I'*(pos) + label + 'I'*(num_qubits-pos-1)
assert(len(label) == num_qubits)
return PrimitiveOp(Pauli(label=label))


Note however that for composing an Operator in Aqua with another you should use @ and * for multiplying with a scalar.

Edit: Trying to explain the new error you are getting, i think the following is happening: At some point, the QAOA algorithm tries to build the parameterized circuit corresponding to your variatonal form as specified by the cost Hamiltonian and the mixer operator. However, the mixer is composite (defined with sums and compositions) and tries to reduce it. This causes the coeff (all Operators in Aqua have a coefficient) field to be a complex number. But then, since multiplication of a Parameter with a complex is not supported, an error is raising.

A simpler example may be helpful in order to see what's going wrong. Let's say, we want to build a parameterized circuit corresponding to the evolution of $$X \otimes X$$ operator (i.e $$e^{-i \beta (X \otimes X)}$$). We will try two different ways. The second one will fail.

from qiskit.circuit import Parameter
from qiskit.aqua.operators import X, I, EvolutionFactory, EvolvedOp

def evolve(H):
beta = Parameter('β')

evolution = EvolutionFactory.build(operator=beta * H)
eop = EvolvedOp(beta * H)

return evolution.convert(eop)

H = X ^ X
print(H.coeff)
print(type(H.coeff))
evolve(H)
---
1.0
float
Works

H = (X ^ I) @ (I ^ X)
print(H.coeff)
print(type(H.coeff))
evolve(H)
---
1+0j
complex
TypeError: unsupported operand type(s) for *: 'complex' and 'Parameter'


Putting all this aside, as a workaround you could do the calculations on eq.58 (as already briefly explained on the orginal paper) and after eliminating some terms you get: \begin{align*} \frac{1}{2}H_{i, u, v} &= X_{u, i} X_{v, i + 1} X_{u, i + 1} X_{v, i} \\ & - X_{u, i} X_{v, i + 1} Y_{u, i + 1} Y_{v, i} \\ & + X_{u, i} Y_{v, i + 1} X_{u, i + 1} Y_{v, i} \\ & + X_{u, i} Y_{v, i + 1} Y_{u, i + 1} X_{v, i} \\ & + Y_{u, i} X_{v, i + 1} X_{u, i + 1} Y_{v, i} \\ & + Y_{u, i} X_{v, i + 1} Y_{u, i + 1} X_{v, i} \\ & - Y_{u, i} Y_{v, i + 1} X_{u, i + 1} X_{v, i} \\ & + Y_{u, i} Y_{v, i + 1} Y_{u, i + 1} Y_{v, i} \end{align*} and implement this operator as your mixer (sum of 8 PauliOps).

For convenience, here is my implementation.

from itertools import combinations

def mixer_operators(n):

mixer = []
n_qubits = pow(n, 2)

for i in range(n - 1):
for u, v in combinations(range(n), 2):

qu = i * n + u
qv = i * n + v

x = [0] * n_qubits
x[qu] = x[qv] = x[qu + n] = x[qv + n] = 1

Hi = 0

# XXXX term
z = [0] * n_qubits
pauli = Pauli(z, x)
pauli = PrimitiveOp(pauli)

Hi += pauli

# YYYY term
z = [0] * n_qubits
z[qu] = z[qv] = z[qu + n] = z[qv + n] = 1
pauli = Pauli(z, x)
pauli = PrimitiveOp(pauli)

Hi += pauli

# XXYY and similar terms (two Ys)
for q0, q1 in combinations([qu, qv, qu + n, qv + n], 2):
z = [0] * n_qubits
z[q0] = z[q1] = 1
pauli = Pauli(z, x)

coeff = 1
if (q0, q1) in [(qu, qv), (qu + n, qv + n)]:
coeff = -1
pauli = PrimitiveOp(pauli, coeff)

Hi += pauli

mixer.append(2 * Hi)

return SummedOp(mixer)

• Thank you, changing the code according to your solution worked, however I run into the next error I dont understand. When running result = qaoa.run(quantum_instance) I get TypeError: unsupported operand type(s) for *: 'complex' and 'ParameterExpression'
– D.D.
Commented Oct 2, 2020 at 15:10
• This error usually comes from an invalid mixer. Check e.g if your exponents are all imaginary. Commented Oct 3, 2020 at 17:12
• @NiklasPirnay In an effort to explain and overcome the new error, i edited my answer. Please, check if it's helpful. Commented Oct 4, 2020 at 11:22
• @Cryoris Your opinion would be valuable here. Maybe Aqua needs some improvement on this? Commented Oct 4, 2020 at 11:24