Running QAOA with Qiskit Runtime

Hi!

I am trying to use Qiskit Runtime to run a PennyLane implementation of QAOA. I am following the example for VQE (https://pennylaneqiskit.readthedocs.io/en/latest/devices/runtime.html) and I am trying to adapt it to run QAOA. The example for VQE run smoothly, however, I am obtaining an error with my QAOA adaptation.

My code is the following:

import pennylane as qml
from pennylane import qaoa
from pennylane_qiskit import upload_vqe_runner, vqe_runner


H1 = 1*qml.PauliZ(0) @ qml.PauliZ(1)

H0 = qml.PauliX(0) + qml.PauliX(1) 

wires = range(2)

def cost_function(params):
    for w in wires:
        qml.Hadamard(wires=w)
    qaoa.cost_layer(params[0], H1)
    qaoa.mixer_layer(params[1], H0)
    
shots = 1024

program_id = upload_vqe_runner(hub="ibm-q", group="open", project="main")

job = vqe_runner(
    program_id=program_id,
    backend="ibmq_qasm_simulator",
    hamiltonian=H1,
    ansatz=cost_function,
    x0=[0.1, 0.0],
    shots=shots,
    optimizer="SPSA",
    optimizer_config={"maxiter": 40},
    kwargs={"hub": "ibm-q", "group": "open", "project": "main"},
)

And the error that I obtain is

/Users/elias/opt/anaconda3/lib/python3.8/site-packages/pennylane_qiskit/vqe_runtime_runner.py:362: UserWarning: In order to match the tape expansion, the number of parameters has been changed.
  warnings.warn(
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-3a187e27d882> in <module>
     26 print(program_id)
     27 
---> 28 job = vqe_runner(
     29     program_id=program_id,
     30     backend="ibmq_qasm_simulator",

~/opt/anaconda3/lib/python3.8/site-packages/pennylane_qiskit/vqe_runtime_runner.py in vqe_runner(backend, hamiltonian, x0, program_id, ansatz, ansatz_config, optimizer, optimizer_config, shots, use_measurement_mitigation, **kwargs)
    308     # Callbacks functions are different between optimizers.
    309     if optimizer in ["SPSA", "QNSPSA"]:
--> 310         job = provider.runtime.run(
    311             program_id, options=options, inputs=inputs, callback=rt_job._callback
    312         )

~/opt/anaconda3/lib/python3.8/site-packages/qiskit/providers/ibmq/runtime/ibm_runtime_service.py in run(self, program_id, options, inputs, callback, result_decoder, image)
    285         backend_name = options.backend_name
    286         result_decoder = result_decoder or ResultDecoder
--> 287         response = self._api_client.program_run(program_id=program_id,
    288                                                 credentials=self._provider.credentials,
    289                                                 backend_name=backend_name,

~/opt/anaconda3/lib/python3.8/site-packages/qiskit/providers/ibmq/api/clients/runtime.py in program_run(self, program_id, credentials, backend_name, params, image, log_level)
    128             JSON response.
    129         """
--> 130         return self.api.program_run(program_id=program_id, hub=credentials.hub,
    131                                     group=credentials.group, project=credentials.project,
    132                                     backend_name=backend_name, params=params,

~/opt/anaconda3/lib/python3.8/site-packages/qiskit/providers/ibmq/api/rest/runtime.py in program_run(self, program_id, hub, group, project, backend_name, params, image, log_level)
    145         if log_level:
    146             payload["log_level"] = log_level
--> 147         data = json.dumps(payload, cls=RuntimeEncoder)
    148         return self.session.post(url, data=data).json()
    149 

~/opt/anaconda3/lib/python3.8/json/__init__.py in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
    232     if cls is None:
    233         cls = JSONEncoder
--> 234     return cls(
    235         skipkeys=skipkeys, ensure_ascii=ensure_ascii,
    236         check_circular=check_circular, allow_nan=allow_nan, indent=indent,

~/opt/anaconda3/lib/python3.8/json/encoder.py in encode(self, o)
    197         # exceptions aren't as detailed.  The list call should be roughly
    198         # equivalent to the PySequence_Fast that ''.join() would do.
--> 199         chunks = self.iterencode(o, _one_shot=True)
    200         if not isinstance(chunks, (list, tuple)):
    201             chunks = list(chunks)

~/opt/anaconda3/lib/python3.8/json/encoder.py in iterencode(self, o, _one_shot)
    255                 self.key_separator, self.item_separator, self.sort_keys,
    256                 self.skipkeys, _one_shot)
--> 257         return _iterencode(o, 0)
    258 
    259 def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,

~/opt/anaconda3/lib/python3.8/site-packages/qiskit/providers/ibmq/runtime/utils.py in default(self, obj)
    229             value = _serialize_and_encode(obj, scipy.sparse.save_npz, compress=False)
    230             return {'__type__': 'spmatrix', '__value__': value}
--> 231         return super().default(obj)
    232 
    233 

~/opt/anaconda3/lib/python3.8/json/encoder.py in default(self, o)
    177 
    178         """
--> 179         raise TypeError(f'Object of type {o.__class__.__name__} '
    180                         f'is not JSON serializable')
    181 

TypeError: Object of type int64 is not JSON serializable

Any help would be greatly appreciated!!!

Thanks in advance

Hi @combarro,

If you change your H1 to H1 = qml.Hamiltonian([1,1],[qml.PauliZ(0), qml.PauliZ(1)]) then you shouldn’t get the error anymore.

Please let me know if this works for you!

Thanks so much. What I need is Z0Z1, not Z0 + Z1, so I have used

H1 = qml.Hamiltonian([1],[qml.PauliZ(0)@ qml.PauliZ(1)])

With this, not my code works, thanks!

However, I am not sure why H1 = 1*qml.PauliZ(0) @ qml.PauliZ(1) did not work. When I print qml.Hamiltonian([1],[qml.PauliZ(0)@ qml.PauliZ(1)]) and 1*qml.PauliZ(0) @ qml.PauliZ(1) they both give me

 (1) [Z0 Z1]

and their type is <class 'pennylane.ops.qubit.hamiltonian.Hamiltonian'>

I am very confused about the difference between them. I would really appreciate it if you could explain why they are different and which case I should one or the other.

Thanks!

Hey @combarro! What happens if you try the following:

H1 = 1.0 * qml.PauliZ(0) @ qml.PauliZ(1)

Note here that it is 1.0 (a float) rather than 1 (an integer).

Yeah, I tried that yesterday and it works. Don’t know why or what is the difference with H1 = 1.0 * qml.PauliZ(0) @ qml.PauliZ(1) or with H1 = qml.Hamiltonian([1],[qml.PauliZ(0)@ qml.PauliZ(1)])

Hi @combarro,

qml.Hamiltonian() creates an operator representing a Hamiltonian. It allows you to specify many additional parameters as you can see in the docs.

H1 = 1.0 * qml.PauliZ(0) @ qml.PauliZ(1) results in the same output, but it doesn’t let you choose options as with qml.Hamiltonian(). In both cases if you print type(H1) you will get pennylane.ops.qubit.hamiltonian.Hamiltonian.

For your particular case both options work.

I see, thank you. And what is the difference with H1 = 1 * qml.PauliZ(0) @ qml.PauliZ(1)?

Hi @combarro, the difference is that the ‘1’ in this case is of type int64. If you do H1 = float(1) * qml.PauliZ(0) @ qml.PauliZ(1) you’ll see that it works. The problem is that this number needs to be JSON serializable and int64 is not.

Thanks! And what are other situations in which H1 = 1 * qml.PauliZ(0) @ qml.PauliZ(1) will not work? It seems a little bit counterintuitive to me, since I would think it should be equivalent to H1 = 1.0 * qml.PauliZ(0) @ qml.PauliZ(1). Thanks once again!

Hi @combarro, it’s hard to say if there are other similar situations. Both pieces of code are in theory equivalent but, as with many software-related problems, even the slightest difference (having an int vs a float) can make your program fail or not.

We will be here to help you though in case you have any trouble in the future!

Thanks, I really appreciate it!