# -*- 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.

"""
Pass for detecting commutativity in a circuit.

Property_set['commutation_set'] is a dictionary that describes
the commutation relations on a given wire, all the gates on a wire
are grouped into a set of gates that commute.

This pass also provides useful methods to determine if two gates
can commute in the circuit.

TODO: the current pass determines commutativity through matrix multiplication.
A rule-based analysis would be potentially faster, but more limited.
"""

from collections import defaultdict
import numpy as np
from qiskit.transpiler.exceptions import TranspilerError

from qiskit.transpiler.basepasses import AnalysisPass

_CUTOFF_PRECISION = 1E-10

class CommutationAnalysis(AnalysisPass):
    """An analysis pass to find commutation relations between DAG nodes."""

    def __init__(self):
        super().__init__()
        self.gates_on_wire = {}

    def run(self, dag):
        """
        Run the pass on the DAG, and write the discovered commutation relations
        into the property_set.
        """
        # Initiate the commutation set
        self.property_set['commutation_set'] = defaultdict(list)

        # Build a dictionary to keep track of the gates on each qubit
        for wire in dag.wires:
            wire_name = "{0}[{1}]".format(str(wire[0].name), str(wire[1]))
            self.property_set['commutation_set'][wire_name] = []

        # Add edges to the dictionary for each qubit
        for node in dag.topological_op_nodes():
            for (_, _, edge_data) in dag.edges(node):

                edge_name = edge_data['name']
                self.property_set['commutation_set'][(node, edge_name)] = -1

        for wire in dag.wires:
            wire_name = "{0}[{1}]".format(str(wire[0].name), str(wire[1]))

            for current_gate in dag.nodes_on_wire(wire):

                current_comm_set = self.property_set['commutation_set'][wire_name]
                if not current_comm_set:
                    current_comm_set.append([current_gate])

                if current_gate not in current_comm_set[-1]:
                    prev_gate = current_comm_set[-1][-1]
                    if _commute(current_gate, prev_gate):
                        current_comm_set[-1].append(current_gate)

                    else:
                        current_comm_set.append([current_gate])

                temp_len = len(current_comm_set)
                self.property_set['commutation_set'][(current_gate, wire_name)] = temp_len - 1

def _gate_master_def(name, params=None):
    # pylint: disable=too-many-return-statements

    if name == 'h':
        return 1. / np.sqrt(2) * np.array([[1.0, 1.0],
                                           [1.0, -1.0]], dtype=np.complex)
    if name == 'x':
        return np.array([[0.0, 1.0],
                         [1.0, 0.0]], dtype=np.complex)
    if name == 'y':
        return np.array([[0.0, -1.0j],
                         [1.0j, 0.0]], dtype=np.complex)
    if name == 'cx':
        return np.array([[1.0, 0.0, 0.0, 0.0],
                         [0.0, 1.0, 0.0, 0.0],
                         [0.0, 0.0, 0.0, 1.0],
                         [0.0, 0.0, 1.0, 0.0]], dtype=np.complex)
    if name == 'cz':
        return np.array([[1.0, 0.0, 0.0, 0.0],
                         [0.0, 1.0, 0.0, 0.0],
                         [0.0, 0.0, 1.0, 0.0],
                         [0.0, 0.0, 0.0, -1.0]], dtype=np.complex)
    if name == 'cy':
        return np.array([[1.0, 0.0, 0.0, 0.0],
                         [0.0, 1.0, 0.0, 0.0],
                         [0.0, 0.0, 0.0, 1.0j],
                         [0.0, 0.0, -1.0j, 0.0]], dtype=np.complex)
    if name == 'z':
        return np.array([[1.0, 0.0],
                         [0.0, -1.0]], dtype=np.complex)
    if name == 't':
        return np.array([[1.0, 0.0],
                         [0.0, np.exp(1j * np.pi / 4.0)]], dtype=np.complex)
    if name == 's':
        return np.array([[1.0, 0.0],
                         [0.0, np.exp(1j * np.pi / 2.0)]], dtype=np.complex)
    if name == 'sdag':
        return np.array([[1.0, 0.0],
                         [0.0, -np.exp(1j * np.pi / 2.0)]], dtype=np.complex)
    if name == 'tdag':
        return np.array([[1.0, 0.0],
                         [0.0, -np.exp(1j * np.pi / 4.0)]], dtype=np.complex)
    if name in ('rz', 'u1'):
        return np.array([[np.exp(-1j * float(params[0]) / 2), 0],
                         [0, np.exp(1j * float(params[0]) / 2)]], dtype=np.complex)
    if name == 'rx':
        return np.array([[np.cos(float(params[0]) / 2), -1j * np.sin(float(params[0]) / 2)],
                         [-1j * np.sin(float(params[0]) / 2), np.cos(float(params[0]) / 2)]],
                        dtype=np.complex)
    if name == 'ry':
        return np.array([[np.cos(float(params[0]) / 2), - np.sin(float(params[0]) / 2)],
                         [np.sin(float(params[0]) / 2), np.cos(float(params[0]) / 2)]],
                        dtype=np.complex)
    if name == 'u2':
        return 1. / np.sqrt(2) * np.array(
            [[1, -np.exp(1j * float(params[1]))],
             [np.exp(1j * float(params[0])), np.exp(1j * (float(params[0]) + float(params[1])))]],
            dtype=np.complex)

    if name == 'u3':
        return 1./np.sqrt(2) * np.array(
            [[np.cos(float(params[0]) / 2.),
              -np.exp(1j * float(params[2])) * np.sin(float(params[0]) / 2.)],
             [np.exp(1j * float(params[1])) * np.sin(float(params[0]) / 2.),
              np.cos(float(params[0]) / 2.) * np.exp(1j * (float(params[2]) + float(params[1])))]],
            dtype=np.complex)

    if name == 'P0':
        return np.array([[1.0, 0.0], [0.0, 0.0]], dtype=np.complex)

    if name == 'P1':
        return np.array([[0.0, 0.0], [0.0, 1.0]], dtype=np.complex)

    if name == 'Id':
        return np.identity(2)

    raise TranspilerError("The gate %s isn't supported" % name)

def _calc_product(node1, node2):

    wire_num = len(set(node1.qargs + node2.qargs))
    wires = sorted(list(map(lambda x: "{0}[{1}]".format(str(x[0].name), str(x[1])),
                            list(set(node1.qargs + node2.qargs)))))
    final_unitary = np.identity(2 ** wire_num, dtype=np.complex)

    for node in [node1, node2]:

        qstate_list = [np.identity(2)] * wire_num

        if node.name in ['cx', 'cy', 'cz']:

            qstate_list_ext = [np.identity(2)] * wire_num

            node_ctrl = "{0}[{1}]".format(str(node.qargs[0][0].name), str(node.qargs[0][1]))
            node_tgt = "{0}[{1}]".format(str(node.qargs[1][0].name), str(node.qargs[1][1]))
            ctrl = wires.index(node_ctrl)
            tgt = wires.index(node_tgt)

            qstate_list[ctrl] = _gate_master_def(name='P0')
            qstate_list[tgt] = _gate_master_def(name='Id')
            qstate_list_ext[ctrl] = _gate_master_def(name='P1')
            if node.name == 'cx':
                qstate_list_ext[tgt] = _gate_master_def(name='x')
            if node.name == 'cy':
                qstate_list_ext[tgt] = _gate_master_def(name='y')
            if node.name == 'cz':
                qstate_list_ext[tgt] = _gate_master_def(name='z')

            rt_list = [qstate_list] + [qstate_list_ext]

        else:

            mat = _gate_master_def(name=node.name, params=node.op.params)
            node_num = "{0}[{1}]".format(str(node.qargs[0][0].name),
                                         str(node.qargs[0][1]))
            qstate_list[wires.index(node_num)] = mat

            rt_list = [qstate_list]

        crt = np.zeros([2 ** wire_num, 2 ** wire_num])

        for state in rt_list:
            crt = crt + _kron_list(state)

        final_unitary = np.dot(crt, final_unitary)
    return final_unitary

def _kron_list(args):
    ret = args[0]
    for item in args[1:]:
        ret = np.kron(ret, item)
    return ret

def _matrix_commute(node1, node2):
    # Good for composite gates or any future
    # user-defined gate of equal or less than 2 qubits.
    ret = False
    if set(node1.qargs) & set(node2.qargs) == set():
        ret = True
    if _calc_product(node1, node2) is not None:
        ret = np.allclose(_calc_product(node1, node2),
                          _calc_product(node2, node1),
                          atol=_CUTOFF_PRECISION)
    return ret

def _commute(node1, node2):
    if node1.type != "op" or node2.type != "op":
        return False
    return _matrix_commute(node1, node2)


# -*- 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.

"""A module for monitoring backends."""

import time
import threading
import types
from IPython.display import display                              # pylint: disable=import-error
from IPython.core.magic import line_magic, Magics, magics_class  # pylint: disable=import-error
from IPython.core import magic_arguments                         # pylint: disable=import-error
import matplotlib.pyplot as plt                                  # pylint: disable=import-error
import ipywidgets as widgets                                     # pylint: disable=import-error
from qiskit.tools.monitor.backend_overview import get_unique_backends
from qiskit.visualization.gate_map import plot_gate_map

@magics_class
class BackendOverview(Magics):
    """A class of status magic functions.
    """
    @line_magic
    @magic_arguments.magic_arguments()
    @magic_arguments.argument(
        '-i',
        '--interval',
        type=float,
        default=60,
        help='Interval for status check.'
    )
    def qiskit_backend_overview(self, line='', cell=None):  # pylint: disable=W0613
        """A Jupyter magic function to monitor backends.
        """
        args = magic_arguments.parse_argstring(
            self.qiskit_backend_overview, line)

        unique_hardware_backends = get_unique_backends()
        _value = "
Backend Overview
"
        backend_title = widgets.HTML(value=_value,
                                     layout=widgets.Layout(margin='0px 0px 0px 0px'))

        build_back_widgets = [backend_widget(b)
                              for b in unique_hardware_backends]

        _backends = []
        # Sort backends by operational or not
        oper_ord_backends = []
        for n, back in enumerate(unique_hardware_backends):
            if back.status().operational:
                oper_ord_backends = [build_back_widgets[n]] + oper_ord_backends
                _backends = [back] + _backends
            else:
                oper_ord_backends = oper_ord_backends + [build_back_widgets[n]]
                _backends = _backends + [back]

        qubit_label = widgets.Label(value='Num. Qubits')
        pend_label = widgets.Label(value='Pending Jobs')
        least_label = widgets.Label(value='Least Busy')
        oper_label = widgets.Label(
            value='Operational', layout=widgets.Layout(margin='5px 0px 0px 0px'))
        t1_label = widgets.Label(
            value='Avg. T1', layout=widgets.Layout(margin='10px 0px 0px 0px'))
        t2_label = widgets.Label(
            value='Avg. T2', layout=widgets.Layout(margin='10px 0px 0px 0px'))

        labels_widget = widgets.VBox([qubit_label, pend_label, least_label,
                                      oper_label, t1_label, t2_label],
                                     layout=widgets.Layout(margin='295px 0px 0px 0px',
                                                           min_width='100px'))

        backend_grid = GridBox_with_thread(children=oper_ord_backends,
                                           layout=widgets.Layout(
                                               grid_template_columns='250px ' *
                                               len(unique_hardware_backends),
                                               grid_template_rows='auto',
                                               grid_gap='0px 25px'))

        backend_grid._backends = _backends        # pylint: disable=W0201
        backend_grid._update = types.MethodType(  # pylint: disable=W0201
            update_backend_info, backend_grid)

        backend_grid._thread = threading.Thread(  # pylint: disable=W0201
            target=backend_grid._update, args=(args.interval,))
        backend_grid._thread.start()

        back_box = widgets.HBox([labels_widget, backend_grid])

        back_monitor = widgets.VBox([backend_title, back_box])
        display(back_monitor)

class GridBox_with_thread(widgets.GridBox):  # pylint: disable=invalid-name
    """A GridBox that will close an attached thread
    """
    def __del__(self):
        """Object disposal"""
        if hasattr(self, '_thread'):
            try:
                self._thread.do_run = False
                self._thread.join()
            except Exception:  # pylint: disable=W0703
                pass
        self.close()

def backend_widget(backend):
    """Creates a backend widget.
    """
    config = backend.configuration().to_dict()
    props = backend.properties().to_dict()

    name = widgets.HTML(value="
{name}
".format(name=backend.name()),
                        layout=widgets.Layout())

    n_qubits = config['n_qubits']

    qubit_count = widgets.HTML(value="
{qubits}
".format(qubits=n_qubits),
                               layout=widgets.Layout(justify_content='center'))

    cmap = widgets.Output(layout=widgets.Layout(min_width='250px', max_width='250px',
                                                max_height='250px',
                                                min_height='250px',
                                                justify_content='center',
                                                align_items='center',
                                                margin='0px 0px 0px 0px'))

    with cmap:
        _cmap_fig = plot_gate_map(backend,
                                  plot_directed=False,
                                  label_qubits=False)
        if _cmap_fig is not None:
            display(_cmap_fig)
            # Prevents plot from showing up twice.
            plt.close(_cmap_fig)

    pending = generate_jobs_pending_widget()

    is_oper = widgets.HTML(value="

",
                           layout=widgets.Layout(justify_content='center'))

    least_busy = widgets.HTML(value="

",
                              layout=widgets.Layout(justify_content='center'))

    t1_units = props['qubits'][0][0]['unit']
    avg_t1 = round(sum([q[0]['value'] for q in props['qubits']])/n_qubits, 1)
    t1_widget = widgets.HTML(value="
{t1} {units}
".format(t1=avg_t1, units=t1_units),
                             layout=widgets.Layout())

    t2_units = props['qubits'][0][1]['unit']
    avg_t2 = round(sum([q[1]['value'] for q in props['qubits']])/n_qubits, 1)
    t2_widget = widgets.HTML(value="
{t2} {units}
".format(t2=avg_t2, units=t2_units),
                             layout=widgets.Layout())

    out = widgets.VBox([name, cmap, qubit_count, pending,
                        least_busy, is_oper, t1_widget, t2_widget],
                       layout=widgets.Layout(display='inline-flex',
                                             flex_flow='column',
                                             align_items='center'))

    out._is_alive = True
    return out

def update_backend_info(self, interval=60):
    """Updates the monitor info
    Called from another thread.
    """
    my_thread = threading.currentThread()
    current_interval = 0
    started = False
    all_dead = False
    stati = [None]*len(self._backends)
    while getattr(my_thread, "do_run", True) and not all_dead:
        if current_interval == interval or started is False:
            for ind, back in enumerate(self._backends):
                _value = self.children[ind].children[2].value
                _head = _value.split('')[0]
                try:
                    _status = back.status()
                    stati[ind] = _status
                except Exception:  # pylint: disable=W0703
                    self.children[ind].children[2].value = _value.replace(
                        _head, "
")
                    self.children[ind]._is_alive = False
                else:
                    self.children[ind]._is_alive = True
                    self.children[ind].children[2].value = _value.replace(
                        _head, "
")

            idx = list(range(len(self._backends)))
            pending = [s.pending_jobs for s in stati]
            _, least_idx = zip(*sorted(zip(pending, idx)))

            # Make sure least pending is operational
            for ind in least_idx:
                if stati[ind].operational:
                    least_pending_idx = ind
                    break

            for var in idx:
                if var == least_pending_idx:
                    self.children[var].children[4].value = "
True
"
                else:
                    self.children[var].children[4].value = "
False
"

                self.children[var].children[3].children[1].value = pending[var]
                self.children[var].children[3].children[1].max = max(
                    self.children[var].children[3].children[1].max, pending[var]+10)
                if stati[var].operational:
                    self.children[var].children[5].value = "
True
"
                else:
                    self.children[var].children[5].value = "
False
"

            started = True
            current_interval = 0
        time.sleep(1)
        all_dead = not any([wid._is_alive for wid in self.children])
        current_interval += 1

def generate_jobs_pending_widget():
    """Generates a jobs_pending progress bar widget.
    """
    pbar = widgets.IntProgress(
        value=0,
        min=0,
        max=50,
        description='',
        orientation='horizontal', layout=widgets.Layout(max_width='180px'))
    pbar.style.bar_color = '#71cddd'

    pbar_current = widgets.Label(
        value=str(pbar.value), layout=widgets.Layout(min_width='auto'))
    pbar_max = widgets.Label(
        value=str(pbar.max), layout=widgets.Layout(min_width='auto'))

    def _on_max_change(change):
        pbar_max.value = str(change['new'])

    def _on_val_change(change):
        pbar_current.value = str(change['new'])

    pbar.observe(_on_max_change, names='max')
    pbar.observe(_on_val_change, names='value')

    jobs_widget = widgets.HBox([pbar_current, pbar, pbar_max],
                               layout=widgets.Layout(max_width='250px',
                                                     min_width='250px',
                                                     justify_content='center'))

    return jobs_widget


# -*- 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.

"""
The CX direction rearrenges the direction of the cx nodes to make the circuit
compatible with the coupling_map.
"""

from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError

from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler import Layout
from qiskit.extensions.standard import HGate

class CXDirection(TransformationPass):
    """
     Rearranges the direction of the cx nodes to make the circuit
     compatible with the directed coupling map.

     It uses this equivalence::

        ---(+)---      --[H]---.---[H]--
            |      =           |
        ----.----      --[H]--(+)--[H]--
    """

    def __init__(self, coupling_map, initial_layout=None):
        """
        Args:
            coupling_map (CouplingMap): Directed graph represented a coupling map.
            initial_layout (Layout): The initial layout of the DAG.
        """

        super().__init__()
        self.coupling_map = coupling_map
        self.layout = initial_layout

    def run(self, dag):
        """
        Flips the cx nodes to match the directed coupling map.
        Args:
            dag (DAGCircuit): DAG to map.
        Returns:
            DAGCircuit: The rearranged dag for the coupling map

        Raises:
            TranspilerError: If the circuit cannot be mapped just by flipping the
                cx nodes.
        """
        new_dag = DAGCircuit()

        if self.layout is None:
            # LegacySwap renames the register in the DAG and does not match the property set
            self.layout = Layout.generate_trivial_layout(*dag.qregs.values())

        for layer in dag.serial_layers():
            subdag = layer['graph']

            for cnot_node in subdag.named_nodes('cx', 'CX'):
                control = cnot_node.qargs[0]
                target = cnot_node.qargs[1]

                physical_q0 = self.layout[control]
                physical_q1 = self.layout[target]
                if self.coupling_map.distance(physical_q0, physical_q1) != 1:
                    raise TranspilerError('The circuit requires a connection between physical '
                                          'qubits %s and %s' % (physical_q0, physical_q1))

                if (physical_q0, physical_q1) not in self.coupling_map.get_edges():
                    # A flip needs to be done

                    # Create the involved registers
                    if control[0] not in subdag.qregs.values():
                        subdag.add_qreg(control[0])
                    if target[0] not in subdag.qregs.values():
                        subdag.add_qreg(target[0])

                    # Add H gates around
                    subdag.apply_operation_back(HGate(), [target], [])
                    subdag.apply_operation_back(HGate(), [control], [])
                    subdag.apply_operation_front(HGate(), [target], [])
                    subdag.apply_operation_front(HGate(), [control], [])

                    # Flips the CX
                    cnot_node.qargs[0], cnot_node.qargs[1] = target, control

            new_dag.extend_back(subdag)

        return new_dag


# -*- 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.

"""Pass for peep-hole cancellation of consecutive CX gates.
"""
from qiskit.transpiler.basepasses import TransformationPass

class CXCancellation(TransformationPass):
    """Cancel back-to-back 'cx' gates in dag."""

    def run(self, dag):
        """
        Run one pass of cx cancellation on the circuit

        Args:
            dag (DAGCircuit): the directed acyclic graph to run on.
        Returns:
            DAGCircuit: Transformed DAG.
        """
        cx_runs = dag.collect_runs(["cx"])
        for cx_run in cx_runs:
            # Partition the cx_run into chunks with equal gate arguments
            partition = []
            chunk = []
            for i in range(len(cx_run) - 1):
                chunk.append(cx_run[i])

                qargs0 = cx_run[i].qargs
                qargs1 = cx_run[i + 1].qargs

                if qargs0 != qargs1:
                    partition.append(chunk)
                    chunk = []
            chunk.append(cx_run[-1])
            partition.append(chunk)
            # Simplify each chunk in the partition
            for chunk in partition:
                if len(chunk) % 2 == 0:
                    for n in chunk:
                        dag.remove_op_node(n)
                else:
                    for n in chunk[1:]:
                        dag.remove_op_node(n)
        return dag


# -*- 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=redundant-returns-doc,missing-raises-doc

"""Base class for a backend provider."""

from abc import ABC, abstractmethod
import logging

from .exceptions import QiskitBackendNotFoundError

logger = logging.getLogger(__name__)

class BaseProvider(ABC):
    """
    Base class for a backend provider.
    """
    def __init__(self, *args, **kwargs):
        pass

    def get_backend(self, name=None, **kwargs):
        """Return a single backend matching the specified filtering.

        Args:
            name (str): name of the backend.
            **kwargs (dict): dict used for filtering.

        Returns:
            BaseBackend: a backend matching the filtering.

        Raises:
            QiskitBackendNotFoundError: if no backend could be found or
                more than one backend matches.
        """
        backends = self.backends(name, **kwargs)
        if len(backends) > 1:
            raise QiskitBackendNotFoundError('More than one backend matches the criteria')
        elif not backends:
            raise QiskitBackendNotFoundError('No backend matches the criteria')

        return backends[0]

    @abstractmethod
    def backends(self, name=None, **kwargs):
        """Return a list of backends matching the specified filtering.

        Args:
            name (str): name of the backend.
            **kwargs (dict): dict used for filtering.

        Returns:
            list[BaseBackend]: a list of backends matching the filtering
                criteria.
        """
        pass

    def __eq__(self, other):
        """Equality comparison.

        By default, it is assumed that two `Providers` from the same class are
        equal. Subclassed providers can override this behavior.
        """
        return type(self).__name__ == type(other).__name__


# -*- 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.
"""
Choi-matrix representation of a Quantum Channel.

For a quantum channel E, the Choi matrix Λ is defined by:
Λ = sum_{i,j} |i⟩⟨j|⊗E(|i⟩⟨j|)

Evolution of a density matrix with respect to the Choi-matrix is given by:

    E(ρ) = Tr_{1}[Λ.(ρ^T⊗I)]

See [1] for further details.

References:
    [1] C.J. Wood, J.D. Biamonte, D.G. Cory, Quant. Inf. Comp. 15, 0579-0811 (2015)
        Open access: arXiv:1111.6950 [quant-ph]
"""

from numbers import Number

import numpy as np

from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.circuit.instruction import Instruction
from qiskit.qiskiterror import QiskitError
from qiskit.quantum_info.operators.channel.quantum_channel import QuantumChannel
from qiskit.quantum_info.operators.channel.superop import SuperOp
from qiskit.quantum_info.operators.channel.transformations import _to_choi
from qiskit.quantum_info.operators.channel.transformations import _bipartite_tensor

class Choi(QuantumChannel):
    """Choi-matrix representation of a quantum channel"""

    def __init__(self, data, input_dims=None, output_dims=None):
        """Initialize a quantum channel Choi matrix operator.

        Args:
            data (QuantumCircuit or
                  Instruction or
                  BaseOperator or
                  matrix): data to initialize superoperator.
            input_dims (tuple): the input subsystem dimensions.
                                [Default: None]
            output_dims (tuple): the output subsystem dimensions.
                                 [Default: None]

        Raises:
            QiskitError: if input data cannot be initialized as a
            Choi matrix.

        Additional Information
        ----------------------
        If the input or output dimensions are None, they will be
        automatically determined from the input data. If the input data is
        a Numpy array of shape (4**N, 4**N) qubit systems will be used. If
        the input operator is not an N-qubit operator, it will assign a
        single subsystem with dimension specifed by the shape of the input.
        """
        # If the input is a raw list or matrix we assume that it is
        # already a Choi matrix.
        if isinstance(data, (list, np.ndarray)):
            # Initialize from raw numpy or list matrix.
            choi_mat = np.array(data, dtype=complex)
            # Determine input and output dimensions
            dim_l, dim_r = choi_mat.shape
            if dim_l != dim_r:
                raise QiskitError('Invalid Choi-matrix input.')
            if input_dims:
                input_dim = np.product(input_dims)
            if output_dims:
                output_dim = np.product(output_dims)
            if output_dims is None and input_dims is None:
                output_dim = int(np.sqrt(dim_l))
                input_dim = dim_l // output_dim
            elif input_dims is None:
                input_dim = dim_l // output_dim
            elif output_dims is None:
                output_dim = dim_l // input_dim
            # Check dimensions
            if input_dim * output_dim != dim_l:
                raise QiskitError("Invalid shape for input Choi-matrix.")
        else:
            # Otherwise we initialize by conversion from another Qiskit
            # object into the QuantumChannel.
            if isinstance(data, (QuantumCircuit, Instruction)):
                # If the input is a Terra QuantumCircuit or Instruction we
                # convert it to a SuperOp
                data = SuperOp._instruction_to_superop(data)
            else:
                # We use the QuantumChannel init transform to intialize
                # other objects into a QuantumChannel or Operator object.
                data = self._init_transformer(data)
            input_dim, output_dim = data.dim
            # Now that the input is an operator we convert it to a Choi object
            choi_mat = _to_choi(data.rep, data._data, input_dim, output_dim)
            if input_dims is None:
                input_dims = data.input_dims()
            if output_dims is None:
                output_dims = data.output_dims()
        # Check and format input and output dimensions
        input_dims = self._automatic_dims(input_dims, input_dim)
        output_dims = self._automatic_dims(output_dims, output_dim)
        super().__init__('Choi', choi_mat, input_dims, output_dims)

    @property
    def _bipartite_shape(self):
        """Return the shape for bipartite matrix"""
        return (self._input_dim, self._output_dim, self._input_dim,
                self._output_dim)

    def conjugate(self):
        """Return the conjugate of the QuantumChannel."""
        return Choi(np.conj(self._data), self.input_dims(), self.output_dims())

    def transpose(self):
        """Return the transpose of the QuantumChannel."""
        # Make bipartite matrix
        d_in, d_out = self.dim
        data = np.reshape(self._data, (d_in, d_out, d_in, d_out))
        # Swap input and output indicies on bipartite matrix
        data = np.transpose(data, (1, 0, 3, 2))
        # Transpose channel has input and output dimensions swapped
        data = np.reshape(data, (d_in * d_out, d_in * d_out))
        return Choi(
            data, input_dims=self.output_dims(), output_dims=self.input_dims())

    def compose(self, other, qargs=None, front=False):
        """Return the composition channel self∘other.

        Args:
            other (QuantumChannel): a quantum channel.
            qargs (list): a list of subsystem positions to compose other on.
            front (bool): If False compose in standard order other(self(input))
                          otherwise compose in reverse order self(other(input))
                          [default: False]

        Returns:
            Choi: The composition channel as a Choi object.

        Raises:
            QiskitError: if other cannot be converted to a channel or
            has incompatible dimensions.
        """
        if qargs is not None:
            return Choi(
                SuperOp(self).compose(other, qargs=qargs, front=front))

        # Convert to Choi matrix
        if not isinstance(other, Choi):
            other = Choi(other)
        # Check dimensions match up
        if front and self._input_dim != other._output_dim:
            raise QiskitError(
                'input_dim of self must match output_dim of other')
        if not front and self._output_dim != other._input_dim:
            raise QiskitError(
                'input_dim of other must match output_dim of self')

        if front:
            first = np.reshape(other._data, other._bipartite_shape)
            second = np.reshape(self._data, self._bipartite_shape)
            input_dim = other._input_dim
            input_dims = other.input_dims()
            output_dim = self._output_dim
            output_dims = self.output_dims()
        else:
            first = np.reshape(self._data, self._bipartite_shape)
            second = np.reshape(other._data, other._bipartite_shape)
            input_dim = self._input_dim
            input_dims = self.input_dims()
            output_dim = other._output_dim
            output_dims = other.output_dims()

        # Contract Choi matrices for composition
        data = np.reshape(
            np.einsum('iAjB,AkBl->ikjl', first, second),
            (input_dim * output_dim, input_dim * output_dim))
        return Choi(data, input_dims, output_dims)

    def power(self, n):
        """The matrix power of the channel.

        Args:
            n (int): compute the matrix power of the superoperator matrix.

        Returns:
            Choi: the matrix power of the SuperOp converted to a Choi channel.

        Raises:
            QiskitError: if the input and output dimensions of the
            QuantumChannel are not equal, or the power is not an integer.
        """
        if n > 0:
            return super().power(n)
        return Choi(SuperOp(self).power(n))

    def tensor(self, other):
        """Return the tensor product channel self ⊗ other.

        Args:
            other (QuantumChannel): a quantum channel.

        Returns:
            Choi: the tensor product channel self ⊗ other as a Choi object.

        Raises:
            QiskitError: if other cannot be converted to a channel.
        """
        return self._tensor_product(other, reverse=False)

    def expand(self, other):
        """Return the tensor product channel other ⊗ self.

        Args:
            other (QuantumChannel): a quantum channel.

        Returns:
            Choi: the tensor product channel other ⊗ self as a Choi object.

        Raises:
            QiskitError: if other cannot be converted to a channel.
        """
        return self._tensor_product(other, reverse=True)

    def add(self, other):
        """Return the QuantumChannel self + other.

        Args:
            other (QuantumChannel): a quantum channel.

        Returns:
            Choi: the linear addition self + other as a Choi object.

        Raises:
            QiskitError: if other cannot be converted to a channel or
            has incompatible dimensions.
        """
        if not isinstance(other, Choi):
            other = Choi(other)
        if self.dim != other.dim:
            raise QiskitError("other QuantumChannel dimensions are not equal")
        return Choi(self._data + other.data, self._input_dims,
                    self._output_dims)

    def subtract(self, other):
        """Return the QuantumChannel self - other.

        Args:
            other (QuantumChannel): a quantum channel.

        Returns:
            Choi: the linear subtraction self - other as Choi object.

        Raises:
            QiskitError: if other cannot be converted to a channel or
            has incompatible dimensions.
        """
        if not isinstance(other, Choi):
            other = Choi(other)
        if self.dim != other.dim:
            raise QiskitError("other QuantumChannel dimensions are not equal")
        return Choi(self._data - other.data, self._input_dims,
                    self._output_dims)

    def multiply(self, other):
        """Return the QuantumChannel self + other.

        Args:
            other (complex): a complex number.

        Returns:
            Choi: the scalar multiplication other * self as a Choi object.

        Raises:
            QiskitError: if other is not a valid scalar.
        """
        if not isinstance(other, Number):
            raise QiskitError("other is not a number")
        return Choi(other * self._data, self._input_dims, self._output_dims)

    def _evolve(self, state, qargs=None):
        """Evolve a quantum state by the QuantumChannel.

        Args:
            state (QuantumState): The input statevector or density matrix.
            qargs (list): a list of QuantumState subsystem positions to apply
                           the operator on.

        Returns:
            DensityMatrix: the output quantum state as a density matrix.

        Raises:
            QiskitError: if the operator dimension does not match the
            specified QuantumState subsystem dimensions.
        """
        # If subsystem evolution we use the SuperOp representation
        if qargs is not None:
            return SuperOp(self)._evolve(state, qargs)
        # Otherwise we compute full evolution directly
        state = self._format_state(state, density_matrix=True)
        if state.shape[0] != self._input_dim:
            raise QiskitError(
                "QuantumChannel input dimension is not equal to state dimension."
            )
        return np.einsum('AB,AiBj->ij', state,
                         np.reshape(self._data, self._bipartite_shape))

    def _tensor_product(self, other, reverse=False):
        """Return the tensor product channel.

        Args:
            other (QuantumChannel): a quantum channel.
            reverse (bool): If False return self ⊗ other, if True return
                            if True return (other ⊗ self) [Default: False
        Returns:
            Choi: the tensor product channel as a Choi object.

        Raises:
            QiskitError: if other is not a QuantumChannel subclass.
        """
        # Convert other to Choi
        if not isinstance(other, Choi):
            other = Choi(other)

        if reverse:
            input_dims = self.input_dims() + other.input_dims()
            output_dims = self.output_dims() + other.output_dims()
            data = _bipartite_tensor(
                other.data,
                self._data,
                shape1=other._bipartite_shape,
                shape2=self._bipartite_shape)
        else:
            input_dims = other.input_dims() + self.input_dims()
            output_dims = other.output_dims() + self.output_dims()
            data = _bipartite_tensor(
                self._data,
                other.data,
                shape1=self._bipartite_shape,
                shape2=other._bipartite_shape)
        return Choi(data, input_dims, output_dims)


# -*- 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=invalid-name,anomalous-backslash-in-string,missing-docstring
# pylint: disable=consider-using-enumerate

"""latex circuit visualization backends."""

import collections
import io
import itertools
import json
import math
import operator
import re

import numpy as np

from qiskit.visualization import exceptions
from qiskit.visualization import qcstyle as _qcstyle

class QCircuitImage:
    """This class contains methods to create \\LaTeX circuit images.

    The class targets the \\LaTeX package Q-circuit
    (https://arxiv.org/pdf/quant-ph/0406003).

    Thanks to Eric Sabo for the initial implementation for Qiskit.
    """

    def __init__(self, qregs, cregs, ops, scale, style=None,
                 plot_barriers=True, reverse_bits=False):
        """
        Args:
            qregs (list): A list of tuples for the quantum registers
            cregs (list): A list of tuples for the classical registers
            ops (list): A list of dicts where each entry is a operation from
                the circuit.
            scale (float): image scaling
            style (dict or str): dictionary of style or file name of style file
            reverse_bits (bool): When set to True reverse the bit order inside
               registers for the output visualization.
            plot_barriers (bool): Enable/disable drawing barriers in the output
               circuit. Defaults to True.
        """
        # style sheet
        self._style = _qcstyle.QCStyle()
        if style:
            if isinstance(style, dict):
                self._style.set_style(style)
            elif isinstance(style, str):
                with open(style, 'r') as infile:
                    dic = json.load(infile)
                self._style.set_style(dic)

        # list of lists corresponding to layers of the circuit
        self.ops = ops

        # image scaling
        self.scale = scale

        # Map of qregs to sizes
        self.qregs = {}

        # Map of cregs to sizes
        self.cregs = {}

        # List of qregs and cregs in order of appearance in code and image
        self.ordered_regs = []

        # Map from registers to the list they appear in the image
        self.img_regs = {}

        # Array to hold the \\LaTeX commands to generate a circuit image.
        self._latex = []

        # Variable to hold image depth (width)
        self.img_depth = 0

        # Variable to hold image width (height)
        self.img_width = 0

        # Variable to hold total circuit depth
        self.sum_column_widths = 0

        # Variable to hold total circuit width
        self.sum_row_heights = 0

        # em points of separation between circuit columns
        self.column_separation = 0.5

        # em points of separation between circuit row
        self.row_separation = 0.0

        # presence of "box" or "target" determines row spacing
        self.has_box = False
        self.has_target = False
        self.reverse_bits = reverse_bits
        self.plot_barriers = plot_barriers

        #################################
        self.qregs = collections.OrderedDict(_get_register_specs(qregs))
        self.qubit_list = qregs
        self.ordered_regs = qregs + cregs
        self.cregs = collections.OrderedDict(_get_register_specs(cregs))
        self.clbit_list = cregs
        self.img_regs = {bit: ind for ind, bit in
                         enumerate(self.ordered_regs)}
        self.img_width = len(self.img_regs)
        self.wire_type = {}
        for key, value in self.ordered_regs:
            self.wire_type[(key, value)] = key in self.cregs.keys()

    def latex(self, aliases=None):
        """Return LaTeX string representation of circuit.

        This method uses the LaTeX Qconfig package to create a graphical
        representation of the circuit.

        Returns:
            string: for writing to a LaTeX file.
        """
        self._initialize_latex_array(aliases)
        self._build_latex_array(aliases)
        header_1 = r"""% \documentclass[preview]{standalone}
% If the image is too large to fit on this documentclass use
\documentclass[draft]{beamer}
"""
        beamer_line = "\\usepackage[size=custom,height=%d,width=%d,scale=%.1f]{beamerposter}\n"
        header_2 = r"""% instead and customize the height and width (in cm) to fit.
% Large images may run out of memory quickly.
% To fix this use the LuaLaTeX compiler, which dynamically
% allocates memory.
\usepackage[braket, qm]{qcircuit}
\usepackage{amsmath}
\pdfmapfile{+sansmathaccent.map}
% \usepackage[landscape]{geometry}
% Comment out the above line if using the beamer documentclass.
\begin{document}
\begin{equation*}"""
        qcircuit_line = r"""
    \Qcircuit @C=%.1fem @R=%.1fem @!R {
"""
        output = io.StringIO()
        output.write(header_1)
        output.write('%% img_width = %d, img_depth = %d\n' % (self.img_width, self.img_depth))
        output.write(beamer_line % self._get_beamer_page())
        output.write(header_2)
        output.write(qcircuit_line %
                     (self.column_separation, self.row_separation))
        for i in range(self.img_width):
            output.write("\t \t")
            for j in range(self.img_depth + 1):
                cell_str = self._latex[i][j]
                # Don't truncate offset float if drawing a barrier
                if 'barrier' in cell_str:
                    output.write(cell_str)
                else:
                    # floats can cause "Dimension too large" latex error in
                    # xymatrix this truncates floats to avoid issue.
                    cell_str = re.sub(r'[-+]?\d*\.\d{2,}|\d{2,}',
                                      _truncate_float,
                                      cell_str)
                    output.write(cell_str)
                if j != self.img_depth:
                    output.write(" & ")
                else:
                    output.write(r'\\' + '\n')
        output.write('\t }\n')
        output.write('\\end{equation*}\n\n')
        output.write('\\end{document}')
        contents = output.getvalue()
        output.close()
        return contents

    def _initialize_latex_array(self, aliases=None):
        # pylint: disable=unused-argument
        self.img_depth, self.sum_column_widths = self._get_image_depth()
        self.sum_row_heights = self.img_width
        # choose the most compact row spacing, while not squashing them
        if self.has_box:
            self.row_separation = 0.0
        elif self.has_target:
            self.row_separation = 0.2
        else:
            self.row_separation = 1.0
        self._latex = [
            ["\\cw" if self.wire_type[self.ordered_regs[j]]
             else "\\qw" for _ in range(self.img_depth + 1)]
            for j in range(self.img_width)]
        self._latex.append([" "] * (self.img_depth + 1))
        for i in range(self.img_width):
            if self.wire_type[self.ordered_regs[i]]:
                self._latex[i][0] = "\\lstick{" + self.ordered_regs[i][0].name + \
                                    "_{" + str(self.ordered_regs[i][1]) + "}" + \
                                    ": 0}"
            else:
                self._latex[i][0] = "\\lstick{" + \
                                    self.ordered_regs[i][0].name + "_{" + \
                                    str(self.ordered_regs[i][1]) + "}" + \
                                    ": \\ket{0}}"

    def _get_image_depth(self):
        """Get depth information for the circuit.

        Returns:
            int: number of columns in the circuit
            int: total size of columns in the circuit
        """

        max_column_widths = []

        for layer in self.ops:

            # store the max width for the layer
            current_max = 0

            for op in layer:

                # update current op width
                arg_str_len = 0

                # the wide gates
                for arg in op.op.params:
                    arg_str = re.sub(r'[-+]?\d*\.\d{2,}|\d{2,}',
                                     _truncate_float, str(arg))
                    arg_str_len += len(arg_str)

                # the width of the column is the max of all the gates in the column
                current_max = max(arg_str_len, current_max)

            max_column_widths.append(current_max)

        # wires in the beginning and end
        columns = 2
        # each layer is one column
        columns += len(self.ops)

        # every 3 characters is roughly one extra 'unit' of width in the cell
        # the gate name is 1 extra 'unit'
        # the qubit/cbit labels plus initial states is 2 more
        # the wires poking out at the ends is 2 more
        sum_column_widths = sum(1 + v / 3 for v in max_column_widths)

        # could be a fraction so ceil
        return columns, math.ceil(sum_column_widths) + 4

    def _get_beamer_page(self):
        """Get height, width & scale attributes for the beamer page.

        Returns:
            tuple: (height, width, scale) desirable page attributes
        """
        # PIL python package limits image size to around a quarter gigabyte
        # this means the beamer image should be limited to < 50000
        # if you want to avoid a "warning" too, set it to < 25000
        PIL_limit = 40000

        # the beamer latex template limits each dimension to < 19 feet
        # (i.e. 575cm)
        beamer_limit = 550

        # columns are roughly twice as big as rows
        aspect_ratio = self.sum_row_heights / self.sum_column_widths

        # choose a page margin so circuit is not cropped
        margin_factor = 1.5
        height = min(self.sum_row_heights * margin_factor, beamer_limit)
        width = min(self.sum_column_widths * margin_factor, beamer_limit)

        # if too large, make it fit
        if height * width > PIL_limit:
            height = min(np.sqrt(PIL_limit * aspect_ratio), beamer_limit)
            width = min(np.sqrt(PIL_limit / aspect_ratio), beamer_limit)

        # if too small, give it a minimum size
        height = max(height, 10)
        width = max(width, 10)

        return (height, width, self.scale)

    def _get_mask(self, creg_name):
        mask = 0
        for index, cbit in enumerate(self.clbit_list):
            if creg_name == cbit[0]:
                mask |= (1 << index)
        return mask

    def _build_latex_array(self, aliases=None):
        """Returns an array of strings containing \\LaTeX for this circuit.

        If aliases is not None, aliases contains a dict mapping
        the current qubits in the circuit to new qubit names.
        We will deduce the register names and sizes from aliases.
        """
        columns = 1

        # Rename qregs if necessary
        if aliases:
            qregdata = {}
            for q in aliases.values():
                if q[0] not in qregdata:
                    qregdata[q[0]] = q[1] + 1
                elif qregdata[q[0]] < q[1] + 1:
                    qregdata[q[0]] = q[1] + 1
        else:
            qregdata = self.qregs

        for column, layer in enumerate(self.ops, 1):
            for op in layer:
                if op.condition:
                    mask = self._get_mask(op.condition[0])
                    cl_reg = self.clbit_list[self._ffs(mask)]
                    if_reg = cl_reg[0]
                    pos_2 = self.img_regs[cl_reg]
                    if_value = format(op.condition[1],
                                      'b').zfill(self.cregs[if_reg])[::-1]
                if op.name not in ['measure', 'barrier', 'snapshot', 'load',
                                   'save', 'noise']:
                    nm = op.name
                    qarglist = op.qargs
                    if aliases is not None:
                        qarglist = map(lambda x: aliases[x], qarglist)
                    if len(qarglist) == 1:
                        pos_1 = self.img_regs[(qarglist[0][0],
                                               qarglist[0][1])]

                        if op.condition:
                            mask = self._get_mask(op.condition[0])
                            cl_reg = self.clbit_list[self._ffs(mask)]
                            if_reg = cl_reg[0]
                            pos_2 = self.img_regs[cl_reg]

                            if nm == "x":
                                self._latex[pos_1][column] = "\\gate{X}"
                            elif nm == "y":
                                self._latex[pos_1][column] = "\\gate{Y}"
                            elif nm == "z":
                                self._latex[pos_1][column] = "\\gate{Z}"
                            elif nm == "h":
                                self._latex[pos_1][column] = "\\gate{H}"
                            elif nm == "s":
                                self._latex[pos_1][column] = "\\gate{S}"
                            elif nm == "sdg":
                                self._latex[pos_1][column] = "\\gate{S^\\dag}"
                            elif nm == "t":
                                self._latex[pos_1][column] = "\\gate{T}"
                            elif nm == "tdg":
                                self._latex[pos_1][column] = "\\gate{T^\\dag}"
                            elif nm == "u0":
                                self._latex[pos_1][column] = "\\gate{U_0(%s)}" % (
                                    op.op.params[0])
                            elif nm == "u1":
                                self._latex[pos_1][column] = "\\gate{U_1(%s)}" % (
                                    op.op.params[0])
                            elif nm == "u2":
                                self._latex[pos_1][column] = \
                                    "\\gate{U_2\\left(%s,%s\\right)}" % (
                                        op.op.params[0], op.op.params[1])
                            elif nm == "u3":
                                self._latex[pos_1][column] = ("\\gate{U_3(%s,%s,%s)}" % (
                                    op.op.params[0],
                                    op.op.params[1],
                                    op.op.params[2]))
                            elif nm == "rx":
                                self._latex[pos_1][column] = "\\gate{R_x(%s)}" % (
                                    op.op.params[0])
                            elif nm == "ry":
                                self._latex[pos_1][column] = "\\gate{R_y(%s)}" % (
                                    op.op.params[0])
                            elif nm == "rz":
                                self._latex[pos_1][column] = "\\gate{R_z(%s)}" % (
                                    op.op.params[0])
                            else:
                                self._latex[pos_1][columns] = "\\gate{%s}" % nm

                            gap = pos_2 - pos_1
                            for i in range(self.cregs[if_reg]):
                                if if_value[i] == '1':
                                    self._latex[pos_2 + i][column] = \
                                        "\\control \\cw \\cwx[-" + str(gap) + "]"
                                    gap = 1
                                else:
                                    self._latex[pos_2 + i][column] = \
                                        "\\controlo \\cw \\cwx[-" + str(gap) + "]"
                                    gap = 1

                        else:
                            if nm == "x":
                                self._latex[pos_1][column] = "\\gate{X}"
                            elif nm == "y":
                                self._latex[pos_1][column] = "\\gate{Y}"
                            elif nm == "z":
                                self._latex[pos_1][column] = "\\gate{Z}"
                            elif nm == "h":
                                self._latex[pos_1][column] = "\\gate{H}"
                            elif nm == "s":
                                self._latex[pos_1][column] = "\\gate{S}"
                            elif nm == "sdg":
                                self._latex[pos_1][column] = "\\gate{S^\\dag}"
                            elif nm == "t":
                                self._latex[pos_1][column] = "\\gate{T}"
                            elif nm == "tdg":
                                self._latex[pos_1][column] = "\\gate{T^\\dag}"
                            elif nm == "u0":
                                self._latex[pos_1][column] = "\\gate{U_0(%s)}" % (
                                    op.op.params[0])
                            elif nm == "u1":
                                self._latex[pos_1][column] = "\\gate{U_1(%s)}" % (
                                    op.op.params[0])
                            elif nm == "u2":
                                self._latex[pos_1][column] = \
                                    "\\gate{U_2\\left(%s,%s\\right)}" % (
                                        op.op.params[0], op.op.params[1])
                            elif nm == "u3":
                                self._latex[pos_1][column] = ("\\gate{U_3(%s,%s,%s)}" % (
                                    op.op.params[0],
                                    op.op.params[1],
                                    op.op.params[2]))
                            elif nm == "rx":
                                self._latex[pos_1][column] = "\\gate{R_x(%s)}" % (
                                    op.op.params[0])
                            elif nm == "ry":
                                self._latex[pos_1][column] = "\\gate{R_y(%s)}" % (
                                    op.op.params[0])
                            elif nm == "rz":
                                self._latex[pos_1][column] = "\\gate{R_z(%s)}" % (
                                    op.op.params[0])
                            elif nm == "reset":
                                self._latex[pos_1][column] = (
                                    "\\push{\\rule{.6em}{0em}\\ket{0}\\"
                                    "rule{.2em}{0em}} \\qw")
                            else:
                                self._latex[pos_1][columns] = "\\gate{%s}" % nm

                    elif len(qarglist) == 2:
                        pos_1 = self.img_regs[(qarglist[0][0], qarglist[0][1])]
                        pos_2 = self.img_regs[(qarglist[1][0], qarglist[1][1])]

                        if op.condition:
                            pos_3 = self.img_regs[(if_reg, 0)]
                            temp = [pos_1, pos_2, pos_3]
                            temp.sort(key=int)
                            bottom = temp[1]

                            gap = pos_3 - bottom
                            for i in range(self.cregs[if_reg]):
                                if if_value[i] == '1':
                                    self._latex[pos_3 + i][column] = \
                                        "\\control \\cw \\cwx[-" + str(gap) + "]"
                                    gap = 1
                                else:
                                    self._latex[pos_3 + i][column] = \
                                        "\\controlo \\cw \\cwx[-" + str(gap) + "]"
                                    gap = 1

                            if nm == "cx":
                                self._latex[pos_1][column] = \
                                    "\\ctrl{" + str(pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = "\\targ"
                            elif nm == "cz":
                                self._latex[pos_1][column] = \
                                    "\\ctrl{" + str(pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = "\\control\\qw"
                            elif nm == "cy":
                                self._latex[pos_1][column] = \
                                    "\\ctrl{" + str(pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = "\\gate{Y}"
                            elif nm == "ch":
                                self._latex[pos_1][column] = \
                                    "\\ctrl{" + str(pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = "\\gate{H}"
                            elif nm == "swap":
                                self._latex[pos_1][column] = "\\qswap"
                                self._latex[pos_2][column] = \
                                    "\\qswap \\qwx[" + str(pos_1 - pos_2) + "]"
                            elif nm == "crz":
                                self._latex[pos_1][column] = \
                                    "\\ctrl{" + str(pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = \
                                    "\\gate{R_z(%s)}" % (op.op.params[0])
                            elif nm == "cu1":
                                self._latex[pos_1][column - 1] = "\\ctrl{" + str(
                                    pos_2 - pos_1) + "}"
                                self._latex[pos_2][column - 1] = "\\control\\qw"
                                self._latex[min(pos_1, pos_2)][column] = \
                                    "\\dstick{%s}\\qw" % (op.op.params[0])
                                self._latex[max(pos_1, pos_2)][column] = "\\qw"
                            elif nm == "cu3":
                                self._latex[pos_1][column] = \
                                    "\\ctrl{" + str(pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = \
                                    "\\gate{U_3(%s,%s,%s)}" % (op.op.params[0],
                                                               op.op.params[1],
                                                               op.op.params[2])
                        else:
                            temp = [pos_1, pos_2]
                            temp.sort(key=int)

                            if nm == "cx":
                                self._latex[pos_1][column] = "\\ctrl{" + str(
                                    pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = "\\targ"
                            elif nm == "cz":
                                self._latex[pos_1][column] = "\\ctrl{" + str(
                                    pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = "\\control\\qw"
                            elif nm == "cy":
                                self._latex[pos_1][column] = "\\ctrl{" + str(
                                    pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = "\\gate{Y}"
                            elif nm == "ch":
                                self._latex[pos_1][column] = "\\ctrl{" + str(
                                    pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = "\\gate{H}"
                            elif nm == "swap":
                                self._latex[pos_1][column] = "\\qswap"
                                self._latex[pos_2][column] = \
                                    "\\qswap \\qwx[" + str(pos_1 - pos_2) + "]"
                            elif nm == "crz":
                                self._latex[pos_1][column] = "\\ctrl{" + str(
                                    pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = \
                                    "\\gate{R_z(%s)}" % (op.op.params[0])
                            elif nm == "cu1":
                                self._latex[pos_1][column - 1] = "\\ctrl{" + str(
                                    pos_2 - pos_1) + "}"
                                self._latex[pos_2][column - 1] = "\\control\\qw"
                                self._latex[min(pos_1, pos_2)][column] = \
                                    "\\dstick{%s}\\qw" % (op.op.params[0])
                                self._latex[max(pos_1, pos_2)][column] = "\\qw"
                            elif nm == "cu3":
                                self._latex[pos_1][column] = "\\ctrl{" + str(
                                    pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = ("\\gate{U_3(%s,%s,%s)}" % (
                                    op.op.params[0],
                                    op.op.params[1],
                                    op.op.params[2]))
                            else:
                                start_pos = min([pos_1, pos_2])
                                stop_pos = max([pos_1, pos_2])
                                if stop_pos - start_pos >= 2:
                                    delta = stop_pos - start_pos
                                    self._latex[start_pos][columns] = (
                                        "\\multigate{%s}{%s}" % (delta, nm))
                                    for i_pos in range(start_pos + 1, stop_pos + 1):
                                        self._latex[i_pos][columns] = "\\ghost{%s}" % nm
                                else:
                                    self._latex[start_pos][columns] = (
                                        "\\multigate{1}{%s}" % nm)
                                    self._latex[stop_pos][columns] = "\\ghost{%s}" % nm

                    elif len(qarglist) == 3:
                        pos_1 = self.img_regs[(qarglist[0][0], qarglist[0][1])]
                        pos_2 = self.img_regs[(qarglist[1][0], qarglist[1][1])]
                        pos_3 = self.img_regs[(qarglist[2][0], qarglist[2][1])]

                        if op.condition:
                            pos_4 = self.img_regs[(if_reg, 0)]

                            temp = [pos_1, pos_2, pos_3, pos_4]
                            temp.sort(key=int)
                            bottom = temp[2]

                            prev_column = [x[column - 1] for x in self._latex]
                            for item, prev_entry in enumerate(prev_column):
                                if 'barrier' in prev_entry:
                                    span = re.search('barrier{(.*)}', prev_entry)
                                    if span and any(i in temp for i in range(
                                            item, int(span.group(1)))):
                                        self._latex[item][column - 1] = \
                                            prev_entry.replace(
                                                '\\barrier{',
                                                '\\barrier[-0.65em]{')

                            gap = pos_4 - bottom
                            for i in range(self.cregs[if_reg]):
                                if if_value[i] == '1':
                                    self._latex[pos_4 + i][column] = \
                                        "\\control \\cw \\cwx[-" + str(gap) + "]"
                                    gap = 1
                                else:
                                    self._latex[pos_4 + i][column] = \
                                        "\\controlo \\cw \\cwx[-" + str(gap) + "]"
                                    gap = 1

                            if nm == "ccx":
                                self._latex[pos_1][column] = "\\ctrl{" + str(
                                    pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = "\\ctrl{" + str(
                                    pos_3 - pos_2) + "}"
                                self._latex[pos_3][column] = "\\targ"

                            if nm == "cswap":
                                self._latex[pos_1][column] = "\\ctrl{" + str(
                                    pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = "\\qswap"
                                self._latex[pos_3][column] = \
                                    "\\qswap \\qwx[" + str(pos_2 - pos_3) + "]"
                        else:
                            temp = [pos_1, pos_2, pos_3]
                            temp.sort(key=int)

                            prev_column = [x[column - 1] for x in self._latex]
                            for item, prev_entry in enumerate(prev_column):
                                if 'barrier' in prev_entry:
                                    span = re.search('barrier{(.*)}', prev_entry)
                                    if span and any(i in temp for i in range(
                                            item, int(span.group(1)))):
                                        self._latex[item][column - 1] = \
                                            prev_entry.replace(
                                                '\\barrier{',
                                                '\\barrier[-0.65em]{')

                            if nm == "ccx":
                                self._latex[pos_1][column] = "\\ctrl{" + str(
                                    pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = "\\ctrl{" + str(
                                    pos_3 - pos_2) + "}"
                                self._latex[pos_3][column] = "\\targ"

                            elif nm == "cswap":
                                self._latex[pos_1][column] = "\\ctrl{" + str(
                                    pos_2 - pos_1) + "}"
                                self._latex[pos_2][column] = "\\qswap"
                                self._latex[pos_3][column] = \
                                    "\\qswap \\qwx[" + str(pos_2 - pos_3) + "]"
                            else:
                                start_pos = min([pos_1, pos_2, pos_3])
                                stop_pos = max([pos_1, pos_2, pos_3])
                                if stop_pos - start_pos >= 3:
                                    delta = stop_pos - start_pos
                                    self._latex[start_pos][columns] = (
                                        "\\multigate{%s}{%s}" % (delta, nm))
                                    for i_pos in range(start_pos + 1, stop_pos + 1):
                                        self._latex[i_pos][columns] = "\\ghost{%s}" % nm
                                else:
                                    self._latex[pos_1][columns] = (
                                        "\\multigate{2}{%s}" % nm)
                                    self._latex[pos_2][columns] = "\\ghost{%s}" % nm
                                    self._latex[pos_3][columns] = "\\ghost{%s}" % nm

                    elif len(qarglist) > 3:
                        nbits = len(qarglist)
                        pos_array = [self.img_regs[(qarglist[0][0],
                                                    qarglist[0][1])]]
                        for i in range(1, nbits):
                            pos_array.append(self.img_regs[(qarglist[i][0],
                                                            qarglist[i][1])])
                        pos_start = min(pos_array)
                        pos_stop = max(pos_array)
                        delta = pos_stop - pos_start
                        self._latex[pos_start][columns] = (
                            "\\multigate{%s}{%s}" % (nbits - 1, nm))
                        for pos in range(pos_start + 1, pos_stop + 1):
                            self._latex[pos][columns] = "\\ghost{%s}" % nm

                elif op.name == "measure":
                    if (len(op.cargs) != 1
                            or len(op.qargs) != 1
                            or op.op.params):
                        raise exceptions.VisualizationError("bad operation record")

                    if op.condition:
                        raise exceptions.VisualizationError(
                            "If controlled measures currently not supported.")

                    qname, qindex = op.qargs[0]
                    cname, cindex = op.cargs[0]
                    if aliases:
                        newq = aliases[(qname, qindex)]
                        qname = newq[0]
                        qindex = newq[1]

                    pos_1 = self.img_regs[(qname, qindex)]
                    pos_2 = self.img_regs[(cname, cindex)]

                    try:
                        self._latex[pos_1][column] = "\\meter"
                        prev_column = [x[column - 1] for x in self._latex]
                        for item, prev_entry in enumerate(prev_column):
                            if 'barrier' in prev_entry:
                                span = re.search('barrier{(.*)}', prev_entry)
                                if span and (
                                        item + int(span.group(1))) - pos_1 >= 0:
                                    self._latex[item][column - 1] = \
                                        prev_entry.replace(
                                            '\\barrier{',
                                            '\\barrier[-1.15em]{')

                        self._latex[pos_2][column] = \
                            "\\cw \\cwx[-" + str(pos_2 - pos_1) + "]"
                    except Exception as e:
                        raise exceptions.VisualizationError(
                            'Error during Latex building: %s' % str(e))

                elif op.name in ['barrier', 'snapshot', 'load', 'save',
                                 'noise']:
                    if self.plot_barriers:
                        qarglist = op.qargs
                        indexes = [self._get_qubit_index(x) for x in qarglist]
                        start_bit = self.qubit_list[min(indexes)]
                        if aliases is not None:
                            qarglist = map(lambda x: aliases[x], qarglist)
                        start = self.img_regs[start_bit]
                        span = len(op.qargs) - 1

                        self._latex[start][column] = "\\qw \\barrier{" + str(
                            span) + "}"
                else:
                    raise exceptions.VisualizationError("bad node data")

    def _get_qubit_index(self, qubit):
        """Get the index number for a quantum bit
        Args:
            qubit (tuple): The tuple of the bit of the form
                (register_name, bit_number)
        Returns:
            int: The index in the bit list
        Raises:
            VisualizationError: If the bit isn't found
        """
        for i, bit in enumerate(self.qubit_list):
            if qubit == bit:
                qindex = i
                break
        else:
            raise exceptions.VisualizationError("unable to find bit for operation")
        return qindex

    def _ffs(self, mask):
        """Find index of first set bit.

        Args:
            mask (int): integer to search
        Returns:
            int: index of the first set bit.
        """
        origin = (mask & (-mask)).bit_length()
        return origin - 1

def _get_register_specs(bit_labels):
    """Get the number and size of unique registers from bit_labels list.

    Args:
        bit_labels (list): this list is of the form::

            [['reg1', 0], ['reg1', 1], ['reg2', 0]]

            which indicates a register named "reg1" of size 2
            and a register named "reg2" of size 1. This is the
            format of classic and quantum bit labels in qobj
            header.

    Yields:
        tuple: iterator of register_name:size pairs.
    """
    it = itertools.groupby(bit_labels, operator.itemgetter(0))
    for register_name, sub_it in it:
        yield register_name, max(ind[1] for ind in sub_it) + 1

def _truncate_float(matchobj, format_str='0.2g'):
    """Truncate long floats

    Args:
        matchobj (re.Match): contains original float
        format_str (str): format specifier
    Returns:
       str: returns truncated float
    """
    if matchobj.group(0):
        return format(float(matchobj.group(0)), format_str)
    return ''


# -*- 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.

"""Validation module for validation against JSON schemas."""

import json
import os
import logging
import jsonschema

from .exceptions import SchemaValidationError, _SummaryValidationError

logger = logging.getLogger(__name__)

_DEFAULT_SCHEMA_PATHS = {
    'backend_configuration': 'schemas/backend_configuration_schema.json',
    'backend_properties': 'schemas/backend_properties_schema.json',
    'backend_status': 'schemas/backend_status_schema.json',
    'default_pulse_configuration': 'schemas/default_pulse_configuration_schema.json',
    'job_status': 'schemas/job_status_schema.json',
    'qobj': 'schemas/qobj_schema.json',
    'result': 'schemas/result_schema.json'}
# Schema and Validator storage
_SCHEMAS = {}
_VALIDATORS = {}

def _load_schema(file_path, name=None):
    """Loads the QObj schema for use in future validations.

   Caches schema in _SCHEMAS module attribute.

   Args:
        file_path(str): Path to schema.
        name(str): Given name for schema. Defaults to file_path filename
            without schema.
   Return:
        schema(dict): Loaded schema.
    """
    if name is None:
        # filename without extension
        name = os.path.splitext(os.path.basename(file_path))[0]
    if name not in _SCHEMAS:
        with open(file_path, 'r') as schema_file:
            _SCHEMAS[name] = json.load(schema_file)

    return _SCHEMAS[name]

def _get_validator(name, schema=None, check_schema=True,
                   validator_class=None, **validator_kwargs):
    """Generate validator for JSON schema.

    Args:
        name (str): Name for validator. Will be validator key in
            `_VALIDATORS` dict.
        schema (dict): JSON schema `dict`. If not provided searches for schema
            in `_SCHEMAS`.
        check_schema (bool): Verify schema is valid.
        validator_class (jsonschema.IValidator): jsonschema IValidator instance.
            Default behavior is to determine this from the schema `$schema`
            field.
        **validator_kwargs (dict): Additional keyword arguments for validator.

    Return:
        jsonschema.IValidator: Validator for JSON schema.

    Raises:
        SchemaValidationError: Raised if validation fails.
    """
    if schema is None:
        try:
            schema = _SCHEMAS[name]
        except KeyError:
            raise SchemaValidationError("Valid schema name or schema must "
                                        "be provided.")

    if name not in _VALIDATORS:

        # Resolve JSON spec from schema if needed
        if validator_class is None:
            validator_class = jsonschema.validators.validator_for(schema)

        # Generate and store validator in _VALIDATORS
        _VALIDATORS[name] = validator_class(schema, **validator_kwargs)

    validator = _VALIDATORS[name]

    if check_schema:
        validator.check_schema(schema)

    return validator

def _load_schemas_and_validators():
    """Load all default schemas into `_SCHEMAS`."""
    schema_base_path = os.path.join(os.path.dirname(__file__), '../..')
    for name, path in _DEFAULT_SCHEMA_PATHS.items():
        _load_schema(os.path.join(schema_base_path, path), name)
        _get_validator(name)

# Load all schemas on import
_load_schemas_and_validators()

def validate_json_against_schema(json_dict, schema,
                                 err_msg=None):
    """Validates JSON dict against a schema.

    Args:
        json_dict (dict): JSON to be validated.
        schema (dict or str): JSON schema dictionary or the name of one of the
            standards schemas in Qiskit to validate against it. The list of
            standard schemas is: ``backend_configuration``,
            ``backend_properties``, ``backend_status``,
            ``default_pulse_configuration``, ``job_status``, ``qobj``,
            ``result``.
        err_msg (str): Optional error message.

    Raises:
        SchemaValidationError: Raised if validation fails.
    """

    try:
        if isinstance(schema, str):
            schema_name = schema
            schema = _SCHEMAS[schema_name]
            validator = _get_validator(schema_name)
            validator.validate(json_dict)
        else:
            jsonschema.validate(json_dict, schema)
    except jsonschema.ValidationError as err:
        if err_msg is None:
            err_msg = "JSON failed validation. Set Qiskit log level to DEBUG " \
                      "for further information."
        newerr = SchemaValidationError(err_msg)
        newerr.__cause__ = _SummaryValidationError(err)
        logger.debug('%s', _format_causes(err))
        raise newerr

def _format_causes(err, level=0):
    """Return a cascading explanation of the validation error.

    Returns a cascading explanation of the validation error in the form of::

        <validator> failed @ <subfield_path> because of:
            <validator> failed @ <subfield_path> because of:
                ...
            <validator> failed @ <subfield_path> because of:
                ...
            ...

    For example::

        'oneOf' failed @ '<root>' because of:
            'required' failed @ '<root>.config' because of:
                'meas_level' is a required property

    Meaning the validator 'oneOf' failed while validating the whole object
    because of the validator 'required' failing while validating the property
    'config' because its 'meas_level' field is missing.

    The cascade repeats the format "<validator> failed @ <path> because of"
    until there are no deeper causes. In this case, the string representation
    of the error is shown.

    Args:
        err (jsonschema.ValidationError): the instance to explain.
        level (int): starting level of indentation for the cascade of
            explanations.

    Return:
        str: a formatted string with the explanation of the error.

    """
    lines = []

    def _print(string, offset=0):
        lines.append(_pad(string, offset=offset))

    def _pad(string, offset=0):
        padding = '  ' * (level + offset)
        padded_lines = [padding + line for line in string.split('\n')]
        return '\n'.join(padded_lines)

    def _format_path(path):
        def _format(item):
            if isinstance(item, str):
                return '.{}'.format(item)

            return '[{}]'.format(item)

        return ''.join(['<root>'] + list(map(_format, path)))

    _print('\'{}\' failed @ \'{}\' because of:'.format(
        err.validator, _format_path(err.absolute_path)))

    if not err.context:
        _print(str(err.message), offset=1)
    else:
        for suberr in err.context:
            lines.append(_format_causes(suberr, level+1))

    return '\n'.join(lines)


# -*- 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.

"""Node for an OPENQASM idlist."""

from .node import Node

class IdList(Node):
    """Node for an OPENQASM idlist.

    children is a list of id nodes.
    """

    def __init__(self, children):
        """Create the idlist node."""
        super().__init__('id_list', children, None)

    def size(self):
        """Return the length of the list."""
        return len(self.children)

    def qasm(self, prec=15):
        """Return the corresponding OPENQASM string."""
        return ",".join([self.children[j].qasm(prec)
                         for j in range(self.size())])


# -*- 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.

"""
Ripple adder example based on Cuccaro et al., quant-ph/0410184.

Note: if you have only cloned the Qiskit repository but not
used `pip install`, the examples only work from the root directory.
"""

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit import BasicAer
from qiskit import execute

###############################################################
# Set the backend name and coupling map.
###############################################################
backend = BasicAer.get_backend("qasm_simulator")
coupling_map = [[0,1], [0, 8], [1, 2], [1, 9], [2, 3], [2, 10], [3, 4], [3, 11],
                [4, 5], [4, 12], [5, 6], [5, 13], [6, 7], [6, 14], [7, 15], [8, 9],
                [9, 10], [10, 11], [11, 12], [12, 13], [13, 14], [14, 15]]

###############################################################
# Make a quantum program for the n-bit ripple adder.
###############################################################
n = 2

a = QuantumRegister(n, "a")
b = QuantumRegister(n, "b")
cin = QuantumRegister(1, "cin")
cout = QuantumRegister(1, "cout")
ans = ClassicalRegister(n+1, "ans")
qc = QuantumCircuit(a, b, cin, cout, ans, name="rippleadd")

def majority(p, a, b, c):
    """Majority gate."""
    p.cx(c, b)
    p.cx(c, a)
    p.ccx(a, b, c)

def unmajority(p, a, b, c):
    """Unmajority gate."""
    p.ccx(a, b, c)
    p.cx(c, a)
    p.cx(a, b)

# Build a temporary subcircuit that adds a to b,
# storing the result in b
adder_subcircuit = QuantumCircuit(cin, a, b, cout)
majority(adder_subcircuit, cin[0], b[0], a[0])
for j in range(n - 1):
    majority(adder_subcircuit, a[j], b[j + 1], a[j + 1])
adder_subcircuit.cx(a[n - 1], cout[0])
for j in reversed(range(n - 1)):
    unmajority(adder_subcircuit, a[j], b[j + 1], a[j + 1])
unmajority(adder_subcircuit, cin[0], b[0], a[0])

# Set the inputs to the adder
qc.x(a[0])  # Set input a = 0...0001
qc.x(b)   # Set input b = 1...1111
# Apply the adder
qc += adder_subcircuit
# Measure the output register in the computational basis
for j in range(n):
    qc.measure(b[j], ans[j])
qc.measure(cout[0], ans[n])

###############################################################
# execute the program.
###############################################################

# First version: not mapped
job = execute(qc, backend=backend, coupling_map=None, shots=1024)
result = job.result()
print(result.get_counts(qc))

# Second version: mapped to 2x8 array coupling graph
job = execute(qc, backend=backend, coupling_map=coupling_map, shots=1024)
result = job.result()
print(result.get_counts(qc))

# Both versions should give the same distribution


# -*- 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.
"""
A pass that merges any adjacent barriers into one

Only barriers which can be merged without affecting the barrier structure of the
DAG will be merged.

Not all redundant barriers will necessarily be merged, only adjacent barriers are merged.

For example, the circuit
qr = QuantumRegister(3, 'q')
circuit = QuantumCircuit(qr)
circuit.barrier(qr[0])
circuit.barrier(qr[1])
circuit.barrier(qr)

Will be transformed into a circuit corresponding to
circuit.barrier(qr[0])
circuit.barrier(qr)
after one iteration of the pass. These two barriers were not merged by the first pass as they are
not adjacent in the initial circuit.

The pass then can be reapplied to merge the newly adjacent barriers.
"""

from qiskit.transpiler.basepasses import TransformationPass
from qiskit.dagcircuit import DAGCircuit
from qiskit.extensions.standard.barrier import Barrier

class MergeAdjacentBarriers(TransformationPass):
    """Returns a circuit with any adjacent barriers merged together"""

    def run(self, dag):

        # sorted to so that they are in the order they appear in the DAG
        # so ancestors/descendants makes sense
        barriers = [nd for nd in dag.topological_op_nodes() if nd.name == 'barrier']

        # get dict of barrier merges
        node_to_barrier_qubits = MergeAdjacentBarriers._collect_potential_merges(dag, barriers)

        if not node_to_barrier_qubits:
            return dag

        # add the merged barriers to a new DAG
        new_dag = DAGCircuit()

        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)

        # go over current nodes, and add them to the new dag
        for node in dag.topological_op_nodes():
            if node.name == 'barrier':
                if node in node_to_barrier_qubits:
                    qubits = node_to_barrier_qubits[node]
                    # qubits are stored as a set, need to convert to a list
                    new_dag.apply_operation_back(Barrier(len(qubits)), qargs=list(qubits))
            else:
                # copy the condition over too
                if node.condition:
                    new_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs,
                                                 condition=node.condition)
                else:
                    new_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs)
        return new_dag

    @staticmethod
    def _collect_potential_merges(dag, barriers):
        """
        Returns a dict of DAGNode : Barrier objects, where the barrier needs to be
        inserted where the corresponding DAGNode appears in the main DAG
        """
        # if only got 1 or 0 barriers then can't merge
        if len(barriers) < 2:
            return None

        # mapping from the node that will be the main barrier to the
        # barrier object that gets built up
        node_to_barrier_qubits = {}

        # Start from the first barrier
        current_barrier = barriers[0]
        end_of_barrier = current_barrier
        current_barrier_nodes = [current_barrier]

        current_qubits = set(current_barrier.qargs)
        current_ancestors = dag.ancestors(current_barrier)
        current_descendants = dag.descendants(current_barrier)

        barrier_to_add = Barrier(len(current_qubits))

        for next_barrier in barriers[1:]:

            # Remove all barriers that have already been included in this new barrier from the set
            # of ancestors/descendants as they will be removed from the new DAG when it is created
            next_ancestors = {nd for nd in dag.ancestors(next_barrier)
                              if nd not in current_barrier_nodes}
            next_descendants = {nd for nd in dag.descendants(next_barrier)
                                if nd not in current_barrier_nodes}
            next_qubits = set(next_barrier.qargs)

            if (
                    not current_qubits.isdisjoint(next_qubits)
                    and current_ancestors.isdisjoint(next_descendants)
                    and current_descendants.isdisjoint(next_ancestors)
            ):

                # can be merged
                current_ancestors = current_ancestors | next_ancestors
                current_descendants = current_descendants | next_descendants
                current_qubits = current_qubits | next_qubits

                # update the barrier that will be added back to include this barrier
                barrier_to_add = Barrier(len(current_qubits))

            else:
                # store the previously made barrier
                if barrier_to_add:
                    node_to_barrier_qubits[end_of_barrier] = current_qubits

                # reset the properties
                current_qubits = set(next_barrier.qargs)
                current_ancestors = dag.ancestors(next_barrier)
                current_descendants = dag.descendants(next_barrier)

                barrier_to_add = Barrier(len(current_qubits))
                current_barrier_nodes = []

            end_of_barrier = next_barrier
            current_barrier_nodes.append(end_of_barrier)

        if barrier_to_add:
            node_to_barrier_qubits[end_of_barrier] = current_qubits

        return node_to_barrier_qubits