Update the local python simulators to use Qobj (#667)

* Update qasm and statevector py sims for using Qobj

Update the `qasm_simulator_py` and the `statevector_simulator_py`
simulators in order to use the `Qobj` schema internally, without
converting the circuits to the old schema.

Fix missing import in `qiskit.qobj.__init__`.

Fix `seed` parameter not being passed into the experiments `config`
during compilation.

* Update unitary python simulator for uusing Qobj

Update the `unitary_simulator_py` in order to use the `Qobj` schema
internally, without converting the circuits to the old schema.

* Update changelog

* Use config.seed inheritance in Python simulator

Use inheritance for the `config.seed` value for the Python simulator,
reverting the change that copied the qobj-level configuration into the
experiment-level configuration.
Fix comma in test.
This commit is contained in:
Diego M. Rodríguez 2018-07-24 16:09:38 +02:00 committed by GitHub
parent 80530d6b78
commit c7121fc65e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 163 additions and 182 deletions

View File

@ -26,7 +26,10 @@ Added
Changed
-------
- Renamed ``QISKit`` to ``Qiskit`` in the documentation. (#634)
- Introduce a ``QObj`` class replacing the previous dictionaries (#589).
- Use ``Qobj`` as the formally defined schema for sending information to the
devices:
- introduce the ``qiskit.qobj`` module. (#589, #655)
- update the local simulators for accepting ``Qobj`` as input. (#667)
- Use ``get_status_job()`` for checking IBMQJob status. (#641)
Removed

View File

@ -82,7 +82,6 @@ import numpy as np
from qiskit._result import Result
from qiskit.backends import BaseBackend
from qiskit.backends.local.localjob import LocalJob
from qiskit.qobj import qobj_to_dict
from ._simulatorerror import SimulatorError
from ._simulatortools import single_gate_matrix
logger = logging.getLogger(__name__)
@ -117,7 +116,7 @@ class QasmSimulatorPy(BaseBackend):
self._number_of_cbits = 0
self._number_of_qubits = 0
self._shots = 0
self._seed = 1
self._qobj_config = None
@staticmethod
def _index1(b, i, k):
@ -278,10 +277,10 @@ class QasmSimulatorPy(BaseBackend):
self._validate(qobj)
result_list = []
self._shots = qobj.config.shots
self._qobj_config = qobj.config
start = time.time()
qobj_prev = qobj_to_dict(qobj, version='0.0.1')
for circuit in qobj_prev['circuits']:
for circuit in qobj.experiments:
result_list.append(self.run_circuit(circuit))
end = time.time()
job_id = str(uuid.uuid4())
@ -298,7 +297,7 @@ class QasmSimulatorPy(BaseBackend):
"""Run a circuit and return a single Result.
Args:
circuit (dict): JSON circuit from qobj circuits list
circuit (QobjExperiment): experiment from qobj experiments list
Returns:
dict: A dictionary of results which looks something like::
@ -314,24 +313,24 @@ class QasmSimulatorPy(BaseBackend):
Raises:
SimulatorError: if an error occurred.
"""
ccircuit = circuit['compiled_circuit']
self._number_of_qubits = ccircuit['header']['number_of_qubits']
self._number_of_cbits = ccircuit['header']['number_of_clbits']
self._number_of_qubits = circuit.header.number_of_qubits
self._number_of_cbits = circuit.header.number_of_clbits
self._statevector = 0
self._classical_state = 0
self._snapshots = {}
cl_reg_index = [] # starting bit index of classical register
cl_reg_nbits = [] # number of bits in classical register
cbit_index = 0
for cl_reg in ccircuit['header']['clbit_labels']:
for cl_reg in circuit.header.clbit_labels:
cl_reg_nbits.append(cl_reg[1])
cl_reg_index.append(cbit_index)
cbit_index += cl_reg[1]
if circuit['config']['seed'] is None:
self._seed = random.getrandbits(32)
else:
self._seed = circuit['config']['seed']
self._local_random.seed(self._seed)
# Get the seed looking in circuit, qobj, and then random.
seed = getattr(circuit.config, 'seed',
getattr(self._qobj_config, 'seed',
random.getrandbits(32)))
self._local_random.seed(seed)
outcomes = []
start = time.time()
@ -340,69 +339,67 @@ class QasmSimulatorPy(BaseBackend):
dtype=complex)
self._statevector[0] = 1
self._classical_state = 0
for operation in ccircuit['operations']:
if 'conditional' in operation:
mask = int(operation['conditional']['mask'], 16)
for operation in circuit.instructions:
if getattr(operation, 'conditional', None):
mask = int(operation.conditional.mask, 16)
if mask > 0:
value = self._classical_state & mask
while (mask & 0x1) == 0:
mask >>= 1
value >>= 1
if value != int(operation['conditional']['val'], 16):
if value != int(operation.conditional.val, 16):
continue
# Check if single gate
if operation['name'] in ['U', 'u1', 'u2', 'u3']:
if 'params' in operation:
params = operation['params']
else:
params = None
qubit = operation['qubits'][0]
gate = single_gate_matrix(operation['name'], params)
if operation.name in ('U', 'u1', 'u2', 'u3'):
params = getattr(operation, 'params', None)
qubit = operation.qubits[0]
gate = single_gate_matrix(operation.name, params)
self._add_qasm_single(gate, qubit)
# Check if CX gate
elif operation['name'] in ['id', 'u0']:
elif operation.name in ('id', 'u0'):
pass
elif operation['name'] in ['CX', 'cx']:
qubit0 = operation['qubits'][0]
qubit1 = operation['qubits'][1]
elif operation.name in ('CX', 'cx'):
qubit0 = operation.qubits[0]
qubit1 = operation.qubits[1]
self._add_qasm_cx(qubit0, qubit1)
# Check if measure
elif operation['name'] == 'measure':
qubit = operation['qubits'][0]
cbit = operation['clbits'][0]
elif operation.name == 'measure':
qubit = operation.qubits[0]
cbit = operation.clbits[0]
self._add_qasm_measure(qubit, cbit)
# Check if reset
elif operation['name'] == 'reset':
qubit = operation['qubits'][0]
elif operation.name == 'reset':
qubit = operation.qubits[0]
self._add_qasm_reset(qubit)
# Check if barrier
elif operation['name'] == 'barrier':
elif operation.name == 'barrier':
pass
# Check if snapshot command
elif operation['name'] == 'snapshot':
params = operation['params']
elif operation.name == 'snapshot':
params = operation.params
self._add_qasm_snapshot(params[0])
else:
backend = self._configuration['name']
err_msg = '{0} encountered unrecognized operation "{1}"'
raise SimulatorError(err_msg.format(backend,
operation['name']))
operation.name))
# Turn classical_state (int) into bit string
outcomes.append(bin(self._classical_state)[2:].zfill(
self._number_of_cbits))
# Return the results
counts = dict(Counter(outcomes))
data = {'counts': self._format_result(
counts, cl_reg_index, cl_reg_nbits)}
data['snapshots'] = self._snapshots
data = {
'counts': self._format_result(counts, cl_reg_index, cl_reg_nbits),
'snapshots': self._snapshots
}
if self._shots == 1:
# TODO: deprecated -- remove in v0.6
data['statevector'] = self._statevector
data['quantum_state'] = self._statevector
data['classical_state'] = self._classical_state
end = time.time()
return {'name': circuit['name'],
'seed': self._seed,
return {'name': circuit.header.name,
'seed': seed,
'shots': self._shots,
'data': data,
'status': 'DONE',

View File

@ -88,7 +88,6 @@ import numpy as np
from qiskit._result import Result
from qiskit.backends import BaseBackend
from qiskit.backends.local.localjob import LocalJob
from qiskit.qobj import qobj_to_dict
from ._simulatortools import enlarge_single_opt, enlarge_two_opt, single_gate_matrix
logger = logging.getLogger(__name__)
@ -128,8 +127,8 @@ class UnitarySimulatorPy(BaseBackend):
is q_{n-1} ... otimes q_1 otimes q_0.
number_of_qubits is the number of qubits in the system.
"""
unitaty_add = enlarge_single_opt(gate, qubit, self._number_of_qubits)
self._unitary_state = np.dot(unitaty_add, self._unitary_state)
unitary_add = enlarge_single_opt(gate, qubit, self._number_of_qubits)
self._unitary_state = np.dot(unitary_add, self._unitary_state)
def _add_unitary_two(self, gate, q_0, q_1):
"""Apply the two-qubit gate.
@ -162,46 +161,49 @@ class UnitarySimulatorPy(BaseBackend):
Result: Result object
"""
result_list = []
qobj_converted = qobj_to_dict(qobj, version='0.0.1')
for circuit in qobj_converted['circuits']:
for circuit in qobj.experiments:
result_list.append(self.run_circuit(circuit))
job_id = str(uuid.uuid4())
return Result(
{'job_id': job_id, 'result': result_list, 'status': 'COMPLETED'})
def run_circuit(self, circuit):
"""Apply the single-qubit gate."""
ccircuit = circuit['compiled_circuit']
self._number_of_qubits = ccircuit['header']['number_of_qubits']
result = {}
result['data'] = {}
result['name'] = circuit.get('name')
self._unitary_state = np.identity(2**(self._number_of_qubits),
"""Apply the single-qubit gate.
Args:
circuit (QobjExperiment): experiment from qobj experiments list
Returns:
dict: A dictionary of results.
"""
self._number_of_qubits = circuit.header.number_of_qubits
result = {
'data': {},
'name': circuit.header.name
}
self._unitary_state = np.identity(2 ** self._number_of_qubits,
dtype=complex)
for operation in ccircuit['operations']:
if operation['name'] in ['U', 'u1', 'u2', 'u3']:
if 'params' in operation:
params = operation['params']
else:
params = None
qubit = operation['qubits'][0]
gate = single_gate_matrix(operation['name'], params)
for operation in circuit.instructions:
if operation.name in ('U', 'u1', 'u2', 'u3'):
params = getattr(operation, 'params', None)
qubit = operation.qubits[0]
gate = single_gate_matrix(operation.name, params)
self._add_unitary_single(gate, qubit)
elif operation['name'] in ['id', 'u0']:
elif operation.name in ('id', 'u0'):
pass
elif operation['name'] in ['CX', 'cx']:
qubit0 = operation['qubits'][0]
qubit1 = operation['qubits'][1]
elif operation.name in ('CX', 'cx'):
qubit0 = operation.qubits[0]
qubit1 = operation.qubits[1]
gate = np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0],
[0, 1, 0, 0]])
self._add_unitary_two(gate, qubit0, qubit1)
elif operation['name'] == 'measure':
elif operation.name == 'measure':
logger.info('Warning have dropped measure from unitary '
'simulator')
elif operation['name'] == 'reset':
elif operation.name == 'reset':
logger.info('Warning have dropped reset from unitary '
'simulator')
elif operation['name'] == 'barrier':
elif operation.name == 'barrier':
pass
else:
result['status'] = 'ERROR'

View File

@ -7,5 +7,6 @@
"""Module for the Qobj structure."""
from ._qobj import (Qobj, QobjConfig, QobjExperiment, QobjInstruction, QobjItem)
from ._qobj import (Qobj, QobjConfig, QobjExperiment, QobjInstruction,
QobjItem, QobjHeader)
from ._converter import qobj_to_dict

View File

@ -16,9 +16,10 @@ import unittest
from matplotlib.backends.backend_pdf import PdfPages
import numpy as np
from qiskit import qasm, unroll, QuantumProgram
from qiskit import (qasm, unroll, transpiler,
ClassicalRegister, QuantumRegister, QuantumCircuit)
from qiskit.backends.local.qasm_simulator_py import QasmSimulatorPy
from qiskit.qobj import Qobj, QobjItem
from qiskit.qobj import Qobj, QobjHeader, QobjItem, QobjConfig, QobjExperiment
from ._random_qasm_generator import RandomQasmGenerator
from .common import QiskitTestCase
@ -42,30 +43,25 @@ class TestLocalQasmSimulatorPy(QiskitTestCase):
cls.pdf.close()
def setUp(self):
self.qp = None
self.seed = 88
self.qasm_filename = self._get_resource_path('qasm/example.qasm')
self.qp = QuantumProgram()
self.qp.load_qasm_file(self.qasm_filename, name='example')
basis_gates = [] # unroll to base gates
unroller = unroll.Unroller(
qasm.Qasm(data=self.qp.get_qasm('example')).parse(),
unroll.JsonBackend(basis_gates))
circuit = unroller.execute()
circuit_config = {'coupling_map': None,
'basis_gates': 'u1,u2,u3,cx,id',
'layout': None,
'seed': self.seed}
resources = {'max_credits': 3}
self.qobj = {'id': 'test_sim_single_shot',
'config': {
'max_credits': resources['max_credits'],
'shots': 1024,
},
'experiments': [circuit],
'header': {'backend_name': 'local_qasm_simulator_py'}}
self.qobj = Qobj.from_dict(self.qobj)
self.qobj.experiments[0].config = QobjItem.from_dict(circuit_config)
self.qobj.experiments[0].header.name = 'test'
qasm_filename = self._get_resource_path('qasm/example.qasm')
unroller = unroll.Unroller(qasm.Qasm(filename=qasm_filename).parse(),
unroll.JsonBackend([]))
circuit = QobjExperiment.from_dict(unroller.execute())
circuit.config = QobjItem(coupling_map=None,
basis_gates='u1,u2,u3,cx,id',
layout=None,
seed=self.seed)
circuit.header.name = 'test'
self.qobj = Qobj(id='test_sim_single_shot',
config=QobjConfig(shots=1024,
register_slots=6,
max_credits=3),
experiments=[circuit],
header=QobjHeader(
backend_name='local_qasm_simulator_py'))
def test_qasm_simulator_single_shot(self):
"""Test single shot run."""
@ -90,10 +86,9 @@ class TestLocalQasmSimulatorPy(QiskitTestCase):
self.log.info('test_if_statement_x')
shots = 100
max_qubits = 3
qp = QuantumProgram()
qr = qp.create_quantum_register('qr', max_qubits)
cr = qp.create_classical_register('cr', max_qubits)
circuit_if_true = qp.create_circuit('test_if_true', [qr], [cr])
qr = QuantumRegister(max_qubits, 'qr')
cr = ClassicalRegister(max_qubits, 'cr')
circuit_if_true = QuantumCircuit(qr, cr, name='test_if_true')
circuit_if_true.x(qr[0])
circuit_if_true.x(qr[1])
circuit_if_true.measure(qr[0], cr[0])
@ -102,7 +97,7 @@ class TestLocalQasmSimulatorPy(QiskitTestCase):
circuit_if_true.measure(qr[0], cr[0])
circuit_if_true.measure(qr[1], cr[1])
circuit_if_true.measure(qr[2], cr[2])
circuit_if_false = qp.create_circuit('test_if_false', [qr], [cr])
circuit_if_false = QuantumCircuit(qr, cr, name='test_if_false')
circuit_if_false.x(qr[0])
circuit_if_false.measure(qr[0], cr[0])
circuit_if_false.measure(qr[1], cr[1])
@ -112,38 +107,32 @@ class TestLocalQasmSimulatorPy(QiskitTestCase):
circuit_if_false.measure(qr[2], cr[2])
basis_gates = [] # unroll to base gates
unroller = unroll.Unroller(
qasm.Qasm(data=qp.get_qasm('test_if_true')).parse(),
qasm.Qasm(data=circuit_if_true.qasm()).parse(),
unroll.JsonBackend(basis_gates))
ucircuit_true = unroller.execute()
ucircuit_true = QobjExperiment.from_dict(unroller.execute())
unroller = unroll.Unroller(
qasm.Qasm(data=qp.get_qasm('test_if_false')).parse(),
qasm.Qasm(data=circuit_if_false.qasm()).parse(),
unroll.JsonBackend(basis_gates))
ucircuit_false = unroller.execute()
ucircuit_false = QobjExperiment.from_dict(unroller.execute())
qobj = {
'id': 'test_if_qobj',
'config': {
'max_credits': 3,
'shots': shots,
},
'experiments': [ucircuit_true, ucircuit_false],
'header': {'backend_name': 'local_qasm_simulator_py'}
}
qobj['experiments'][0]['header']['name'] = 'test_if_true'
qobj['experiments'][0]['config'] = {
'coupling_map': None,
'basis_gates': 'u1,u2,u3,cx,id',
'layout': None,
'seed': None
}
qobj['experiments'][1]['header']['name'] = 'test_if_false'
qobj['experiments'][1]['config'] = {
'coupling_map': None,
'basis_gates': 'u1,u2,u3,cx,id',
'layout': None,
'seed': None
}
qobj = Qobj.from_dict(qobj)
# Customize the experiments and create the qobj.
ucircuit_true.config = QobjItem(coupling_map=None,
basis_gates='u1,u2,u3,cx,id',
layout=None,
seed=None)
ucircuit_true.header.name = 'test_if_true'
ucircuit_false.config = QobjItem(coupling_map=None,
basis_gates='u1,u2,u3,cx,id',
layout=None,
seed=None)
ucircuit_false.header.name = 'test_if_false'
qobj = Qobj(id='test_if_qobj',
config=QobjConfig(max_credits=3,
shots=shots,
register_slots=max_qubits),
experiments=[ucircuit_true, ucircuit_false],
header=QobjHeader(backend_name='local_qasm_simulator_py'))
result = QasmSimulatorPy().run(qobj).result()
result_if_true = result.get_data('test_if_true')
@ -163,17 +152,14 @@ class TestLocalQasmSimulatorPy(QiskitTestCase):
"we have to disable this test until fixed")
def test_teleport(self):
"""test teleportation as in tutorials"""
self.log.info('test_teleport')
pi = np.pi
shots = 1000
qp = QuantumProgram()
qr = qp.create_quantum_register('qr', 3)
cr0 = qp.create_classical_register('cr0', 1)
cr1 = qp.create_classical_register('cr1', 1)
cr2 = qp.create_classical_register('cr2', 1)
circuit = qp.create_circuit('teleport', [qr],
[cr0, cr1, cr2])
qr = QuantumRegister(3, 'qr')
cr0 = ClassicalRegister(1, 'cr0')
cr1 = ClassicalRegister(1, 'cr1')
cr2 = ClassicalRegister(1, 'cr2')
circuit = QuantumCircuit(qr, cr0, cr1, cr2, name='teleport')
circuit.h(qr[1])
circuit.cx(qr[1], qr[2])
circuit.ry(pi/4, qr[0])
@ -185,20 +171,23 @@ class TestLocalQasmSimulatorPy(QiskitTestCase):
circuit.z(qr[2]).c_if(cr0, 1)
circuit.x(qr[2]).c_if(cr1, 1)
circuit.measure(qr[2], cr2[0])
backend = 'local_qasm_simulator_py'
qobj = qp.compile('teleport', backend=backend, shots=shots,
seed=self.seed)
results = qp.run(qobj)
backend = QasmSimulatorPy()
qobj = transpiler.compile(circuit, backend=backend, shots=shots,
seed=self.seed)
results = backend.run(qobj).result()
data = results.get_counts('teleport')
alice = {}
bob = {}
alice['00'] = data['0 0 0'] + data['1 0 0']
alice['01'] = data['0 1 0'] + data['1 1 0']
alice['10'] = data['0 0 1'] + data['1 0 1']
alice['11'] = data['0 1 1'] + data['1 1 1']
bob['0'] = data['0 0 0'] + data['0 1 0'] + data['0 0 1'] + data['0 1 1']
bob['1'] = data['1 0 0'] + data['1 1 0'] + data['1 0 1'] + data['1 1 1']
self.log.info('test_telport: circuit:')
alice = {
'00': data['0 0 0'] + data['1 0 0'],
'01': data['0 1 0'] + data['1 1 0'],
'10': data['0 0 1'] + data['1 0 1'],
'11': data['0 1 1'] + data['1 1 1']
}
bob = {
'0': data['0 0 0'] + data['0 1 0'] + data['0 0 1'] + data['0 1 1'],
'1': data['1 0 0'] + data['1 1 0'] + data['1 0 1'] + data['1 1 1']
}
self.log.info('test_teleport: circuit:')
self.log.info('test_teleport: circuit:')
self.log.info(circuit.qasm())
self.log.info('test_teleport: data %s', data)
self.log.info('test_teleport: alice %s', alice)

View File

@ -14,10 +14,10 @@ import pstats
import unittest
import numpy as np
from qiskit import (qasm, unroll, QuantumProgram, QuantumCircuit,
from qiskit import (qasm, unroll, QuantumCircuit,
QuantumRegister, ClassicalRegister, compile)
from qiskit.backends.local.unitary_simulator_py import UnitarySimulatorPy
from qiskit.qobj import Qobj, QobjItem
from qiskit.qobj import Qobj, QobjItem, QobjExperiment, QobjConfig, QobjHeader
from ._random_qasm_generator import RandomQasmGenerator
from .common import QiskitTestCase
@ -28,41 +28,30 @@ class LocalUnitarySimulatorTest(QiskitTestCase):
def setUp(self):
self.seed = 88
self.qasm_filename = self._get_resource_path('qasm/example.qasm')
self.qp = QuantumProgram()
def tearDown(self):
pass
def test_unitary_simulator(self):
"""test generation of circuit unitary"""
self.qp.load_qasm_file(self.qasm_filename, name='example')
basis_gates = [] # unroll to base gates
unroller = unroll.Unroller(
qasm.Qasm(data=self.qp.get_qasm('example')).parse(),
unroll.JsonBackend(basis_gates))
qasm.Qasm(filename=self.qasm_filename).parse(),
unroll.JsonBackend([]))
circuit = unroller.execute()
# strip measurements from circuit to avoid warnings
circuit['instructions'] = [op for op in circuit['instructions']
if op['name'] != 'measure']
# the simulator is expecting a JSON format, so we need to convert it
# back to JSON
qobj = {
'id': 'unitary',
'config': {
'max_credits': None,
'shots': 1
},
'experiments': [circuit],
'header': {'backend_name': 'local_unitary_simulator_py'}
}
qobj = Qobj.from_dict(qobj)
qobj.experiments[0].header.name = 'test'
qobj.experiments[0].header.compiled_circuit_qasm = self.qp.get_qasm('example')
qobj.experiments[0].config = QobjItem(coupling_map=None,
basis_gates=None,
layout=None,
seed=None)
circuit = QobjExperiment.from_dict(circuit)
circuit.config = QobjItem(coupling_map=None,
basis_gates=None,
layout=None,
seed=self.seed)
circuit.header.name = 'test'
qobj = Qobj(id='unitary',
config=QobjConfig(shots=1,
register_slots=6,
max_credits=None),
experiments=[circuit],
header=QobjHeader(
backend_name='local_unitary_simulator_py'))
# numpy.savetxt currently prints complex numbers in a way
# loadtxt can't read. To save file do,
# fmtstr=['% .4g%+.4gj' for i in range(numCols)]
@ -121,10 +110,10 @@ class LocalUnitarySimulatorTest(QiskitTestCase):
max_depth=max_depth,
max_qubits=max_qubits)
random_circuits.add_circuits(n_circuits, do_measure=False)
self.qp = random_circuits.get_program()
qp = random_circuits.get_program()
pr.enable()
self.qp.execute(self.qp.get_circuit_names(),
backend=UnitarySimulatorPy())
qp.execute(qp.get_circuit_names(),
backend=UnitarySimulatorPy())
pr.disable()
sout = io.StringIO()
ps = pstats.Stats(pr, stream=sout).sort_stats('cumulative')