Qiskit / qiskit-ibm-runtime

IBM Client for Qiskit Runtime
https://docs.quantum.ibm.com/api/qiskit-ibm-runtime
Apache License 2.0
149 stars 154 forks source link

Error when calling job.result() in programs with StatePreparation class #392

Closed lipinor closed 1 year ago

lipinor commented 2 years ago

Describe the bug

When using the StatePreparation class to initialize a circuit in a runtime program, an error is raised when trying to run the program in the cloud. As an example, I will use this simple program, that just generates and returns a quantum circuit with StatePreparation:

import qiskit
from qiskit.circuit.library import StatePreparation 
from qiskit.circuit import QuantumCircuit

def prep_circ(num_qubits, init_state):

    qc = QuantumCircuit(num_qubits)
    qc.append(StatePreparation(init_state), qc.qubits)

    return qc

def main(
    backend,
    user_messenger,
    **kwargs
):

    circuit = prep_circ(**kwargs)

    return circuit

After saving it in a file called 'stateprep_runtime.py' and uploading it to the cloud, an error related to the StatePreparation class ir raised after calling job.result() in order to get the results:

from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()

meta = {
    "name": "state-preparation",
    "description": "A StatePreparation program.",
    "max_execution_time": 100000,
    "spec": {},
    }

program_id = service.upload_program(data="stateprep_runtime.py", metadata=meta)
prog = service.program(program_id)

options = {"backend_name": "ibmq_qasm_simulator"}

inputs = {}

inputs["num_qubits"] = num_qubits
inputs["init_state"] = init_state

job = service.run(program_id, options=options, inputs=inputs)

result = job.result()

result.draw('mpl')

The error is this one:


QiskitError Traceback (most recent call last) /tmp/ipykernel_521180/2864406014.py in 22 job = service.run(program_id, options=options, inputs=inputs) 23 ---> 24 result = job.result() 25 26 result.draw('mpl')

~/cbpf/projects/quantum-battery/vqt-env/lib/python3.9/site-packages/qiskit_ibm_runtime/runtime_job.py in result(self, timeout, decoder) 184 ) 185 result_raw = self._api_client.job_results(job_id=self.job_id) --> 186 self._results = _decoder.decode(result_raw) if result_raw else None 187 return self._results 188

~/cbpf/projects/quantum-battery/vqt-env/lib/python3.9/site-packages/qiskit_ibm_runtime/program/result_decoder.py in decode(cls, data) 49 """ 50 try: ---> 51 return json.loads(data, cls=RuntimeDecoder) 52 except json.JSONDecodeError: 53 return data

~/anaconda3/lib/python3.9/json/init.py in loads(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, kw) 357 if parse_constant is not None: 358 kw['parse_constant'] = parse_constant --> 359 return cls(kw).decode(s)

~/anaconda3/lib/python3.9/json/decoder.py in decode(self, s, _w) 335 336 """ --> 337 obj, end = self.raw_decode(s, idx=_w(s, 0).end()) 338 end = _w(s, end).end() 339 if end != len(s):

~/anaconda3/lib/python3.9/json/decoder.py in raw_decode(self, s, idx) 351 """ 352 try: --> 353 obj, end = self.scan_once(s, idx) 354 except StopIteration as err: 355 raise JSONDecodeError("Expecting value", s, err.value) from None

~/cbpf/projects/quantum-battery/vqt-env/lib/python3.9/site-packages/qiskit_ibm_runtime/utils/json.py in object_hook(self, obj) 290 return set(obj_val) 291 if obj_type == "QuantumCircuit": --> 292 return _decode_and_deserialize(obj_val, load)[0] 293 if obj_type == "Parameter": 294 return _decode_and_deserialize(obj_val, _read_parameter, False)

~/cbpf/projects/quantum-battery/vqt-env/lib/python3.9/site-packages/qiskit_ibm_runtime/utils/json.py in _decode_and_deserialize(data, deserializer, decompress) 120 buff.write(decoded) 121 buff.seek(0) --> 122 return deserializer(buff) 123 124

~/cbpf/projects/quantum-battery/vqt-env/lib/python3.9/site-packages/qiskit_ibm_runtime/qpy/interface.py in load(file_obj, metadatadeserializer) 168 for in range(data.num_circuits): 169 circuits.append( --> 170 binary_io.read_circuit( 171 file_obj, data.qpy_version, metadata_deserializer=metadata_deserializer 172 )

~/cbpf/projects/quantum-battery/vqt-env/lib/python3.9/site-packages/qiskit_ibm_runtime/qpy/binary_io/circuits.py in read_circuit(file_obj, version, metadata_deserializer) 815 custom_instructions = _read_custom_instructions(file_obj, version, vectors) 816 for _instruction in range(num_instructions): --> 817 _read_instruction( 818 file_obj, circ, out_registers, custom_instructions, version, vectors 819 )

~/cbpf/projects/quantum-battery/vqt-env/lib/python3.9/site-packages/qiskit_ibm_runtime/qpy/binary_io/circuits.py in _read_instruction(file_obj, circuit, registers, custom_instructions, version, vectors) 262 elif gate_name in {"BreakLoopOp", "ContinueLoopOp"}: 263 params = [len(qargs), len(cargs)] --> 264 gate = gate_class(*params) 265 gate.condition = condition_tuple 266 if instruction.label_size > 0:

~/cbpf/projects/quantum-battery/vqt-env/lib/python3.9/site-packages/qiskit/circuit/library/data_preparation/state_preparation.py in init(self, params, num_qubits, inverse, label) 90 91 if not isinstance(params, int) and num_qubits is not None: ---> 92 raise QiskitError( 93 "The num_qubits parameter to StatePreparation should only be" 94 " used when params is an integer"

QiskitError: 'The num_qubits parameter to StatePreparation should only be used when params is an integer'


I have noticed that the error does not happen if, before returning the circuit in the prep_circ function, the .decompose() method is called, like this:

def prep_circ(num_qubits, init_state):

    qc = QuantumCircuit(num_qubits)
    qc.append(StatePreparation(init_state), qc.qubits)
    qc = qc.decompose()

    return qc

The problem does not happen when running locally, like this:

import json
from qiskit_ibm_runtime.program import UserMessenger
from qiskit_ibm_runtime import RuntimeEncoder, RuntimeDecoder
import stateprep_runtime

user_messenger = UserMessenger()

backend = Aer.get_backend('qasm_simulator')

inputs = {}

inputs["num_qubits"] = 2
inputs["init_state"] = '01'

serialized_inputs = json.dumps(inputs, cls=RuntimeEncoder)
deserialized_inputs = json.loads(serialized_inputs, cls=RuntimeDecoder)

result_test = stateprep_runtime.main(backend, user_messenger, **deserialized_inputs)
print(result_test)

Steps to reproduce

Just follow the codes provided above.

Expected behavior

It should be able to return a quantum circuit initialized with the StatePreparation class.

Suggested solutions

I guess that maybe the problem is related to the process of uploading the program to the cloud.

Calling the decompose() method on the quantum circuit generated with StatePreparation solves the problem, but I think it should be able to run even without this step.

Additional Information

rathishcholarajan commented 2 years ago

@mtreinish Is this a qpy issue? Should we move this to terra?

lipinor commented 2 years ago

Hello @rathishcholarajan . I've posted this issue here because it only occurs when using runtime. Also, when running the runtime program locally, without uploading it, it finishes with no error.

rathishcholarajan commented 2 years ago

Hi @lipinor, Got it. Looks like the qpy serializer which we fork from terra is throwing an error and I need @mtreinish's advice here on where the right fix should be. If we determine the fix could be in terra, we will move it there.

lipinor commented 2 years ago

That's nice. Thank you for the answer!

rathishcholarajan commented 2 years ago

@mtreinish Any updates on this?

kt474 commented 1 year ago

Closing for now as this issue is no longer relevant