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 <jay.gambetta@us.ibm.com>

* Update qiskit/qobj/run_config.py

Co-Authored-By: jaygambetta <jay.gambetta@us.ibm.com>

* update warning messages. remove init from RunConfig as all optional

* only put optional shots, max_credits, memory in qobj if they are set
This commit is contained in:
Jay Gambetta 2019-01-03 08:54:25 -05:00 committed by Ali Javadi-Abhari
parent 95fbde842d
commit 2ea7d176ea
21 changed files with 283 additions and 757 deletions

View File

@ -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
=====================

View File

@ -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)

View File

@ -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 = []
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 = []
try:
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)
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

View File

@ -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)

View File

@ -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

View File

@ -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__)

View File

@ -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__()

40
qiskit/qobj/run_config.py Normal file
View File

@ -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.
"""

View File

@ -887,9 +887,6 @@
"type": "integer"
}
},
"required": [
"shots"
],
"title": "Qobj-level configuration",
"type": "object"
},

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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")

View File

@ -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)

View File

@ -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):

View File

@ -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():

View File

@ -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)

View File

@ -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())

View File

@ -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)