Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dissasembler to dissassemble a compiled qobj #2137

Merged
merged 22 commits into from
Apr 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions qiskit/compiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
from .run_config import RunConfig
from .transpile_config import TranspileConfig
from .assembler import assemble_circuits, assemble_schedules
from .disassembler import disassemble
from .transpiler import transpile
92 changes: 92 additions & 0 deletions qiskit/compiler/disassembler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-

# Copyright 2019, 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=missing-return-type-doc

"""Disassemble function for a qobj into a list of circuits and it's config"""

from qiskit.circuit.classicalregister import ClassicalRegister
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.circuit.quantumregister import QuantumRegister


# TODO: This is broken for conditionals. Will fix after circuits_2_qobj pr
def _experiments_to_circuits(qobj):
"""Return a list of QuantumCircuit object(s) from a qobj

Args:
qobj (Qobj): The Qobj object to convert to QuantumCircuits
Returns:
list: A list of QuantumCircuit objects from the qobj

"""
if qobj.experiments:
circuits = []
for x in qobj.experiments:
quantum_registers = [QuantumRegister(i[1], name=i[0])
for i in x.header.qreg_sizes]
classical_registers = [ClassicalRegister(i[1], name=i[0])
for i in x.header.creg_sizes]
circuit = QuantumCircuit(*quantum_registers,
*classical_registers,
name=x.header.name)
qreg_dict = {}
creg_dict = {}
for reg in quantum_registers:
qreg_dict[reg.name] = reg
for reg in classical_registers:
creg_dict[reg.name] = reg
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]])
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)
elif i.name == 'initialize':
instr_method(params, qubits)
else:
instr_method(*params, *qubits, *clbits)
circuits.append(circuit)
return circuits
return None


def disassemble(qobj):
"""Dissasemble a qobj and return the circuits, run_config, and user header

Args:
qobj (Qobj): The input qobj object to dissasemble
Returns:
circuits (list): A list of quantum circuits
run_config (dict): The dist of the run config
user_qobj_header (dict): The dict of any user headers in the qobj

"""
run_config = qobj.config.to_dict()
user_qobj_header = qobj.header.to_dict()
circuits = _experiments_to_circuits(qobj)

return circuits, run_config, user_qobj_header
58 changes: 8 additions & 50 deletions qiskit/converters/qobj_to_circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@

"""Helper function for converting qobj to a list of circuits"""

from qiskit.circuit import QuantumCircuit, ClassicalRegister, QuantumRegister
import warnings

from qiskit.compiler import disassembler


# 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

Expand All @@ -20,51 +21,8 @@ def qobj_to_circuits(qobj):
list: A list of QuantumCircuit objects from the qobj

"""
if qobj.experiments:
circuits = []
for x in qobj.experiments:
quantum_registers = [QuantumRegister(i[1], name=i[0])
for i in x.header.qreg_sizes]
classical_registers = [ClassicalRegister(i[1], name=i[0])
for i in x.header.creg_sizes]
circuit = QuantumCircuit(*quantum_registers,
*classical_registers,
name=x.header.name)
qreg_dict = {}
creg_dict = {}
for reg in quantum_registers:
qreg_dict[reg.name] = reg
for reg in classical_registers:
creg_dict[reg.name] = reg
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]])
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)
elif i.name == 'initialize':
instr_method(params, qubits)
else:
instr_method(*params, *qubits, *clbits)
circuits.append(circuit)
return circuits
return None
warnings.warn('qiskit.converters.qobj_to_circuit() is deprecated and will '
'be removed in Qiskit Terra 0.9. Please use '
'qiskit.compiler.disassemble_circuits() to convert a qobj '
'to list of circuits.', DeprecationWarning)
return disassembler._experiments_to_circuits(qobj)
104 changes: 104 additions & 0 deletions test/python/compiler/test_disassembler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-

# Copyright 2019, 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.

"""Assembler Test."""

import unittest

import numpy as np

from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.compiler import assemble_circuits
from qiskit.compiler import disassemble
from qiskit.compiler import RunConfig
from qiskit.test import QiskitTestCase


class TestAssembler(QiskitTestCase):
"""Tests for assembling circuits to qobj."""

def test_disassemble_single_circuit(self):
"""Test assembling a single circuit.
"""
qr = QuantumRegister(2, name='q')
cr = ClassicalRegister(2, name='c')
circ = QuantumCircuit(qr, cr, name='circ')
circ.h(qr[0])
circ.cx(qr[0], qr[1])
circ.measure(qr, cr)

run_config = RunConfig(shots=2000, memory=True)
qobj = assemble_circuits(circ, run_config=run_config)
circuits, run_config_out, headers = disassemble(qobj)
self.assertEqual({'shots': 2000, 'memory': True, 'memory_slots': 2,
'n_qubits': 2}, run_config_out)
self.assertEqual(len(circuits), 1)
self.assertEqual(circuits[0], circ)
self.assertEqual({}, headers)

def test_disssemble_multiple_circuits(self):
"""Test assembling multiple circuits, all should have the same config.
"""
qr0 = QuantumRegister(2, name='q0')
qc0 = ClassicalRegister(2, name='c0')
circ0 = QuantumCircuit(qr0, qc0, name='circ0')
circ0.h(qr0[0])
circ0.cx(qr0[0], qr0[1])
circ0.measure(qr0, qc0)

qr1 = QuantumRegister(3, name='q1')
qc1 = ClassicalRegister(3, name='c1')
circ1 = QuantumCircuit(qr1, qc1, name='circ0')
circ1.h(qr1[0])
circ1.cx(qr1[0], qr1[1])
circ1.cx(qr1[0], qr1[2])
circ1.measure(qr1, qc1)

run_config = RunConfig(shots=100, memory=False, seed=6)
qobj = assemble_circuits([circ0, circ1], run_config=run_config)
circuits, out_run_config, headers = disassemble(qobj)
self.assertEqual({'shots': 100, 'memory': False, 'memory_slots': 3,
'n_qubits': 3, 'seed': 6}, out_run_config)
self.assertEqual(len(circuits), 2)
for circuit in circuits:
self.assertIn(circuit, [circ0, circ1])
self.assertEqual({}, headers)

def test_assemble_no_run_config(self):
"""Test assembling with no run_config, relying on default.
"""
qr = QuantumRegister(2, name='q')
qc = ClassicalRegister(2, name='c')
circ = QuantumCircuit(qr, qc, name='circ')
circ.h(qr[0])
circ.cx(qr[0], qr[1])
circ.measure(qr, qc)

qobj = assemble_circuits(circ)
circuits, run_config_out, headers = disassemble(qobj)
self.assertEqual({'memory_slots': 2, 'n_qubits': 2}, run_config_out)
self.assertEqual(len(circuits), 1)
self.assertEqual(circuits[0], circ)
self.assertEqual({}, headers)

def test_assemble_initialize(self):
"""Test assembling a circuit with an initialize.
"""
q = QuantumRegister(2, name='q')
circ = QuantumCircuit(q, name='circ')
circ.initialize([1/np.sqrt(2), 0, 0, 1/np.sqrt(2)], q[:])

qobj = assemble_circuits(circ)
circuits, run_config_out, header = disassemble(qobj)
self.assertEqual({'memory_slots': 0, 'n_qubits': 2}, run_config_out)
self.assertEqual(len(circuits), 1)
self.assertEqual(circuits[0], circ)
self.assertEqual({}, header)


if __name__ == '__main__':
unittest.main(verbosity=2)