From 9d845d7f24f54c7822d79616bc1bc1b5c71c7e2a Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 19 Feb 2020 17:13:30 -0500 Subject: [PATCH] Define custom json encoder for qobj payloads The qobj payload has a couple of quirks related to how qobj objects can be constructed (and will be constructed after Qiskit/qiskit-terra#3383 merges) that aren't handled by the default json encoder. The first is that complex numbers are converted to the form "(real, imaginary)", and the second is that numpy arrays are sometimes part of the payload. Instead of relying on a layer outside of the provider coercing these objects to the expected format this commit changes how we push qobj payloads to the iqx api so that we run it through a json encoder that understands how to do this. After this commit qiskit-ibmq-provider owns it's own payload format and the generation of the expected json payload format instead of relying on an external library to handle it for us. Related to #553 --- qiskit/providers/ibmq/api/rest/job.py | 4 ++- qiskit/providers/ibmq/api/rest/root.py | 4 ++- qiskit/providers/ibmq/utils/json_encoder.py | 33 +++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 qiskit/providers/ibmq/utils/json_encoder.py diff --git a/qiskit/providers/ibmq/api/rest/job.py b/qiskit/providers/ibmq/api/rest/job.py index bb2d7fef9..b30a59b74 100644 --- a/qiskit/providers/ibmq/api/rest/job.py +++ b/qiskit/providers/ibmq/api/rest/job.py @@ -24,6 +24,7 @@ from .validation import StatusResponseSchema from ..session import RetrySession from ..exceptions import ApiIBMQProtocolError +from qiskit.providers.ibmq.utils import json_encoder class Job(RestAdapterBase): @@ -137,7 +138,8 @@ def put_object_storage(self, url: str, qobj_dict: Dict[str, Any]) -> str: Returns: text response, that will be empty if the request was successful. """ - response = self.session.put(url, json=qobj_dict, bare=True) + data = json.dumps(qobj_dict, cls=json_encoder.IQXJsonEconder) + response = self.session.put(url, data=data, bare=True) return response.text def get_object_storage(self, url: str) -> Dict[str, Any]: diff --git a/qiskit/providers/ibmq/api/rest/root.py b/qiskit/providers/ibmq/api/rest/root.py index e3e352ba2..8428e0b40 100644 --- a/qiskit/providers/ibmq/api/rest/root.py +++ b/qiskit/providers/ibmq/api/rest/root.py @@ -21,6 +21,7 @@ from .base import RestAdapterBase from .backend import Backend from .job import Job +from qiskit.providers.ibmq.utils import json_encoder class Api(RestAdapterBase): @@ -140,7 +141,8 @@ def job_submit( if job_tags: payload['tags'] = job_tags - return self.session.post(url, json=payload).json() + data = json.dumps(payload, cls=json_encoder.IQXJsonEconder) + return self.session.post(url, data=data).json() def submit_job_object_storage( self, diff --git a/qiskit/providers/ibmq/utils/json_encoder.py b/qiskit/providers/ibmq/utils/json_encoder.py new file mode 100644 index 000000000..8489a69ef --- /dev/null +++ b/qiskit/providers/ibmq/utils/json_encoder.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +# pylint: disable=method-hidden + +"""Custom JSON encoders.""" + +import json +from typing import Any + + +class IQXJsonEconder(json.JSONEncoder): + """A json encoder for qobj""" + + def default(self, o: Any): + # Convert numpy arrays: + if hasattr(o, 'tolist'): + return o.tolist() + # Use Qobj complex json format: + if isinstance(o, complex): + return (o.real, o.imag) + return json.JSONEncoder.default(self, o)