From 2ea7d176eae33a5ff8385fd309fe1e74a761f1ac Mon Sep 17 00:00:00 2001 From: Jay Gambetta Date: Thu, 3 Jan 2019 08:54:25 -0500 Subject: [PATCH] Circuits to qobj (#1629) * Removing qobj dependencies in qobjconfig * Adding to rebase * Removing unrolls * Linting * Fixing last test * Updating the schema * Linting that makes no sense * Diego updates * Linting * Update qiskit/tools/compiler.py Co-Authored-By: jaygambetta * Update qiskit/qobj/run_config.py Co-Authored-By: jaygambetta * update warning messages. remove init from RunConfig as all optional * only put optional shots, max_credits, memory in qobj if they are set --- CHANGELOG.rst | 8 + qiskit/converters/circuits_to_qobj.py | 194 +++++++++------ qiskit/converters/qobj_to_circuits.py | 34 ++- .../builtinsimulators/qasm_simulator.py | 5 +- qiskit/qobj/__init__.py | 5 +- qiskit/qobj/_converter.py | 4 +- qiskit/qobj/{_qobj.py => qobj.py} | 72 ++++-- qiskit/qobj/run_config.py | 40 +++ qiskit/schemas/qobj_schema.json | 3 - qiskit/tools/compiler.py | 51 ++-- qiskit/unroll.py | 14 -- qiskit/unrollers/__init__.py | 0 qiskit/unrollers/_dagunroller.py | 90 ------- qiskit/unrollers/_jsonbackend.py | 235 ------------------ qiskit/unrollers/_unrollerbackend.py | 153 ------------ qiskit/unrollers/exceptions.py | 39 --- .../converters/test_qobj_to_circuits.py | 2 + test/python/qobj/test_qobj.py | 16 +- test/python/test_unroller.py | 59 ----- test/python/tools/test_compiler.py | 9 +- test/python/transpiler/test_transpile.py | 7 +- 21 files changed, 283 insertions(+), 757 deletions(-) rename qiskit/qobj/{_qobj.py => qobj.py} (86%) create mode 100644 qiskit/qobj/run_config.py delete mode 100644 qiskit/unroll.py delete mode 100644 qiskit/unrollers/__init__.py delete mode 100644 qiskit/unrollers/_dagunroller.py delete mode 100644 qiskit/unrollers/_jsonbackend.py delete mode 100644 qiskit/unrollers/_unrollerbackend.py delete mode 100644 qiskit/unrollers/exceptions.py delete mode 100644 test/python/test_unroller.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1fd52c1024..6c062b80b7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -19,6 +19,11 @@ The format is based on `Keep a Changelog`_. `UNRELEASED`_ ============= +Added +----- + +- Added a RunConfig object for configurations for run configurations to be used + in compile and circuits_to_qobj. (#1629) Changed ------- @@ -30,6 +35,8 @@ Changed - The snapshot instruction now takes ``label`` and ``snap_type`` instead of ``slot`` (#1615). - The test folders have been reorganized to match the python modules (#1625) +- The circuits_to_qobj no longers uses the unrollers (#1629) + Fixed ----- @@ -46,6 +53,7 @@ Removed - Removed simulator instructions ``save``, ``load``, ``wait``, ``noise`` as unsupported in Aer (#1615). - Removed circuit.add as deprecated (#1627) +- Removed the unroller (#1629) `0.7.0`_ - 2018-12-19 ===================== diff --git a/qiskit/converters/circuits_to_qobj.py b/qiskit/converters/circuits_to_qobj.py index 1039483bd1..f1216291b6 100644 --- a/qiskit/converters/circuits_to_qobj.py +++ b/qiskit/converters/circuits_to_qobj.py @@ -5,104 +5,148 @@ # This source code is licensed under the Apache License, Version 2.0 found in # the LICENSE.txt file in the root directory of this source tree. -"""Helper function for converting a list of circuits to a qobj""" -from copy import deepcopy +"""Compile function for converting a list of circuits to the qobj""" import uuid +import warnings -from qiskit.qobj import Qobj, QobjConfig, QobjExperiment, QobjItem, QobjHeader from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.qobj import Qobj, QobjConfig, QobjExperiment, QobjInstruction, QobjHeader +from qiskit.qobj import QobjExperimentConfig, QobjExperimentHeader, QobjConditional +from qiskit.qobj.run_config import RunConfig -def circuits_to_qobj(circuits, backend_name, config=None, shots=1024, - max_credits=10, qobj_id=None, basis_gates=None, coupling_map=None, - seed=None, memory=False): +def circuits_to_qobj(circuits, user_qobj_header=None, run_config=None, + qobj_id=None, backend_name=None, + config=None, shots=None, max_credits=None, + basis_gates=None, + coupling_map=None, seed=None, memory=None): """Convert a list of circuits into a qobj. Args: circuits (list[QuantumCircuits] or QuantumCircuit): circuits to compile - backend_name (str): name of runner backend - config (dict): dictionary of parameters (e.g. noise) used by runner - shots (int): number of repetitions of each circuit, for sampling - max_credits (int): maximum credits to use + user_qobj_header (QobjHeader): header to pass to the results + run_config (RunConfig): RunConfig object qobj_id (int): identifier for the generated qobj - basis_gates (list[str])): basis gates for the experiment - coupling_map (list): coupling map (perhaps custom) to target in mapping - seed (int): random seed for simulators - memory (bool): if True, per-shot measurement bitstrings are returned as well + + backend_name (str): TODO: delete after qiskit-terra 0.8 + config (dict): TODO: delete after qiskit-terra 0.8 + shots (int): TODO: delete after qiskit-terra 0.8 + max_credits (int): TODO: delete after qiskit-terra 0.8 + basis_gates (str): TODO: delete after qiskit-terra 0.8 + coupling_map (list): TODO: delete after qiskit-terra 0.8 + seed (int): TODO: delete after qiskit-terra 0.8 + memory (bool): TODO: delete after qiskit-terra 0.8 Returns: Qobj: the Qobj to be run on the backends """ - # TODO: the following will be removed from qobj and thus removed here: - # `basis_gates`, `coupling_map` - - # Step 1: create the Qobj, with empty experiments. - # Copy the configuration: the values in `config` have preference - qobj_config = deepcopy(config or {}) - qobj_config.update({'shots': shots, - 'max_credits': max_credits, - 'memory_slots': 0, - 'memory': memory}) - - qobj = Qobj(qobj_id=qobj_id or str(uuid.uuid4()), - config=QobjConfig(**qobj_config), - experiments=[], - header=QobjHeader(backend_name=backend_name)) - if seed: - qobj.config.seed = seed - + user_qobj_header = user_qobj_header or QobjHeader() + run_config = run_config or RunConfig() if isinstance(circuits, QuantumCircuit): circuits = [circuits] + if backend_name: + warnings.warn('backend_name is not required anymore', DeprecationWarning) + user_qobj_header.backend_name = backend_name + if config: + warnings.warn('config is not used anymore. Set all configs in ' + 'run_config.', DeprecationWarning) + if shots: + warnings.warn('shots is not used anymore. Set it via run_config.', DeprecationWarning) + run_config.shots = shots + if basis_gates: + warnings.warn('basis_gates was unused and will be removed.', DeprecationWarning) + if coupling_map: + warnings.warn('coupling_map was unused and will be removed.', DeprecationWarning) + if seed: + warnings.warn('seed is not used anymore. Set it via run_config', DeprecationWarning) + run_config.seed = seed + if memory: + warnings.warn('memory is not used anymore. Set it via run_config', DeprecationWarning) + run_config.memory = memory + if max_credits: + warnings.warn('max_credits is not used anymore. Set it via run_config', DeprecationWarning) + run_config.max_credits = max_credits + + userconfig = QobjConfig(**run_config.to_dict()) + experiments = [] + max_n_qubits = 0 + max_memory_slots = 0 for circuit in circuits: - qobj.experiments.append(_circuit_to_experiment(circuit, - config, - basis_gates, - coupling_map)) + # header stuff + n_qubits = 0 + memory_slots = 0 + qubit_labels = [] + clbit_labels = [] - # Update the global `memory_slots` and `n_qubits` values. - qobj.config.memory_slots = max(experiment.config.memory_slots for - experiment in qobj.experiments) + qreg_sizes = [] + creg_sizes = [] + for qreg in circuit.qregs: + qreg_sizes.append([qreg.name, qreg.size]) + for j in range(qreg.size): + qubit_labels.append([qreg.name, j]) + n_qubits += qreg.size + for creg in circuit.cregs: + creg_sizes.append([creg.name, creg.size]) + for j in range(creg.size): + clbit_labels.append([creg.name, j]) + memory_slots += creg.size - qobj.config.n_qubits = max(experiment.config.n_qubits for - experiment in qobj.experiments) + # TODO: why do we need creq_sizes and qreg_sizes in header + # TODO: we need to rethink memory_slots as they are tied to classical bit + # TODO: when no more backends use the compiled_circuit_qasm lets delete it form header + experimentheader = QobjExperimentHeader(qubit_labels=qubit_labels, + n_qubits=n_qubits, + qreg_sizes=qreg_sizes, + clbit_labels=clbit_labels, + memory_slots=memory_slots, + creg_sizes=creg_sizes, + name=circuit.name, + compiled_circuit_qasm=circuit.qasm()) + # TODO: why do we need n_qubits and memory_slots in both the header and the config + experimentconfig = QobjExperimentConfig(n_qubits=n_qubits, memory_slots=memory_slots) - return qobj + instructions = [] + for opt in circuit.data: + current_instruction = QobjInstruction(name=opt.name) + if opt.qargs: + qubit_indices = [qubit_labels.index([qubit[0].name, qubit[1]]) + for qubit in opt.qargs] + current_instruction.qubits = qubit_indices + if opt.cargs: + clbit_indices = [clbit_labels.index([clbit[0].name, clbit[1]]) + for clbit in opt.cargs] + current_instruction.memory = clbit_indices + # TODO: we are not constant with params vs param + if opt.param: + params = list(map(lambda x: x.evalf(), opt.param)) + current_instruction.params = params + # TODO: I really dont like this for snapshot. I also think we should change + # type to snap_type + if opt.name == "snapshot": + current_instruction.label = str(opt.param[0]) + current_instruction.type = str(opt.param[1]) + if opt.control: + mask = 0 + for clbit in clbit_labels: + if clbit[0] == opt.control[0].name: + mask |= (1 << clbit_labels.index(clbit)) -def _circuit_to_experiment(circuit, config=None, basis_gates=None, - coupling_map=None): - """Helper function for dags to qobj in parallel (if available). + current_instruction.conditional = QobjConditional(mask="0x%X" % mask, + type='equals', + val="0x%X" % opt.control[1]) - Args: - circuit (QuantumCircuit): QuantumCircuit to convert into qobj experiment - config (dict): dictionary of parameters (e.g. noise) used by runner - basis_gates (list[str])): basis gates for the experiment - coupling_map (list): coupling map (perhaps custom) to target in mapping + instructions.append(current_instruction) + experiments.append(QobjExperiment(instructions=instructions, header=experimentheader, + config=experimentconfig)) + if n_qubits > max_n_qubits: + max_n_qubits = n_qubits + if memory_slots > max_memory_slots: + max_memory_slots = memory_slots - Returns: - Qobj: Qobj to be run on the backends - """ - # pylint: disable=unused-argument - # TODO: if arguments are really unused, consider changing the signature - # TODO: removed the DAG from this function - from qiskit.converters import circuit_to_dag - from qiskit.unroll import DagUnroller, JsonBackend - dag = circuit_to_dag(circuit) - json_circuit = DagUnroller(dag, JsonBackend(dag.basis)).execute() - # Step 3a: create the Experiment based on json_circuit - experiment = QobjExperiment.from_dict(json_circuit) - # Step 3b: populate the Experiment configuration and header - experiment.header.name = circuit.name - experiment_config = deepcopy(config or {}) - experiment_config.update({ - 'memory_slots': sum([creg.size for creg in dag.cregs.values()]), - 'n_qubits': sum([qreg.size for qreg in dag.qregs.values()]) - }) - experiment.config = QobjItem(**experiment_config) + userconfig.memory_slots = max_memory_slots + userconfig.n_qubits = max_n_qubits - # set eval_symbols=True to evaluate each symbolic expression - # TODO: after transition to qobj, we can drop this - experiment.header.compiled_circuit_qasm = circuit.qasm() - # Step 3c: add the Experiment to the Qobj - return experiment + return Qobj(qobj_id=qobj_id or str(uuid.uuid4()), config=userconfig, + experiments=experiments, header=user_qobj_header) diff --git a/qiskit/converters/qobj_to_circuits.py b/qiskit/converters/qobj_to_circuits.py index 51a5a438a4..b1660d91f3 100644 --- a/qiskit/converters/qobj_to_circuits.py +++ b/qiskit/converters/qobj_to_circuits.py @@ -12,6 +12,7 @@ from qiskit.circuit import quantumcircuit as qc from qiskit.circuit import quantumregister as qr +# TODO: This is broken for conditionals. Will fix after circuits_2_qobj pr def qobj_to_circuits(qobj): """Return a list of QuantumCircuit object(s) from a qobj @@ -42,19 +43,30 @@ def qobj_to_circuits(qobj): for i in x.instructions: instr_method = getattr(circuit, i.name) qubits = [] - for qubit in i.qubits: - qubit_label = x.header.qubit_labels[qubit] - qubits.append( - qreg_dict[qubit_label[0]][qubit_label[1]]) + try: + for qubit in i.qubits: + qubit_label = x.header.qubit_labels[qubit] + qubits.append( + qreg_dict[qubit_label[0]][qubit_label[1]]) + except Exception: # pylint: disable=broad-except + pass clbits = [] - for clbit in i.memory: - clbit_label = x.header.clbit_labels[clbit] - clbits.append( - creg_dict[clbit_label[0]][clbit_label[1]]) - if i.name in ['snapshot', 'save', 'load', 'noise']: - instr_method(*i.params) + try: + for clbit in i.memory: + clbit_label = x.header.clbit_labels[clbit] + clbits.append( + creg_dict[clbit_label[0]][clbit_label[1]]) + except Exception: # pylint: disable=broad-except + pass + params = [] + try: + params = i.params + except Exception: # pylint: disable=broad-except + pass + if i.name in ['snapshot']: + instr_method(*params) else: - instr_method(*i.params, *qubits, *clbits) + instr_method(*params, *qubits, *clbits) circuits.append(circuit) return circuits return None diff --git a/qiskit/providers/builtinsimulators/qasm_simulator.py b/qiskit/providers/builtinsimulators/qasm_simulator.py index 94777443e1..02df9c3eda 100644 --- a/qiskit/providers/builtinsimulators/qasm_simulator.py +++ b/qiskit/providers/builtinsimulators/qasm_simulator.py @@ -396,7 +396,7 @@ class QasmSimulatorPy(BaseBackend): self._validate(qobj) result_list = [] self._shots = qobj.config.shots - self._memory = qobj.config.memory + self._memory = getattr(qobj.config, 'memory', False) self._qobj_config = qobj.config start = time.time() for experiment in qobj.experiments: @@ -456,7 +456,8 @@ class QasmSimulatorPy(BaseBackend): # For compatibility on Windows force dyte to be int32 # and set the maximum value to be (2 ** 31) - 1 seed = np.random.randint(2147483647, dtype='int32') - self._local_random.seed(seed) + + self._local_random.seed(seed=seed) # Check if measure sampling is supported for current circuit self._validate_measure_sampling(experiment) diff --git a/qiskit/qobj/__init__.py b/qiskit/qobj/__init__.py index 945e4090bb..97fd819c82 100644 --- a/qiskit/qobj/__init__.py +++ b/qiskit/qobj/__init__.py @@ -7,8 +7,9 @@ """Module for the Qobj structure.""" -from ._qobj import (Qobj, QobjConfig, QobjExperiment, QobjInstruction, - QobjItem, QobjHeader, QobjExperimentHeader) +from .qobj import (Qobj, QobjConfig, QobjExperiment, QobjInstruction, QobjItem, + QobjHeader, QobjExperimentHeader, QobjConditional, QobjExperimentConfig) from ._converter import qobj_to_dict from ._validation import validate_qobj_against_schema from .exceptions import QobjValidationError +from .run_config import RunConfig diff --git a/qiskit/qobj/_converter.py b/qiskit/qobj/_converter.py index 583d715603..099dea9df2 100644 --- a/qiskit/qobj/_converter.py +++ b/qiskit/qobj/_converter.py @@ -9,8 +9,8 @@ import logging from qiskit.exceptions import QiskitError -from ._qobj import QOBJ_VERSION -from ._qobj import QobjItem +from .qobj import QOBJ_VERSION +from .qobj import QobjItem logger = logging.getLogger(__name__) diff --git a/qiskit/qobj/_qobj.py b/qiskit/qobj/qobj.py similarity index 86% rename from qiskit/qobj/_qobj.py rename to qiskit/qobj/qobj.py index 633aaa2138..937151ce6e 100644 --- a/qiskit/qobj/_qobj.py +++ b/qiskit/qobj/qobj.py @@ -138,27 +138,6 @@ class Qobj(QobjItem): super().__init__(**kwargs) -class QobjConfig(QobjItem): - """Configuration for a Qobj. - - Attributes: - shots (int): number of shots. - memory_slots (int): number of measurements slots in the classical - memory on the backend. - - Attributes defined in the schema but not required: - max_credits (int): number of credits. - seed (int): random seed. - """ - REQUIRED_ARGS = ['shots', 'memory_slots'] - - def __init__(self, shots, memory_slots, **kwargs): - self.shots = shots - self.memory_slots = memory_slots - - super().__init__(**kwargs) - - class QobjHeader(QobjItem): """Header for a Qobj. @@ -171,6 +150,21 @@ class QobjHeader(QobjItem): pass +class QobjConfig(QobjItem): + """Configuration for a Qobj. + + Attributes: + None should be required + Attributes defined in the schema but not required: + max_credits (int): number of credits. + seed (int): random seed. + memory_slots (int): number of measurements slots in the classical + memory on the backend. + shots (int): number of shots. + """ + pass + + class QobjExperiment(QobjItem): """Quantum experiment represented inside a Qobj. @@ -189,7 +183,7 @@ class QobjExperiment(QobjItem): class QobjExperimentHeader(QobjItem): - """Header for a Qobj. + """Header for a Qobj Experiment . Attributes defined in the schema but not required: name (str): experiment name. @@ -197,12 +191,23 @@ class QobjExperimentHeader(QobjItem): pass +class QobjExperimentConfig(QobjItem): + """Config for a Qobj Experiment. + + """ + pass + + class QobjInstruction(QobjItem): """Quantum Instruction. Attributes: - name(str): name of the gate. - qubits(list): list of qubits to apply to the gate. + name(str): name of the operation. + Optional Attributes: + qubits(list): list of qubits to apply to the operation. + params(list): list of params for the operation. + memory(list): list of memory to apply to the operation. + conditional(QobjConditional): conditional Qobj """ REQUIRED_ARGS = ['name'] @@ -210,3 +215,22 @@ class QobjInstruction(QobjItem): self.name = name super().__init__(**kwargs) + + +class QobjConditional(QobjItem): + """Quantum Conditional. + + Attributes: + mask (hex): mask of the conditional + type (string): type of the conditional + val (hex): value of the conditional + """ + # pylint: disable=redefined-builtin + REQUIRED_ARGS = ['mask', 'type', 'val'] + + def __init__(self, mask, type, val): + self.mask = mask + self.type = type + self.val = val + + super().__init__() diff --git a/qiskit/qobj/run_config.py b/qiskit/qobj/run_config.py new file mode 100644 index 0000000000..fa861668a7 --- /dev/null +++ b/qiskit/qobj/run_config.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018, IBM. +# +# This source code is licensed under the Apache License, Version 2.0 found in +# the LICENSE.txt file in the root directory of this source tree. + +"""Models for RunConfig and its related components.""" +from marshmallow.validate import Range + +from qiskit.validation import BaseModel, BaseSchema, bind_schema +from qiskit.validation.fields import Boolean, Integer + + +class RunConfigSchema(BaseSchema): + """Schema for RunConfig.""" + + # Required properties. + # None + + # Optional properties. + shots = Integer(validate=Range(min=1)) + max_credits = Integer(validate=Range(min=3, max=10)) # TODO: can we check the range + seed = Integer() + memory = Boolean() # set default to be False + + +@bind_schema(RunConfigSchema) +class RunConfig(BaseModel): + """Model for RunConfig. + + Please note that this class only describes the required fields. For the + full description of the model, please check ``RunConfigSchema``. + + Attributes: + shots (int): the number of shots. + max_credits (int): the max_credits to use on the IBMQ public devices. + seed (int): the seed to use in the simulator for the first experiment. + memory (bool): to use memory. + """ diff --git a/qiskit/schemas/qobj_schema.json b/qiskit/schemas/qobj_schema.json index 3aa77f7877..52f9c6c9b2 100644 --- a/qiskit/schemas/qobj_schema.json +++ b/qiskit/schemas/qobj_schema.json @@ -887,9 +887,6 @@ "type": "integer" } }, - "required": [ - "shots" - ], "title": "Qobj-level configuration", "type": "object" }, diff --git a/qiskit/tools/compiler.py b/qiskit/tools/compiler.py index 19942250e1..654f6f5a8c 100644 --- a/qiskit/tools/compiler.py +++ b/qiskit/tools/compiler.py @@ -10,9 +10,10 @@ import warnings import logging from qiskit import transpiler -from qiskit.transpiler._passmanager import PassManager from qiskit.converters import circuits_to_qobj -from qiskit.exceptions import QiskitError +from qiskit.qobj import RunConfig +from qiskit.qobj import QobjHeader + logger = logging.getLogger(__name__) @@ -20,8 +21,8 @@ logger = logging.getLogger(__name__) # pylint: disable=redefined-builtin def compile(circuits, backend, config=None, basis_gates=None, coupling_map=None, initial_layout=None, - shots=1024, max_credits=10, seed=None, qobj_id=None, - skip_transpiler=False, seed_mapper=None, pass_manager=None, memory=False): + shots=1024, max_credits=10, seed=None, qobj_id=None, seed_mapper=None, + pass_manager=None, memory=False): """Compile a list of circuits into a qobj. Args: @@ -38,7 +39,6 @@ def compile(circuits, backend, qobj_id (int): identifier for the generated qobj pass_manager (PassManager): a pass manger for the transpiler pipeline memory (bool): if True, per-shot measurement bitstrings are returned as well - skip_transpiler (bool): DEPRECATED skip transpiler and create qobj directly Returns: Qobj: the qobj to be run on the backends @@ -46,32 +46,33 @@ def compile(circuits, backend, Raises: QiskitError: if the desired options are not supported by backend """ - if skip_transpiler: # empty pass manager which does nothing - pass_manager = PassManager() - warnings.warn('The skip_transpiler option has been deprecated. ' - 'Please pass an empty PassManager() instance instead', - DeprecationWarning) - - backend_memory = getattr(backend.configuration(), 'memory', False) - if memory and not backend_memory: - raise QiskitError("Backend %s only returns total counts, not single-shot memory." % - backend.name()) + if config: + warnings.warn('The `config` argument is deprecated and ' + 'does not do anything', DeprecationWarning) circuits = transpiler.transpile(circuits, backend, basis_gates, coupling_map, initial_layout, seed_mapper, pass_manager) # step 4: Making a qobj - qobj = circuits_to_qobj(circuits, backend_name=backend.name(), - config=config, shots=shots, max_credits=max_credits, - qobj_id=qobj_id, basis_gates=basis_gates, - coupling_map=coupling_map, seed=seed, memory=memory) + run_config = RunConfig() + + if seed: + run_config.seed = seed + if shots: + run_config.shots = shots + if max_credits: + run_config.max_credits = max_credits + if memory: + run_config.memory = memory + qobj = circuits_to_qobj(circuits, user_qobj_header=QobjHeader(), run_config=run_config, + qobj_id=qobj_id) return qobj def execute(circuits, backend, config=None, basis_gates=None, coupling_map=None, initial_layout=None, shots=1024, max_credits=10, seed=None, - qobj_id=None, skip_transpiler=False, seed_mapper=None, pass_manager=None, + qobj_id=None, seed_mapper=None, pass_manager=None, memory=False, **kwargs): """Executes a set of circuits. @@ -89,22 +90,16 @@ def execute(circuits, backend, config=None, basis_gates=None, coupling_map=None, qobj_id (int): identifier for the generated qobj pass_manager (PassManager): a pass manger for the transpiler pipeline memory (bool): if True, per-shot measurement bitstrings are returned as well. - skip_transpiler (bool): DEPRECATED skip transpiler and create qobj directly kwargs: extra arguments used by AER for running configurable backends. Refer to the backend documentation for details on these arguments Returns: BaseJob: returns job instance derived from BaseJob """ - if skip_transpiler: # empty pass manager which does nothing - pass_manager = PassManager() - warnings.warn('The skip_transpiler option has been deprecated. ' - 'Please pass an empty PassManager() instance instead', - DeprecationWarning) qobj = compile(circuits, backend, config, basis_gates, coupling_map, initial_layout, - shots, max_credits, seed, qobj_id, - skip_transpiler, seed_mapper, pass_manager, memory) + shots, max_credits, seed, qobj_id, seed_mapper, + pass_manager, memory) return backend.run(qobj, **kwargs) diff --git a/qiskit/unroll.py b/qiskit/unroll.py deleted file mode 100644 index 83222fd302..0000000000 --- a/qiskit/unroll.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2017, IBM. -# -# This source code is licensed under the Apache License, Version 2.0 found in -# the LICENSE.txt file in the root directory of this source tree. -# pylint: disable=unused-import - - -"""Unroll QASM and different backends.""" -from qiskit.unrollers.exceptions import BackendError -from qiskit.unrollers._dagunroller import DagUnroller -from qiskit.unrollers._unrollerbackend import UnrollerBackend -from qiskit.unrollers._jsonbackend import JsonBackend diff --git a/qiskit/unrollers/__init__.py b/qiskit/unrollers/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/qiskit/unrollers/_dagunroller.py b/qiskit/unrollers/_dagunroller.py deleted file mode 100644 index 9045470b69..0000000000 --- a/qiskit/unrollers/_dagunroller.py +++ /dev/null @@ -1,90 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2017, IBM. -# -# This source code is licensed under the Apache License, Version 2.0 found in -# the LICENSE.txt file in the root directory of this source tree. - -""" -DAG Unroller -""" - -import networkx as nx - -from .exceptions import UnrollerError - - -class DagUnroller: - """An Unroller that takes Dag circuits as the input.""" - - def __init__(self, dag_circuit, backend=None): - if dag_circuit is None: - raise UnrollerError('Invalid dag circuit!!') - - self.dag_circuit = dag_circuit - self.set_backend(backend) - - def set_backend(self, backend): - """Set the backend object. - - Give the same gate definitions to the backend circuit as - the input circuit. - """ - self.backend = backend - for name, data in self.dag_circuit.gates.items(): - self.backend.define_gate(name, data) - - def execute(self): - """Interpret OPENQASM and make appropriate backend calls. - - This does not expand gates. So Unroller must have - been previously called. Otherwise non-basis gates will be ignored - by this method. - """ - if self.backend is not None: - self._process() - return self.backend.get_output() - else: - raise UnrollerError("backend not attached") - - def _process(self): - """Process dag nodes. - - This method does *not* unroll. - """ - for qreg in self.dag_circuit.qregs.values(): - self.backend.new_qreg(qreg) - for creg in self.dag_circuit.cregs.values(): - self.backend.new_creg(creg) - for n in nx.topological_sort(self.dag_circuit.multi_graph): - current_node = self.dag_circuit.multi_graph.node[n] - if current_node["type"] == "op": - if current_node["condition"] is not None: - self.backend.set_condition(current_node["condition"][0], - current_node["condition"][1]) - - # TODO: The schema of the snapshot gate is radically - # different to other QASM instructions. The current model - # of extensions does not support generating custom Qobj - # instructions (only custom QASM strings) and the default - # instruction generator is not enough to produce a valid - # snapshot instruction for the new Qobj format. - # - # This is a hack since there would be mechanisms for the - # extensions to provide their own Qobj instructions. - # Extensions should not be hardcoded in the DAGUnroller. - extra_fields = None - if current_node["op"].name == "snapshot": - extra_fields = {'type': str(current_node["op"].param[1]), - 'label': str(current_node["op"].param[0]), - 'texparams': []} - - self.backend.start_gate(current_node["op"], - qargs=current_node["qargs"], - cargs=current_node["cargs"], - extra_fields=extra_fields) - self.backend.end_gate(current_node["op"]) - - self.backend.drop_condition() - - return self.backend.get_output() diff --git a/qiskit/unrollers/_jsonbackend.py b/qiskit/unrollers/_jsonbackend.py deleted file mode 100644 index b211f7a41a..0000000000 --- a/qiskit/unrollers/_jsonbackend.py +++ /dev/null @@ -1,235 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2017, IBM. -# -# This source code is licensed under the Apache License, Version 2.0 found in -# the LICENSE.txt file in the root directory of this source tree. - -# pylint: disable=arguments-differ - -"""Backend for the unroller that composes qasm into json file. - -The input is a AST and a basis set and returns a json memory object:: - - { - "header": { - "n_qubits": 2, // int - "memory_slots": 2, // int - "qubit_labels": [["q", 0], ["q", 1], null], // list[list[string, int] or null] - "clbit_labels": [["c", 0], ["c", 1]], // list[list[string, int]] - "qreg_sizes": [["q", 1], ["v", 1]], // list[list[string, int]] - "creg_sizes": [["c", 2]] // list[list[string, int]] - } - "instructions": // list[map] - [ - { - "name": , // required -- string - "params": , // optional -- list[double] - "texparams": , // optional -- list[string] - "qubits": , // optional -- list[int] - "cbits": , //optional -- list[int] - "conditional": // optional -- map - { - "type": "equals", // string - "mask": "0xHexadecimalString", // big int - "val": "0xHexadecimalString", // big int - } - }, - ] - } -""" -from collections import OrderedDict -import sympy - -from qiskit.unrollers.exceptions import BackendError -from qiskit.unrollers._unrollerbackend import UnrollerBackend - - -class JsonBackend(UnrollerBackend): - """Backend for the unroller that makes a Json quantum circuit.""" - - def __init__(self, basis=None): - """Setup this backend. - - basis is a list of operation name strings. - The default basis is ["U", "CX"]. - """ - super().__init__(basis) - self.circuit = {} - self.circuit['instructions'] = [] - self.circuit['header'] = { - 'n_qubits': 0, - 'memory_slots': 0, - 'qubit_labels': [], - 'clbit_labels': [], - 'qreg_sizes': [], - 'creg_sizes': [] - } - self._number_of_qubits = 0 - self._number_of_clbits = 0 - self._qreg_sizes = [] - self._creg_sizes = [] - self._qubit_order = [] - self._cbit_order = [] - self._qubit_order_internal = OrderedDict() - self._cbit_order_internal = OrderedDict() - - self.creg = None - self.cval = None - self.gates = OrderedDict() - if basis: - self.basis = basis.copy() - else: - self.basis = [] # default, unroll to U, CX - self.listen = True - self.in_gate = "" - self.printed_gates = [] - - def set_basis(self, basis): - """Declare the set of user-defined gates to emit. - - basis is a list of operation name strings. - """ - self.basis = basis.copy() - - def version(self, version): - """Print the version string. - - v is a version number. - """ - pass - - def new_qreg(self, qreg): - """Create a new quantum register. - - qreg = QuantumRegister object - """ - self._qreg_sizes.append([qreg.name, qreg.size]) - - # order qubits from lower to higher index. backends will do little endian. - for j in range(qreg.size): - self._qubit_order.append([qreg.name, j]) - self._qubit_order_internal[(qreg.name, j)] = self._number_of_qubits + j - self._number_of_qubits += qreg.size - # TODO: avoid rewriting the same data over and over - self.circuit['header']['n_qubits'] = self._number_of_qubits - self.circuit['header']['qreg_sizes'] = self._qreg_sizes - self.circuit['header']['qubit_labels'] = self._qubit_order - - def new_creg(self, creg): - """Create a new classical register. - - creg = ClassicalRegister object - """ - self._creg_sizes.append([creg.name, creg.size]) - # order clbits from lower to higher index. backends will do little endian. - for j in range(creg.size): - self._cbit_order.append([creg.name, j]) - self._cbit_order_internal[(creg.name, j)] = self._number_of_clbits + j - self._number_of_clbits += creg.size - # TODO: avoid rewriting the same data over and over - self.circuit['header']['memory_slots'] = self._number_of_clbits - self.circuit['header']['creg_sizes'] = self._creg_sizes - self.circuit['header']['clbit_labels'] = self._cbit_order - - def define_gate(self, name, gatedata): - """Define a new quantum gate. - - name is a string. - gatedata is the AST node for the gate. - """ - self.gates[name] = gatedata - - def _add_condition(self): - """Check for a condition (self.creg) and add fields if necessary. - - Fields are added to the last operation in the circuit. - """ - if self.creg is not None: - mask = 0 - for cbit, index in self._cbit_order_internal.items(): - if cbit[0] == self.creg.name: - mask |= (1 << index) - # Would be nicer to zero pad the mask, but we - # need to know the total number of cbits. - # format_spec = "{0:#0{%d}X}" % number_of_clbits - # format_spec.format(mask) - conditional = { - 'type': "equals", - 'mask': "0x%X" % mask, - 'val': "0x%X" % self.cval - } - self.circuit['instructions'][-1]['conditional'] = conditional - - def set_condition(self, creg, cval): - """Attach a current condition. - - creg is a name string. - cval is the integer value for the test. - """ - self.creg = creg - self.cval = cval - - def drop_condition(self): - """Drop the current condition.""" - self.creg = None - self.cval = None - - def start_gate(self, op, qargs, cargs, extra_fields=None): - """ - Begin a custom gate. - - Args: - op (Instruction): operation to apply to the dag. - qargs (list[QuantumRegister, int]): qubit arguments - cargs (list[ClassicalRegister, int]): clbit arguments - extra_fields (dict): extra_fields used by non-standard instructions - for now (e.g. snapshot) - - Raises: - BackendError: if using a non-basis opaque gate - """ - if not self.listen: - return - if op.name not in self.basis and self.gates[op.name]["opaque"]: - raise BackendError("opaque gate %s not in basis" % op.name) - if op.name in self.basis: - self.in_gate = op - self.listen = False - qubit_indices = [self._qubit_order_internal.get((qubit[0].name, qubit[1])) - for qubit in qargs] - clbit_indices = [self._cbit_order_internal.get((cbit[0].name, cbit[1])) - for cbit in cargs] - gate_instruction = { - 'name': op.name, - 'params': list(map(lambda x: x.evalf(), op.param)), - 'texparams': list(map(sympy.latex, op.param)), - 'qubits': qubit_indices, - 'memory': clbit_indices - } - if extra_fields is not None: - gate_instruction.update(extra_fields) - self.circuit['instructions'].append(gate_instruction) - self._add_condition() - - def end_gate(self, op): - """End a custom gate. - - Args: - op (Instruction): operation to apply to the dag. - """ - if op == self.in_gate: - self.in_gate = None - self.listen = True - - def get_output(self): - """Returns the generated circuit.""" - if not self._is_circuit_valid(): - raise BackendError("Invalid circuit! Please check the syntax of your circuit." - "Has Qasm parsing been called?. e.g: unroller.execute().") - return self.circuit - - def _is_circuit_valid(self): - """Checks whether the circuit object is a valid one or not.""" - return (len(self.circuit['header']) > 0 and - len(self.circuit['instructions']) >= 0) diff --git a/qiskit/unrollers/_unrollerbackend.py b/qiskit/unrollers/_unrollerbackend.py deleted file mode 100644 index 97fb893d9c..0000000000 --- a/qiskit/unrollers/_unrollerbackend.py +++ /dev/null @@ -1,153 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2017, IBM. -# -# This source code is licensed under the Apache License, Version 2.0 found in -# the LICENSE.txt file in the root directory of this source tree. - -""" -Base backend object for the unroller that raises BackendError. -""" -from qiskit.unrollers.exceptions import BackendError - - -class UnrollerBackend: - """Backend for the unroller that raises BackendError. - - This backend also serves as a base class for other unroller backends. - """ - # pylint: disable=unused-argument - - def __init__(self, basis=None): - """Setup this backend. - - basis is a list of operation name strings. - """ - if basis: - basis = [] - - def set_basis(self, basis): - """Declare the set of user-defined gates to emit. - - basis is a list of operation name strings. - """ - raise BackendError("Backend set_basis unimplemented") - - def version(self, version): - """Print the version string. - - v is a version number. - """ - raise BackendError("Backend version unimplemented") - - def new_qreg(self, qreg): - """Create a new quantum register. - - qreg = QuantumRegister object - """ - raise BackendError("Backend new_qreg unimplemented") - - def new_creg(self, creg): - """Create a new classical register. - - creg = ClassicalRegister object - """ - raise BackendError("Backend new_creg unimplemented") - - def define_gate(self, name, gatedata): - """Define a new quantum gate. - - name is a string. - gatedata is the AST node for the gate. - """ - raise BackendError("Backend define_gate unimplemented") - - def u(self, arg, qubit, nested_scope=None): - """Fundamental single qubit gate. - - arg is 3-tuple of Node expression objects. - qubit is (regname,idx) tuple. - nested_scope is a list of dictionaries mapping expression variables - to Node expression objects in order of increasing nesting depth. - """ - # pylint: disable=invalid-name - raise BackendError("Backend u unimplemented") - - def cx(self, qubit0, qubit1): - """Fundamental two qubit gate. - - qubit0 is (regname,idx) tuple for the control qubit. - qubit1 is (regname,idx) tuple for the target qubit. - """ - # pylint: disable=invalid-name - raise BackendError("Backend cx unimplemented") - - def measure(self, qubit, bit): - """Measurement operation. - - qubit is (regname, idx) tuple for the input qubit. - bit is (regname, idx) tuple for the output bit. - """ - raise BackendError("Backend measure unimplemented") - - def barrier(self, qubitlists): - """Barrier instruction. - - qubitlists is a list of lists of (regname, idx) tuples. - """ - raise BackendError("Backend barrier unimplemented") - - def reset(self, qubit): - """Reset instruction. - - qubit is a (regname, idx) tuple. - """ - raise BackendError("Backend reset unimplemented") - - def set_condition(self, creg, cval): - """Attach a current condition. - - creg is a name string. - cval is the integer value for the test. - """ - raise BackendError("Backend set_condition unimplemented") - - def drop_condition(self): - """Drop the current condition.""" - raise BackendError("Backend drop_condition unimplemented") - - def start_gate(self, name, args, qubits, nested_scope=None, extra_fields=None): - """Start a custom gate. - - Args: - name (str): name of the gate. - args (list[Node]): list of expression nodes. - qubits (list[tuple(str, int)]): list of (regname, idx) tuples. - nested_scope (list[dict()]): list of dictionaries mapping expression - variables to Node expression objects in order of increasing - nesting depth. - extra_fields (dict(str, obj)): is a dictionary allowing the extension - or overriding of the gate instruction properties. - - Raises: - BackendError: if the gate is not part of the basis. - """ - raise BackendError("Backend start_gate unimplemented") - - def end_gate(self, name, args, qubits, nested_scope=None): - """End a custom gate. - - name is name string. - args is list of Node expression objects. - qubits is list of (regname, idx) tuples. - nested_scope is a list of dictionaries mapping expression variables - to Node expression objects in order of increasing nesting depth. - """ - raise BackendError("Backend end_gate unimplemented") - - def get_output(self): - """Returns the output generated by the backend. - Depending on the type of Backend, the output could have different types. - It must be called once the Qasm parsing has finished - """ - raise BackendError("Backend get_output unimplemented") diff --git a/qiskit/unrollers/exceptions.py b/qiskit/unrollers/exceptions.py deleted file mode 100644 index 5a4dbfb264..0000000000 --- a/qiskit/unrollers/exceptions.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2017, IBM. -# -# This source code is licensed under the Apache License, Version 2.0 found in -# the LICENSE.txt file in the root directory of this source tree. - -""" -Exception for errors raised by unroller. -""" - - -from qiskit.exceptions import QiskitError - - -class UnrollerError(QiskitError): - """Base class for errors raised by unroller.""" - - def __init__(self, *message): - """Set the error message.""" - super().__init__(*message) - self.message = ' '.join(message) - - def __str__(self): - """Return the message.""" - return repr(self.message) - - -class BackendError(QiskitError): - """Base class for errors raised by unroller backends.""" - - def __init__(self, *message): - """Set the error message.""" - super().__init__(*message) - self.message = ' '.join(message) - - def __str__(self): - """Return the message.""" - return repr(self.message) diff --git a/test/python/converters/test_qobj_to_circuits.py b/test/python/converters/test_qobj_to_circuits.py index bfb4d2b185..5117bce306 100644 --- a/test/python/converters/test_qobj_to_circuits.py +++ b/test/python/converters/test_qobj_to_circuits.py @@ -6,6 +6,7 @@ # the LICENSE.txt file in the root directory of this source tree. # pylint: disable=redefined-builtin +# pylint: disable=unused-import """Tests for the converters.""" @@ -20,6 +21,7 @@ from qiskit.qobj import Qobj from qiskit.transpiler import PassManager from qiskit.converters import circuit_to_dag from qiskit.test import QiskitTestCase +import qiskit.extensions.simulator class TestQobjToCircuits(QiskitTestCase): diff --git a/test/python/qobj/test_qobj.py b/test/python/qobj/test_qobj.py index b827487cc9..cd9da03fef 100644 --- a/test/python/qobj/test_qobj.py +++ b/test/python/qobj/test_qobj.py @@ -20,6 +20,7 @@ from qiskit.qobj import QobjHeader, validate_qobj_against_schema from qiskit.providers.builtinsimulators import simulatorsjob from qiskit.providers.ibmq import ibmqjob from qiskit.test import QiskitTestCase + from .._mockutils import FakeBackend @@ -131,24 +132,13 @@ class TestQobj(QiskitTestCase): qc1.measure(qr, cr) qc2.measure(qr, cr) circuits = [qc1, qc2] - shots = 1024 backend = BasicAer.get_backend('qasm_simulator') - config = {'seed': 10, 'shots': 1, 'xvals': [1, 2, 3, 4]} - qobj1 = compile(circuits, backend=backend, shots=shots, seed=88, config=config) + qobj1 = compile(circuits, backend=backend, shots=1024, seed=88) qobj1.experiments[0].config.shots = 50 - qobj1.experiments[0].config.xvals = [1, 1, 1] - config['shots'] = 1000 - config['xvals'][0] = 'only for qobj2' - qobj2 = compile(circuits, backend=backend, shots=shots, seed=88, config=config) + qobj1.experiments[1].config.shots = 1 self.assertTrue(qobj1.experiments[0].config.shots == 50) self.assertTrue(qobj1.experiments[1].config.shots == 1) - self.assertTrue(qobj1.experiments[0].config.xvals == [1, 1, 1]) - self.assertTrue(qobj1.experiments[1].config.xvals == [1, 2, 3, 4]) self.assertTrue(qobj1.config.shots == 1024) - self.assertTrue(qobj2.experiments[0].config.shots == 1000) - self.assertTrue(qobj2.experiments[1].config.shots == 1000) - self.assertTrue(qobj2.experiments[0].config.xvals == ['only for qobj2', 2, 3, 4]) - self.assertTrue(qobj2.experiments[1].config.xvals == ['only for qobj2', 2, 3, 4]) def _nop(): diff --git a/test/python/test_unroller.py b/test/python/test_unroller.py deleted file mode 100644 index 77bccf98d7..0000000000 --- a/test/python/test_unroller.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2017, IBM. -# -# This source code is licensed under the Apache License, Version 2.0 found in -# the LICENSE.txt file in the root directory of this source tree. - -"""Test Qiskit Unroller class.""" - -import unittest - -from qiskit import qasm -from qiskit.unroll import DagUnroller, JsonBackend -from qiskit.converters import ast_to_dag -from qiskit.test import QiskitTestCase, Path - - -class UnrollerTest(QiskitTestCase): - """Test the Unroller.""" - - # We need to change the way we create clbit_labels and qubit_labels in order to - # enable this test, as they are lists but the order is not important so comparing - # them usually fails. - @unittest.skip("Temporary skipping") - def test_dag_to_json(self): - """Test DagUnroller with JSON backend.""" - ast = qasm.Qasm(filename=self._get_resource_path('example.qasm', Path.QASMS)).parse() - dag_circuit = ast_to_dag(ast) - dag_unroller = DagUnroller(dag_circuit, JsonBackend()) - json_circuit = dag_unroller.execute() - expected_result = { - 'operations': - [ - {'qubits': [5], 'texparams': ['0.5 \\pi', '0', '\\pi'], - 'name': 'U', 'params': [1.5707963267948966, 0.0, 3.141592653589793]}, - {'name': 'CX', 'qubits': [5, 2]}, - {'clbits': [2], 'name': 'measure', 'qubits': [2]}, - {'qubits': [4], 'texparams': ['0.5 \\pi', '0', '\\pi'], 'name': 'U', - 'params': [1.5707963267948966, 0.0, 3.141592653589793]}, - {'name': 'CX', 'qubits': [4, 1]}, - {'clbits': [1], 'name': 'measure', 'qubits': [1]}, - {'qubits': [3], 'texparams': ['0.5 \\pi', '0', '\\pi'], 'name': 'U', - 'params': [1.5707963267948966, 0.0, 3.141592653589793]}, - {'name': 'CX', 'qubits': [3, 0]}, - {'name': 'barrier', 'qubits': [3, 4, 5]}, - {'clbits': [5], 'name': 'measure', 'qubits': [5]}, - {'clbits': [4], 'name': 'measure', 'qubits': [4]}, - {'clbits': [3], 'name': 'measure', 'qubits': [3]}, - {'clbits': [0], 'name': 'measure', 'qubits': [0]} - ], - 'header': - { - 'memory_slots': 6, - 'qubit_labels': [['r', 0], ['r', 1], ['r', 2], ['q', 0], ['q', 1], ['q', 2]], - 'n_qubits': 6, 'clbit_labels': [['d', 3], ['c', 3]] - } - } - - self.assertEqual(json_circuit, expected_result) diff --git a/test/python/tools/test_compiler.py b/test/python/tools/test_compiler.py index 95b40c77fc..9b02fbcbf0 100644 --- a/test/python/tools/test_compiler.py +++ b/test/python/tools/test_compiler.py @@ -326,17 +326,20 @@ class TestCompiler(QiskitTestCase): qobj = compile(qlist, backend=backend) self.assertEqual(len(qobj.experiments), 10) - def test_compile_skip_transpiler(self): + def test_compile_pass_manager(self): """Test compile with and without an empty pass manager.""" qr = QuantumRegister(2) cr = ClassicalRegister(2) qc = QuantumCircuit(qr, cr) qc.u1(3.14, qr[0]) qc.u2(3.14, 1.57, qr[0]) + qc.barrier(qr) qc.measure(qr, cr) backend = BasicAer.get_backend('qasm_simulator') - rtrue = execute(qc, backend, seed=42).result() - rfalse = execute(qc, backend, seed=42, pass_manager=PassManager()).result() + qrtrue = compile(qc, backend, seed=42) + rtrue = backend.run(qrtrue).result() + qrfalse = compile(qc, backend, seed=42, pass_manager=PassManager()) + rfalse = backend.run(qrfalse).result() self.assertEqual(rtrue.get_counts(), rfalse.get_counts()) diff --git a/test/python/transpiler/test_transpile.py b/test/python/transpiler/test_transpile.py index 6f6b8b0492..0d5fd94c1b 100644 --- a/test/python/transpiler/test_transpile.py +++ b/test/python/transpiler/test_transpile.py @@ -15,6 +15,7 @@ from qiskit.transpiler import PassManager, transpile_dag, transpile from qiskit.tools.compiler import circuits_to_qobj from qiskit.converters import circuit_to_dag from qiskit.test import QiskitTestCase +from qiskit.qobj.run_config import RunConfig class TestTranspile(QiskitTestCase): @@ -66,8 +67,6 @@ class TestTranspile(QiskitTestCase): pass_manager=None) qobj = compile(circuit, backend=backend, coupling_map=coupling_map, basis_gates=basis_gates) - - qobj2 = circuits_to_qobj(circuit2, backend.name(), basis_gates=basis_gates, - coupling_map=coupling_map, qobj_id=qobj.qobj_id) - + run_config = RunConfig(shots=1024, max_credits=10) + qobj2 = circuits_to_qobj(circuit2, qobj_id=qobj.qobj_id, run_config=run_config) self.assertEqual(qobj, qobj2)