Skip to content

Commit

Permalink
Define custom json encoder for qobj payloads
Browse files Browse the repository at this point in the history
The qobj payload has a couple of quirks related to how qobj objects can
be constructed (and will be constructed after Qiskit/qiskit#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 Qiskit#553
  • Loading branch information
mtreinish committed Feb 19, 2020
1 parent 4fa201b commit 45adb4f
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 2 deletions.
6 changes: 5 additions & 1 deletion qiskit/providers/ibmq/api/rest/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
"""Job REST adapter for the IBM Q Experience API."""

import pprint
import json
from json.decoder import JSONDecodeError

from typing import Dict, Any
from marshmallow.exceptions import ValidationError

from qiskit.providers.ibmq.utils import json_encoder

from .base import RestAdapterBase
from .validation import StatusResponseSchema
from ..session import RetrySession
Expand Down Expand Up @@ -137,7 +140,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]:
Expand Down
5 changes: 4 additions & 1 deletion qiskit/providers/ibmq/api/rest/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

from typing import Dict, List, Optional, Any

from qiskit.providers.ibmq.utils import json_encoder

from .base import RestAdapterBase
from .backend import Backend
from .job import Job
Expand Down Expand Up @@ -140,7 +142,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,
Expand Down
33 changes: 33 additions & 0 deletions qiskit/providers/ibmq/utils/json_encoder.py
Original file line number Diff line number Diff line change
@@ -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) -> 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)

0 comments on commit 45adb4f

Please sign in to comment.