diff --git a/qiskit/providers/ibmq/utils/json_encoder.py b/qiskit/providers/ibmq/utils/json_encoder.py index f8c518e62..553dd6fab 100644 --- a/qiskit/providers/ibmq/utils/json_encoder.py +++ b/qiskit/providers/ibmq/utils/json_encoder.py @@ -23,6 +23,33 @@ class IQXJsonEncoder(json.JSONEncoder): """A json encoder for qobj""" + def __encode(self, param: Any) -> Any: + """ + Convert dictionary to contain only JSON serializable types. For example, + if the key is a Parameter we convert it to a string. + """ + if isinstance(param, dict): + param_bind_str = {} + for key in param.keys(): + value = self.__encode(param[key]) + + if isinstance(key, (bool, float, int, str)) or key is None: + param_bind_str[key] = value + else: + param_bind_str[str(key)] = value + return param_bind_str + elif isinstance(param, list): + return [self.__encode(p) for p in param] + else: + return param + + def encode(self, o: Any) -> str: + """ + Return a JSON string representation of a Python data structure. + """ + new_o = self.__encode(o) + return super().encode(new_o) + def default(self, o: Any) -> Any: # Convert numpy arrays: if hasattr(o, 'tolist'): diff --git a/test/ibmq/test_serialization.py b/test/ibmq/test_serialization.py index 85bd06729..e88d2dcb2 100644 --- a/test/ibmq/test_serialization.py +++ b/test/ibmq/test_serialization.py @@ -15,11 +15,13 @@ from unittest import SkipTest, skipIf from typing import Any, Dict, Optional +import json import dateutil.parser from qiskit.test.reference_circuits import ReferenceCircuits from qiskit.test import slow_test from qiskit.providers.ibmq import least_busy -from qiskit import transpile, schedule, QuantumCircuit +from qiskit import transpile, schedule, assemble +from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister from qiskit.providers.ibmq.utils.json_encoder import IQXJsonEncoder from qiskit.circuit import Parameter from qiskit.version import VERSION as terra_version @@ -178,6 +180,43 @@ def test_convert_complex(self): self.assertEqual(val[0], 0.2) self.assertEqual(val[1], 0.1) + def test_exception_message(self): + """Test executing job with Parameter in methadata.""" + qr = QuantumRegister(1) + cr = ClassicalRegister(1) + my_circ_str = 'test_metadata' + my_circ = QuantumCircuit(qr, cr, name=my_circ_str, metadata={Parameter('φ'): 0.2}) + qobj = assemble(my_circ) + qobj_dict = qobj.to_dict() + json.dumps(qobj_dict, cls=IQXJsonEncoder) + # There is no self.assert method because if we cannot pass Parameter as metadata + # the last line throw: + # "TypeError: keys must be str, int, float, bool or None, not Parameter" + + def test_encode_no_replace(self): + """Test encode where there is no invalid key to replace.""" + test_dir = { + 't1': 1, + None: None, + 'list': [1, 2, {'ld': 1, 2: 3}] + } + + self.assertEqual('{"t1": 1, "null": null, "list": [1, 2, {"ld": 1, "2": 3}]}', + IQXJsonEncoder().encode(test_dir)) + + def test_encode_replace(self): + """Test encode where there is no invalid key to replace.""" + test_dir = { + 't1': 1, + None: None, + Parameter('a'): 0.2, + 'list': [1, 2, {'ld': 1, 2: 3, Parameter('alfa'): 0.1}] + } + + self.assertEqual( + '{"t1": 1, "null": null, "a": 0.2, "list": [1, 2, {"ld": 1, "2": 3, "alfa": 0.1}]}', + IQXJsonEncoder().encode(test_dir)) + def _find_potential_encoded(data: Any, c_key: str, tally: set) -> None: """Find data that may be in JSON serialized format.