Skip to content

Commit

Permalink
converters (Qiskit#1410)
Browse files Browse the repository at this point in the history
* Adding qobj converters

* Adding circuit_to_dag

* Tests

* Cleaning up

* Adding changes

* Adding more

* Lint

* Update test_circuit_operations.py

* Cleaning up
  • Loading branch information
jaygambetta authored and ajavadia committed Dec 5, 2018
1 parent e8d60bb commit 6525615
Show file tree
Hide file tree
Showing 18 changed files with 309 additions and 249 deletions.
60 changes: 16 additions & 44 deletions qiskit/_quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@
from copy import deepcopy
import itertools
import warnings
import random
import string
import networkx as nx


from qiskit.qasm import _qasm
from qiskit._qiskiterror import QiskitError
from qiskit._quantumregister import QuantumRegister
from qiskit._classicalregister import ClassicalRegister
from qiskit.dagcircuit import DAGCircuit


class QuantumCircuit(object):
Expand Down Expand Up @@ -81,7 +77,9 @@ def __str__(self):
return str(self.draw(output='text'))

def __eq__(self, other):
return DAGCircuit.fromQuantumCircuit(self) == DAGCircuit.fromQuantumCircuit(other)
# TODO: removed the DAG from this function
from qiskit.converters import circuit_to_dag
return circuit_to_dag(self) == circuit_to_dag(other)

@classmethod
def _increment_instances(cls):
Expand Down Expand Up @@ -346,17 +344,21 @@ def draw(self, scale=0.7, filename=None, style=None, output='text',

def size(self):
"""Return total number of operations in circuit."""
dag = DAGCircuit.fromQuantumCircuit(self)
# TODO: removed the DAG from this function
from qiskit.converters import circuit_to_dag
dag = circuit_to_dag(self)
return dag.size()

def depth(self):
"""Return circuit depth (i.e. length of critical path)."""
dag = DAGCircuit.fromQuantumCircuit(self)
from qiskit.converters import circuit_to_dag
dag = circuit_to_dag(self)
return dag.depth()

def width(self):
"""Return number of qubits in circuit."""
dag = DAGCircuit.fromQuantumCircuit(self)
from qiskit.converters import circuit_to_dag
dag = circuit_to_dag(self)
return dag.width()

def count_ops(self):
Expand All @@ -365,12 +367,14 @@ def count_ops(self):
Returns:
dict: a breakdown of how many operations of each kind.
"""
dag = DAGCircuit.fromQuantumCircuit(self)
from qiskit.converters import circuit_to_dag
dag = circuit_to_dag(self)
return dag.count_ops()

def num_tensor_factors(self):
"""How many non-entangled subcircuits can the circuit be factored to."""
dag = DAGCircuit.fromQuantumCircuit(self)
from qiskit.converters import circuit_to_dag
dag = circuit_to_dag(self)
return dag.num_tensor_factors()

@staticmethod
Expand All @@ -397,43 +401,11 @@ def from_qasm_str(qasm_str):
qasm = _qasm.Qasm(data=qasm_str)
return _circuit_from_qasm(qasm)

@staticmethod
def fromDAGCircuit(dag):
"""Build a ``QuantumCircuit`` object from a ``DAGCircuit``.
Args:
dag (DAGCircuit): the input dag.
Return:
QuantumCircuit: the circuit representing the input dag.
"""
circuit = QuantumCircuit()
random_name = QuantumCircuit.cls_prefix() + \
str(''.join(random.choice(string.ascii_lowercase) for i in range(8)))
circuit.name = dag.name or random_name
for qreg in dag.qregs.values():
circuit.add_register(qreg)
for creg in dag.cregs.values():
circuit.add_register(creg)
graph = dag.multi_graph
for node in nx.topological_sort(graph):
n = graph.nodes[node]
if n['type'] == 'op':
op = deepcopy(n['op'])
op.qargs = n['qargs']
op.cargs = n['cargs']
op.circuit = circuit
if 'condition' in n and n['condition']:
op = op.c_if(*n['condition'])
circuit._attach(op)

return circuit


def _circuit_from_qasm(qasm):
from qiskit.unroll import Unroller
from qiskit.unroll import DAGBackend
from qiskit.converters import dag_to_circuit
ast = qasm.parse()
dag = Unroller(ast, DAGBackend()).execute()

return QuantumCircuit.fromDAGCircuit(dag)
return dag_to_circuit(dag)
18 changes: 18 additions & 0 deletions qiskit/converters/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# -*- 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.

# pylint: disable=redefined-builtin

"""Helper module for simplified Qiskit usage.
The functions in this module provide convenience converters
"""

from .qobj_to_circuits import qobj_to_circuits
from .circuits_to_qobj import circuits_to_qobj
from .circuit_to_dag import circuit_to_dag
from .dag_to_circuit import dag_to_circuit
82 changes: 82 additions & 0 deletions qiskit/converters/circuit_to_dag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# -*- 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.

"""Helper function for converting a circuit to a dag"""

import copy

from qiskit import _compositegate
from qiskit.dagcircuit._dagcircuit import DAGCircuit


def circuit_to_dag(circuit, expand_gates=True):
"""Build a ``DAGCircuit`` object from a ``QuantumCircuit``.
Args:
circuit (QuantumCircuit): the input circuit.
expand_gates (bool): if ``False``, none of the gates are expanded,
i.e. the gates that are defined in the circuit are included in
the DAG basis.
Return:
DAGCircuit: the DAG representing the input circuit.
"""
circuit = copy.deepcopy(circuit)

dagcircuit = DAGCircuit()
dagcircuit.name = circuit.name
for register in circuit.qregs:
dagcircuit.add_qreg(register)
for register in circuit.cregs:
dagcircuit.add_creg(register)
# Add user gate definitions
for name, data in circuit.definitions.items():
dagcircuit.add_basis_element(name, data["n_bits"], 0, data["n_args"])
dagcircuit.add_gate_data(name, data)
# Add instructions
builtins = {
"U": ["U", 1, 0, 3],
"CX": ["CX", 2, 0, 0],
"measure": ["measure", 1, 1, 0],
"reset": ["reset", 1, 0, 0],
"barrier": ["barrier", -1, 0, 0]
}
# Add simulator instructions
simulator_instructions = {
"snapshot": ["snapshot", -1, 0, 1],
"save": ["save", -1, 0, 1],
"load": ["load", -1, 0, 1],
"noise": ["noise", -1, 0, 1]
}
for main_instruction in circuit.data:
# TODO: generate definitions and nodes for CompositeGates,
# for now simply drop their instructions into the DAG
instruction_list = []
is_composite = isinstance(main_instruction,
_compositegate.CompositeGate)
if is_composite and expand_gates:
instruction_list = main_instruction.instruction_list()
else:
instruction_list.append(main_instruction)

for instruction in instruction_list:
# Add OpenQASM built-in gates on demand
if instruction.name in builtins:
dagcircuit.add_basis_element(*builtins[instruction.name])
# Add simulator extension instructions
if instruction.name in simulator_instructions:
dagcircuit.add_basis_element(*simulator_instructions[instruction.name])
# Get arguments for classical control (if any)
if instruction.control is None:
control = None
else:
control = (instruction.control[0], instruction.control[1])

dagcircuit.apply_operation_back(instruction, instruction.qargs,
instruction.cargs, control)

return dagcircuit
106 changes: 106 additions & 0 deletions qiskit/converters/circuits_to_qobj.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# -*- 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.

"""Helper function for converting a list of circuits to a qobj"""
from copy import deepcopy
import uuid

from qiskit.qobj import Qobj, QobjConfig, QobjExperiment, QobjItem, QobjHeader
from qiskit._quantumcircuit import QuantumCircuit


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):
"""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
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
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})

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

if isinstance(circuits, QuantumCircuit):
circuits = [circuits]

for circuit in circuits:
qobj.experiments.append(_circuit_to_experiment(circuit,
config,
basis_gates,
coupling_map))

# Update the global `memory_slots` and `n_qubits` values.
qobj.config.memory_slots = max(experiment.config.memory_slots for
experiment in qobj.experiments)

qobj.config.n_qubits = max(experiment.config.n_qubits for
experiment in qobj.experiments)

return qobj


def _circuit_to_experiment(circuit, config=None, basis_gates=None,
coupling_map=None):
"""Helper function for dags to qobj in parallel (if available).
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
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)

# 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
46 changes: 46 additions & 0 deletions qiskit/converters/dag_to_circuit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# -*- 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.

"""Helper function for converting a dag to a circuit"""
from copy import deepcopy
import random
import string
import networkx as nx

from qiskit._quantumcircuit import QuantumCircuit


def dag_to_circuit(dag):
"""Build a ``QuantumCircuit`` object from a ``DAGCircuit``.
Args:
dag (DAGCircuit): the input dag.
Return:
QuantumCircuit: the circuit representing the input dag.
"""
circuit = QuantumCircuit()
random_name = QuantumCircuit.cls_prefix() + \
str(''.join(random.choice(string.ascii_lowercase) for i in range(8)))
circuit.name = dag.name or random_name
for qreg in dag.qregs.values():
circuit.add_register(qreg)
for creg in dag.cregs.values():
circuit.add_register(creg)
graph = dag.multi_graph
for node in nx.topological_sort(graph):
n = graph.nodes[node]
if n['type'] == 'op':
op = deepcopy(n['op'])
op.qargs = n['qargs']
op.cargs = n['cargs']
op.circuit = circuit
if 'condition' in n and n['condition']:
op = op.c_if(*n['condition'])
circuit._attach(op)

return circuit
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
# 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 loading all the circuits from a Qobj"""
from qiskit import _quantumcircuit as qc
"""Helper function for converting qobj to a list of circuits"""
from qiskit._quantumcircuit import QuantumCircuit


def qobj_to_circuits(qobj):
Expand All @@ -23,7 +23,7 @@ def qobj_to_circuits(qobj):
for x in qobj.experiments:
if hasattr(x.header, 'compiled_circuit_qasm'):
circuits.append(
qc.QuantumCircuit.from_qasm_str(x.header.compiled_circuit_qasm))
QuantumCircuit.from_qasm_str(x.header.compiled_circuit_qasm))
return circuits
# TODO(mtreinish): add support for converting a qobj if the qasm isn't
# embedded in the header
Expand Down
Loading

0 comments on commit 6525615

Please sign in to comment.