0
$\begingroup$

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.

$\endgroup$
  • $\begingroup$ You didn't define tsp anywhere in your code. Could you also post complete error message? $\endgroup$ – user9318 Oct 2 at 13:32
  • $\begingroup$ I updated the post as requested $\endgroup$ – Niklas Pirnay Oct 2 at 14:08
1
$\begingroup$

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)
| improve this answer | |
$\endgroup$
  • $\begingroup$ 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' $\endgroup$ – Niklas Pirnay Oct 2 at 15:10
  • $\begingroup$ This error usually comes from an invalid mixer. Check e.g if your exponents are all imaginary. $\endgroup$ – Cryoris Oct 3 at 17:12
  • $\begingroup$ @NiklasPirnay In an effort to explain and overcome the new error, i edited my answer. Please, check if it's helpful. $\endgroup$ – tsgeorgios Oct 4 at 11:22
  • $\begingroup$ @Cryoris Your opinion would be valuable here. Maybe Aqua needs some improvement on this? $\endgroup$ – tsgeorgios Oct 4 at 11:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.