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

"""DEPRECATED: WILL BE REMOVED AFTER 0.8."""
import warnings
import logging

from qiskit.compiler.transpiler import transpile
from qiskit.compiler.assembler import assemble

logger = logging.getLogger(__name__)

# pylint: disable=redefined-builtin
def compile(circuits, backend,
            config=None, basis_gates=None, coupling_map=None, initial_layout=None,
            shots=1024, max_credits=10, seed=None, qobj_id=None, seed_mapper=None,
            pass_manager=None, memory=False):
    """Compile a list of circuits into a qobj.

    Args:
        circuits (QuantumCircuit or list[QuantumCircuit]): circuits to compile
        backend (BaseBackend): a backend to compile for
        config (dict): dictionary of parameters (e.g. noise) used by runner
        basis_gates (list[str]): list of basis gates names supported by the
            target. Default: ['u1','u2','u3','cx','id']
        coupling_map (list): coupling map (perhaps custom) to target in mapping
        initial_layout (list): initial layout of qubits in mapping
        shots (int): number of repetitions of each circuit, for sampling
        max_credits (int): maximum credits to use
        seed (int): random seed for simulators
        seed_mapper (int): random seed for swapper mapper
        qobj_id (int): identifier for the generated qobj
        pass_manager (PassManager): a pass manger for the transpiler pipeline
        memory (bool): if True, per-shot measurement bitstrings are returned as well

    Returns:
        Qobj: the qobj to be run on the backends

    Raises:
        QiskitError: if the desired options are not supported by backend
    """
    warnings.warn('qiskit.compile() is deprecated and will be removed in Qiskit Terra 0.9. '
                  'Please use qiskit.compiler.transpile() to transform circuits '
                  'and qiskit.compiler.assemble() to produce a runnable qobj.',
                  DeprecationWarning)

    new_circuits = transpile(circuits,
                             basis_gates=basis_gates,
                             coupling_map=coupling_map,
                             initial_layout=initial_layout,
                             seed_transpiler=seed_mapper,
                             backend=backend,
                             pass_manager=pass_manager)

    qobj = assemble(new_circuits,
                    qobj_header=None,
                    shots=shots,
                    max_credits=max_credits,
                    seed_simulator=seed,
                    memory=memory,
                    qobj_id=qobj_id,
                    config=config)  # deprecated

    return qobj


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

"""Common utilities for Qiskit."""

import logging
import platform
import re
import socket
import sys
import warnings

import psutil
from marshmallow.warnings import ChangedInMarshmallow3Warning

logger = logging.getLogger(__name__)

def _check_python_version():
    """Check for Python version 3.5+."""
    if sys.version_info < (3, 5):
        raise Exception('Qiskit requires Python version 3.5 or greater.')

def _filter_deprecation_warnings():
    """Apply filters to deprecation warnings.

    Force the `DeprecationWarning` warnings to be displayed for the qiskit
    module, overriding the system configuration as they are ignored by default
    [1] for end-users. Additionally, silence the `ChangedInMarshmallow3Warning`
    messages.

    TODO: on Python 3.7, this might not be needed due to PEP-0565 [2].

    [1] https://docs.python.org/3/library/warnings.html#default-warning-filters
    [2] https://www.python.org/dev/peps/pep-0565/
    """
    deprecation_filter = ('always', None, DeprecationWarning,
                          re.compile(r'^qiskit\.*', re.UNICODE), 0)

    # Instead of using warnings.simple_filter() directly, the internal
    # _add_filter() function is used for being able to match against the
    # module.
    try:
        warnings._add_filter(*deprecation_filter, append=False)
    except AttributeError:
        # ._add_filter is internal and not available in some Python versions.
        pass

    # Add a filter for ignoring ChangedInMarshmallow3Warning, as we depend on
    # marhsmallow 2 explicitly. 2.17.0 introduced new deprecation warnings that
    # are useful for eventually migrating, but too verbose for our purposes.
    warnings.simplefilter('ignore', category=ChangedInMarshmallow3Warning)

_check_python_version()
_filter_deprecation_warnings()

def local_hardware_info():
    """Basic hardware information about the local machine.

    Gives actual number of CPU's in the machine, even when hyperthreading is
    turned on. CPU count defaults to 1 when true count can't be determined.

    Returns:
        dict: The hardware information.
    """
    results = {
        'os': platform.system(),
        'memory': psutil.virtual_memory().total / (1024 ** 3),
        'cpus': psutil.cpu_count(logical=False) or 1
    }
    return results

def _has_connection(hostname, port):
    """Checks if internet connection exists to host via specified port.

    If any exception is raised while trying to open a socket this will return
    false.

    Args:
        hostname (str): Hostname to connect to.
        port (int): Port to connect to

    Returns:
        bool: Has connection or not

    """
    try:
        host = socket.gethostbyname(hostname)
        socket.create_connection((host, port), 2)
        return True
    except Exception:  # pylint: disable=broad-except
        return False


# -*- 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.
"""
Pauli Transfer Matrix (PTM) representation of a Quantum Channel.

The PTM is the n-qubit superoperator defined with respect to vectorization in
the Pauli basis. For a quantum channel E, the PTM is defined by

    PTM_{i,j} = Tr[P_i.E(P_j)]

where [P_i, i=0,...4^{n-1}] is the n-qubit Pauli basis in lexicographic order.

Evolution is given by

    |E(ρ)⟩⟩_p = PTM|ρ⟩⟩_p

where |A⟩⟩_p denotes vectorization in the Pauli basis: ⟨i|A⟩⟩_p = Tr[P_i.A]

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_ptm

class PTM(QuantumChannel):
    """Initialize a quantum channel Pauli-Transfer 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 is not an N-qubit channel or
            cannot be initialized as a PTM.

        Additional Information
        ----------------------
        If the input or output dimensions are None, they will be
        automatically determined from the input data. The PTM
        representation is only valid for N-qubit channels.
        """
    def __init__(self, data, input_dims=None, output_dims=None):
        """Initialize a PTM quantum channel operator."""
        # If the input is a raw list or matrix we assume that it is
        # already a Chi matrix.
        if isinstance(data, (list, np.ndarray)):
            # Should we force this to be real?
            ptm = np.array(data, dtype=complex)
            # Determine input and output dimensions
            dout, din = ptm.shape
            if input_dims:
                input_dim = np.product(input_dims)
            else:
                input_dim = int(np.sqrt(din))
            if output_dims:
                output_dim = np.product(input_dims)
            else:
                output_dim = int(np.sqrt(dout))
            if output_dim**2 != dout or input_dim**2 != din or input_dim != output_dim:
                raise QiskitError("Invalid shape for PTM 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 PTM object
            ptm = _to_ptm(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 input is N-qubit channel
        n_qubits = int(np.log2(input_dim))
        if 2**n_qubits != input_dim:
            raise QiskitError("Input is not an n-qubit Pauli transfer matrix.")
        # 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__('PTM', ptm, input_dims, output_dims)

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

    def conjugate(self):
        """Return the conjugate of the QuantumChannel."""
        # Since conjugation is basis dependent we transform
        # to the SuperOp representation to compute the
        # conjugate channel
        return PTM(SuperOp(self).conjugate())

    def transpose(self):
        """Return the transpose of the QuantumChannel."""
        # Since conjugation is basis dependent we transform
        # to the SuperOp representation to compute the
        # conjugate channel
        return PTM(SuperOp(self).transpose())

    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:
            PTM: The composition channel as a PTM object.

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

        # Convert other to PTM
        if not isinstance(other, PTM):
            other = PTM(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:
            # Composition A(B(input))
            input_dim = other._input_dim
            output_dim = self._output_dim
            return PTM(np.dot(self._data, other.data), input_dim, output_dim)
        # Composition B(A(input))
        input_dim = self._input_dim
        output_dim = other._output_dim
        return PTM(np.dot(other.data, self._data), input_dim, output_dim)

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

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

        Returns:
            PTM: the matrix power of the SuperOp converted to a PTM 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 PTM(SuperOp(self).power(n))

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

        Args:
            other (QuantumChannel): a quantum channel.

        Returns:
            PTM: the tensor product channel self ⊗ other as a PTM 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:
            PTM: the tensor product channel other ⊗ self as a PTM 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:
            PTM: the linear addition self + other as a PTM object.

        Raises:
            QiskitError: if other cannot be converted to a channel or
            has incompatible dimensions.
        """
        if not isinstance(other, PTM):
            other = PTM(other)
        if self.dim != other.dim:
            raise QiskitError("other QuantumChannel dimensions are not equal")
        return PTM(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:
            PTM: the linear subtraction self - other as PTM object.

        Raises:
            QiskitError: if other cannot be converted to a channel or
            has incompatible dimensions.
        """
        if not isinstance(other, PTM):
            other = PTM(other)
        if self.dim != other.dim:
            raise QiskitError("other QuantumChannel dimensions are not equal")
        return PTM(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:
            PTM: the scalar multiplication other * self as a PTM object.

        Raises:
            QiskitError: if other is not a valid scalar.
        """
        if not isinstance(other, Number):
            raise QiskitError("other is not a number")
        return PTM(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.
        """
        return SuperOp(self)._evolve(state, qargs)

    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:
            PTM: the tensor product channel as a PTM object.

        Raises:
            QiskitError: if other cannot be converted to a channel.
        """
        if not isinstance(other, PTM):
            other = PTM(other)
        if reverse:
            input_dims = self.input_dims() + other.input_dims()
            output_dims = self.output_dims() + other.output_dims()
            data = np.kron(other.data, self._data)
        else:
            input_dims = other.input_dims() + self.input_dims()
            output_dims = other.output_dims() + self.output_dims()
            data = np.kron(self._data, other.data)
        return PTM(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.

"""A module of magic functions"""

import time
import threading
from IPython.display import display                              # pylint: disable=import-error
from IPython.core import magic_arguments                         # pylint: disable=import-error
from IPython.core.magic import cell_magic, Magics, magics_class  # pylint: disable=import-error
try:
    import ipywidgets as widgets           # pylint: disable=import-error
except ImportError:
    raise ImportError('These functions  need ipywidgets. '
                      'Run "pip install ipywidgets" before.')
import qiskit
from qiskit.tools.events.progressbar import TextProgressBar
from .progressbar import HTMLProgressBar

def _html_checker(job_var, interval, status, header,
                  _interval_set=False):
    """Internal function that updates the status
    of a HTML job monitor.

    Args:
        job_var (BaseJob): The job to keep track of.
        interval (int): The status check interval
        status (widget): HTML ipywidget for output ot screen
        header (str): String representing HTML code for status.
        _interval_set (bool): Was interval set by user?
    """
    job_status = job_var.status()
    job_status_name = job_status.name
    job_status_msg = job_status.value
    status.value = header % (job_status_msg)
    while job_status_name not in ['DONE', 'CANCELLED']:
        time.sleep(interval)
        job_status = job_var.status()
        job_status_name = job_status.name
        job_status_msg = job_status.value
        if job_status_name == 'ERROR':
            break
        else:
            if job_status_name == 'QUEUED':
                job_status_msg += ' (%s)' % job_var.queue_position()
                if not _interval_set:
                    interval = max(job_var.queue_position(), 2)
            else:
                if not _interval_set:
                    interval = 2
            status.value = header % (job_status_msg)

    status.value = header % (job_status_msg)

@magics_class
class StatusMagic(Magics):
    """A class of status magic functions.
    """
    @cell_magic
    @magic_arguments.magic_arguments()
    @magic_arguments.argument(
        '-i',
        '--interval',
        type=float,
        default=None,
        help='Interval for status check.'
    )
    def qiskit_job_status(self, line='', cell=None):
        """A Jupyter magic function to check the status of a Qiskit job instance.
        """
        args = magic_arguments.parse_argstring(self.qiskit_job_status, line)

        if args.interval is None:
            args.interval = 2
            _interval_set = False
        else:
            _interval_set = True

        # Split cell lines to get LHS variables
        cell_lines = cell.split('\n')
        line_vars = []
        for cline in cell_lines:
            if '=' in cline and '==' not in cline:
                line_vars.append(cline.replace(' ', '').split('=')[0])
            elif '.append(' in cline:
                line_vars.append(cline.replace(' ', '').split('(')[0])

        # Execute the cell
        self.shell.ex(cell)

        # Look for all vars that are BaseJob instances
        jobs = []
        for var in line_vars:
            iter_var = False
            if '#' not in var:
                # The line var is a list or array, but we cannot parse the index
                # so just iterate over the whole array for jobs.
                if '[' in var:
                    var = var.split('[')[0]
                    iter_var = True
                elif '.append' in var:
                    var = var.split('.append')[0]
                    iter_var = True

                if iter_var:
                    for item in self.shell.user_ns[var]:
                        if isinstance(item, qiskit.providers.basejob.BaseJob):
                            jobs.append(item)
                else:
                    if isinstance(self.shell.user_ns[var],
                                  qiskit.providers.basejob.BaseJob):
                        jobs.append(self.shell.user_ns[var])

        # Must have one job class
        if not any(jobs):
            raise Exception(
                "Cell must contain at least one variable of BaseJob type.")

        # List index of job if checking status of multiple jobs.
        multi_job = False
        if len(jobs) > 1:
            multi_job = True

        job_checkers = []
        # Loop over every BaseJob that was found.
        for idx, job_var in enumerate(jobs):
            style = "font-size:16px;"
            if multi_job:
                idx_str = '[%s]' % idx
            else:
                idx_str = ''
            header = "<p style='{style}'>Job Status {id}: %s </p>".format(id=idx_str,
                                                                          style=style)
            status = widgets.HTML(
                value=header % job_var.status().value)

            thread = threading.Thread(target=_html_checker, args=(job_var, args.interval,
                                                                  status, header,
                                                                  _interval_set))
            thread.start()
            job_checkers.append(status)

        # Group all HTML widgets into single vertical layout
        box = widgets.VBox(job_checkers)
        display(box)

@magics_class
class ProgressBarMagic(Magics):
    """A class of progress bar magic functions.
    """
    @cell_magic
    @magic_arguments.magic_arguments()
    @magic_arguments.argument(
        '-t',
        '--type',
        type=str,
        default='html',
        help="Type of progress bar, 'html' or 'text'."
    )
    def qiskit_progress_bar(self, line='', cell=None):  # pylint: disable=W0613
        """A Jupyter magic function to generate progressbar.
        """
        args = magic_arguments.parse_argstring(self.qiskit_progress_bar, line)
        if args.type == 'html':
            HTMLProgressBar()
        elif args.type == 'text':
            TextProgressBar()
        else:
            raise qiskit.QiskitError('Invalid progress bar type.')

        self.shell.ex(cell)


# -*- coding: utf-8 -*-

# Copyright 2019, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.

# pylint: disable=missing-return-doc, invalid-unary-operand-type

"""Module for builtin continuous pulse functions."""

import functools
from typing import Union, Tuple

import numpy as np

def constant(times: np.ndarray, amp: complex) -> np.ndarray:
    """Continuous constant pulse.

    Args:
        times: Times to output pulse for.
        amp: Complex pulse amplitude.
    """
    return np.full(len(times), amp, dtype=np.complex_)

def zero(times: np.ndarray) -> np.ndarray:
    """Continuous zero pulse.

    Args:
        times: Times to output pulse for.
    """
    return constant(times, 0)

def square(times: np.ndarray, amp: complex, period: float, phase: float = 0) -> np.ndarray:
    """Continuous square wave.

    Args:
        times: Times to output wave for.
        amp: Pulse amplitude. Wave range is [-amp, amp].
        period: Pulse period, units of dt.
        phase: Pulse phase.
    """
    x = times/period+phase/np.pi
    return amp*(2*(2*np.floor(x) - np.floor(2*x)) + 1).astype(np.complex_)

def sawtooth(times: np.ndarray, amp: complex, period: float, phase: float = 0) -> np.ndarray:
    """Continuous sawtooth wave.

    Args:
        times: Times to output wave for.
        amp: Pulse amplitude. Wave range is [-amp, amp].
        period: Pulse period, units of dt.
        phase: Pulse phase.
    """
    x = times/period+phase/np.pi
    return amp*2*(x-np.floor(1/2+x)).astype(np.complex_)

def triangle(times: np.ndarray, amp: complex, period: float, phase: float = 0) -> np.ndarray:
    """Continuous triangle wave.

    Args:
        times: Times to output wave for.
        amp: Pulse amplitude. Wave range is [-amp, amp].
        period: Pulse period, units of dt.
        phase: Pulse phase.
    """
    return amp*(-2*np.abs(sawtooth(times, 1, period, (phase-np.pi/2)/2)) + 1).astype(np.complex_)

def cos(times: np.ndarray, amp: complex, freq: float, phase: float = 0) -> np.ndarray:
    """Continuous cosine wave.

    Args:
        times: Times to output wave for.
        amp: Pulse amplitude.
        freq: Pulse frequency, units of 1/dt.
        phase: Pulse phase.
    """
    return amp*np.cos(2*np.pi*freq*times+phase).astype(np.complex_)

def sin(times: np.ndarray, amp: complex, freq: float, phase: float = 0) -> np.ndarray:
    """Continuous cosine wave.

    Args:
        times: Times to output wave for.
        amp: Pulse amplitude.
        freq: Pulse frequency, units of 1/dt.
        phase: Pulse phase.
    """
    return amp*np.sin(2*np.pi*freq*times+phase).astype(np.complex_)

def _fix_gaussian_width(gaussian_samples, amp: float, center: float, sigma: float,
                        zeroed_width: Union[None, float] = None, rescale_amp: bool = False,
                        ret_scale_factor: bool = False) -> np.ndarray:
    r"""Enforce that the supplied gaussian pulse is zeroed at a specific width.

    This is acheived by subtracting $\Omega_g(center \pm zeroed_width/2)$ from all samples.

    amp: Pulse amplitude at `2\times center+1`.
    center: Center (mean) of pulse.
    sigma: Width (standard deviation) of pulse.
    zeroed_width: Subtract baseline to gaussian pulses to make sure
             $\Omega_g(center \pm zeroed_width/2)=0$ is satisfied. This is used to avoid
             large discontinuities at the start of a gaussian pulse. If unsupplied,
             defaults to $2*(center+1)$ such that the samples are zero at $\Omega_g(-1)$.
    rescale_amp: If `zeroed_width` is not `None` and `rescale_amp=True` the pulse will
                 be rescaled so that $\Omega_g(center)-\Omega_g(center\pm zeroed_width/2)=amp$.
    ret_scale_factor: Return amplitude scale factor.
    """
    if zeroed_width is None:
        zeroed_width = 2*(center+1)

    zero_offset = gaussian(np.array([-zeroed_width/2]), amp, center, sigma)
    gaussian_samples -= zero_offset
    amp_scale_factor = 1.
    if rescale_amp:
        amp_scale_factor = amp/(amp-zero_offset)
        gaussian_samples *= amp_scale_factor

    if ret_scale_factor:
        return gaussian_samples, amp_scale_factor
    return gaussian_samples

def gaussian(times: np.ndarray, amp: complex, center: float, sigma: float,
             zeroed_width: Union[None, float] = None, rescale_amp: bool = False,
             ret_x: bool = False) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]:
    r"""Continuous unnormalized gaussian pulse.

    Integrated area under curve is $\Omega_g(amp, sigma) = amp \times np.sqrt(2\pi \sigma^2)$

    Args:
        times: Times to output pulse for.
        amp: Pulse amplitude at `center`. If `zeroed_width` is set pulse amplitude at center
            will be $amp-\Omega_g(center\pm zeroed_width/2)$ unless `rescale_amp` is set,
            in which case all samples will be rescaled such that the center
            amplitude will be `amp`.
        center: Center (mean) of pulse.
        sigma: Width (standard deviation) of pulse.
        zeroed_width: Subtract baseline to gaussian pulses to make sure
                 $\Omega_g(center \pm zeroed_width/2)=0$ is satisfied. This is used to avoid
                 large discontinuities at the start of a gaussian pulse.
        rescale_amp: If `zeroed_width` is not `None` and `rescale_amp=True` the pulse will
                     be rescaled so that $\Omega_g(center)-\Omega_g(center\pm zeroed_width/2)=amp$.
        ret_x: Return centered and standard deviation normalized pulse location.
               $x=(times-center)/sigma.
    """
    times = np.asarray(times, dtype=np.complex_)
    x = (times-center)/sigma
    gauss = amp*np.exp(-x**2/2).astype(np.complex_)

    if zeroed_width is not None:
        gauss = _fix_gaussian_width(gauss, amp=amp, center=center, sigma=sigma,
                                    zeroed_width=zeroed_width, rescale_amp=rescale_amp)

    if ret_x:
        return gauss, x
    return gauss

def gaussian_deriv(times: np.ndarray, amp: complex, center: float, sigma: float,
                   ret_gaussian: bool = False) -> np.ndarray:
    """Continuous unnormalized gaussian derivative pulse.

    Args:
        times: Times to output pulse for.
        amp: Pulse amplitude at `center`.
        center: Center (mean) of pulse.
        sigma: Width (standard deviation) of pulse.
        ret_gaussian: Return gaussian with which derivative was taken with.
    """
    gauss, x = gaussian(times, amp=amp, center=center, sigma=sigma, ret_x=True)
    gauss_deriv = -x/sigma*gauss
    if ret_gaussian:
        return gauss_deriv, gauss
    return gauss_deriv

def gaussian_square(times: np.ndarray, amp: complex, center: float, width: float,
                    sigma: float, zeroed_width: Union[None, float] = None) -> np.ndarray:
    r"""Continuous gaussian square pulse.

    Args:
        times: Times to output pulse for.
        amp: Pulse amplitude.
        center: Center of the square pulse component.
        width: Width of the square pulse component.
        sigma: Width (standard deviation) of gaussian rise/fall portion of the pulse.
        zeroed_width: Subtract baseline of gaussian square pulse
                      to enforce $\OmegaSquare(center \pm zeroed_width/2)=0$.
    """
    square_start = center-width/2
    square_stop = center+width/2
    if zeroed_width:
        zeroed_width = min(width, zeroed_width)
        gauss_zeroed_width = zeroed_width-width
    else:
        gauss_zeroed_width = None

    funclist = [functools.partial(gaussian, amp=amp, center=square_start, sigma=sigma,
                                  zeroed_width=gauss_zeroed_width, rescale_amp=True),
                functools.partial(gaussian, amp=amp, center=square_stop, sigma=sigma,
                                  zeroed_width=gauss_zeroed_width, rescale_amp=True),
                functools.partial(constant, amp=amp)]
    condlist = [times <= square_start, times >= square_stop]
    return np.piecewise(times.astype(np.complex_), condlist, funclist)

def drag(times: np.ndarray, amp: complex, center: float, sigma: float, beta: float,
         zeroed_width: Union[None, float] = None, rescale_amp: bool = False) -> np.ndarray:
    r"""Continuous Y-only correction DRAG pulse for standard nonlinear oscillator (SNO) [1].

    [1] Gambetta, J. M., Motzoi, F., Merkel, S. T. & Wilhelm, F. K.
        Analytic control methods for high-fidelity unitary operations
        in a weakly nonlinear oscillator. Phys. Rev. A 83, 012308 (2011).

    Args:
        times: Times to output pulse for.
        amp: Pulse amplitude at `center`.
        center: Center (mean) of pulse.
        sigma: Width (standard deviation) of pulse.
        beta: Y correction amplitude. For the SNO this is $\beta=-\frac{\lambda_1^2}{4\Delta_2}$.
            Where $\lambds_1$ is the relative coupling strength between the first excited and second
            excited states and $\Delta_2$ is the detuning between the resepective excited states.
        zeroed_width: Subtract baseline to gaussian pulses to make sure
                 $\Omega_g(center \pm zeroed_width/2)=0$ is satisfied. This is used to avoid
                 large discontinuities at the start of a gaussian pulse.
        rescale_amp: If `zeroed_width` is not `None` and `rescale_amp=True` the pulse will
                     be rescaled so that $\Omega_g(center)-\Omega_g(center\pm zeroed_width/2)=amp$.

    """
    gauss_deriv, gauss = gaussian_deriv(times, amp=amp, center=center, sigma=sigma,
                                        ret_gaussian=True)
    if zeroed_width is not None:
        gauss, scale_factor = _fix_gaussian_width(gauss, amp=amp, center=center, sigma=sigma,
                                                  zeroed_width=zeroed_width,
                                                  rescale_amp=rescale_amp,
                                                  ret_scale_factor=True)
        gauss_deriv *= scale_factor

    return gauss + 1j*beta*gauss_deriv


# -*- 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 default passmanager."""

from qiskit.transpiler.passmanager import PassManager
from qiskit.extensions.standard import SwapGate

from qiskit.transpiler.passes.unroller import Unroller
from qiskit.transpiler.passes.unroll_3q_or_more import Unroll3qOrMore
from qiskit.transpiler.passes.cx_cancellation import CXCancellation
from qiskit.transpiler.passes.decompose import Decompose
from qiskit.transpiler.passes.optimize_1q_gates import Optimize1qGates
from qiskit.transpiler.passes.fixed_point import FixedPoint
from qiskit.transpiler.passes.depth import Depth
from qiskit.transpiler.passes.remove_reset_in_zero_state import RemoveResetInZeroState
from qiskit.transpiler.passes.mapping.check_map import CheckMap
from qiskit.transpiler.passes.mapping.cx_direction import CXDirection
from qiskit.transpiler.passes.mapping.dense_layout import DenseLayout
from qiskit.transpiler.passes.mapping.trivial_layout import TrivialLayout
from qiskit.transpiler.passes.mapping.legacy_swap import LegacySwap
from qiskit.transpiler.passes.mapping.full_ancilla_allocation import FullAncillaAllocation
from qiskit.transpiler.passes.mapping.enlarge_with_ancilla import EnlargeWithAncilla

def default_pass_manager(basis_gates, coupling_map, initial_layout, seed_transpiler):
    """
    The default pass manager that maps to the coupling map.

    Args:
        basis_gates (list[str]): list of basis gate names supported by the target.
        coupling_map (CouplingMap): coupling map to target in mapping.
        initial_layout (Layout or None): initial layout of virtual qubits on physical qubits
        seed_transpiler (int or None): random seed for stochastic passes.

    Returns:
        PassManager: A pass manager to map and optimize.
    """
    pass_manager = PassManager()
    pass_manager.property_set['layout'] = initial_layout

    pass_manager.append(Unroller(basis_gates))

    # Use the trivial layout if no layout is found
    pass_manager.append(TrivialLayout(coupling_map),
                        condition=lambda property_set: not property_set['layout'])

    # if the circuit and layout already satisfy the coupling_constraints, use that layout
    # otherwise layout on the most densely connected physical qubit subset
    pass_manager.append(CheckMap(coupling_map))
    pass_manager.append(DenseLayout(coupling_map),
                        condition=lambda property_set: not property_set['is_swap_mapped'])

    # Extend the the dag/layout with ancillas using the full coupling map
    pass_manager.append(FullAncillaAllocation(coupling_map))
    pass_manager.append(EnlargeWithAncilla())

    # Circuit must only contain 1- or 2-qubit interactions for swapper to work
    pass_manager.append(Unroll3qOrMore())

    # Swap mapper
    pass_manager.append(LegacySwap(coupling_map, trials=20, seed=seed_transpiler))

    # Expand swaps
    pass_manager.append(Decompose(SwapGate))

    # Change CX directions
    pass_manager.append(CXDirection(coupling_map))

    # Unroll to the basis
    pass_manager.append(Unroller(['u1', 'u2', 'u3', 'id', 'cx']))

    # Simplify single qubit gates and CXs
    simplification_passes = [Optimize1qGates(), CXCancellation(), RemoveResetInZeroState()]

    pass_manager.append(simplification_passes + [Depth(), FixedPoint('depth')],
                        do_while=lambda property_set: not property_set['depth_fixed_point'])

    return pass_manager

def default_pass_manager_simulator(basis_gates):
    """
    The default pass manager without a coupling map.

    Args:
        basis_gates (list[str]): list of basis gate names to unroll to.

    Returns:
        PassManager: A passmanager that just unrolls, without any optimization.
    """
    pass_manager = PassManager()

    pass_manager.append(Unroller(basis_gates))

    pass_manager.append([RemoveResetInZeroState(), Depth(), FixedPoint('depth')],
                        do_while=lambda property_set: not property_set['depth_fixed_point'])

    return pass_manager


# -*- coding: utf-8 -*-

# Copyright 2017, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.

# pylint: disable=cyclic-import
"""Quantum circuit object."""

from copy import deepcopy
import itertools
import sys
import multiprocessing as mp

from qiskit.circuit.instruction import Instruction
from qiskit.qasm.qasm import Qasm
from qiskit.exceptions import QiskitError
from qiskit.circuit.parameter import Parameter
from .quantumregister import QuantumRegister
from .classicalregister import ClassicalRegister
from .parametertable import ParameterTable

class QuantumCircuit:
    """Quantum circuit."""
    instances = 0
    prefix = 'circuit'

    # Class variable OPENQASM header
    header = "OPENQASM 2.0;"
    extension_lib = "include \"qelib1.inc\";"

    def __init__(self, *regs, name=None):
        """Create a new circuit.
        A circuit is a list of instructions bound to some registers.
        Args:
            *regs (list(Register) or list(Int)): To be included in the circuit.
                  - If [Register], the QuantumRegister and/or ClassicalRegister
                    to include in the circuit.
                    E.g.: QuantumCircuit(QuantumRegister(4))
                          QuantumCircuit(QuantumRegister(4), ClassicalRegister(3))
                          QuantumCircuit(QuantumRegister(4, 'qr0'), QuantumRegister(2, 'qr1'))
                  - If [Int], the amount of qubits and/or classical bits to include
                  in the circuit. It can be (Int, ) or (Int, Int).
                    E.g.: QuantumCircuit(4) # A QuantumCircuit with 4 qubits
                          QuantumCircuit(4, 3) # A QuantumCircuit with 4 qubits and 3 classical bits
            name (str or None): the name of the quantum circuit. If
                None, an automatically generated string will be assigned.

        Raises:
            QiskitError: if the circuit name, if given, is not valid.
        """
        if name is None:
            name = self.cls_prefix() + str(self.cls_instances())
            # pylint: disable=not-callable
            # (known pylint bug: https://github.com/PyCQA/pylint/issues/1699)
            if sys.platform != "win32" and \
               isinstance(mp.current_process(), mp.context.ForkProcess):
                name += '-{}'.format(mp.current_process().pid)
        self._increment_instances()

        if not isinstance(name, str):
            raise QiskitError("The circuit name should be a string "
                              "(or None to auto-generate a name).")

        self.name = name

        # Data contains a list of instructions and their contexts,
        # in the order they were applied.
        self.data = []

        # This is a map of registers bound to this circuit, by name.
        self.qregs = []
        self.cregs = []
        self.add_register(*regs)

        # Parameter table tracks instructions with variable parameters.
        self._parameter_table = ParameterTable()

    def __str__(self):
        return str(self.draw(output='text'))

    def __eq__(self, other):
        # TODO: remove 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):
        cls.instances += 1

    @classmethod
    def cls_instances(cls):
        """Return the current number of instances of this class,
        useful for auto naming."""
        return cls.instances

    @classmethod
    def cls_prefix(cls):
        """Return the prefix to use for auto naming."""
        return cls.prefix

    def has_register(self, register):
        """
        Test if this circuit has the register r.

        Args:
            register (Register): a quantum or classical register.

        Returns:
            bool: True if the register is contained in this circuit.
        """
        has_reg = False
        if (isinstance(register, QuantumRegister) and
                register in self.qregs):
            has_reg = True
        elif (isinstance(register, ClassicalRegister) and
              register in self.cregs):
            has_reg = True
        return has_reg

    def mirror(self):
        """Mirror the circuit by reversing the instructions.

        This is done by recursively mirroring all instructions.
        It does not invert any gate.

        Returns:
            QuantumCircuit: the mirrored circuit
        """
        reverse_circ = self.copy(name=self.name+'_mirror')
        reverse_circ.data = []
        for inst, qargs, cargs in reversed(self.data):
            reverse_circ.data.append((inst.mirror(), qargs, cargs))
        return reverse_circ

    def inverse(self):
        """Invert this circuit.

        This is done by recursively inverting all gates.

        Returns:
            QuantumCircuit: the inverted circuit

        Raises:
            QiskitError: if the circuit cannot be inverted.
        """
        inverse_circ = self.copy(name=self.name+'_dg')
        inverse_circ.data = []
        for inst, qargs, cargs in reversed(self.data):
            inverse_circ.data.append((inst.inverse(), qargs, cargs))
        return inverse_circ

    def combine(self, rhs):
        """
        Append rhs to self if self contains compatible registers.

        Two circuits are compatible if they contain the same registers
        or if they contain different registers with unique names. The
        returned circuit will contain all unique registers between both
        circuits.

        Return self + rhs as a new object.
        """
        # Check registers in LHS are compatible with RHS
        self._check_compatible_regs(rhs)

        # Make new circuit with combined registers
        combined_qregs = deepcopy(self.qregs)
        combined_cregs = deepcopy(self.cregs)

        for element in rhs.qregs:
            if element not in self.qregs:
                combined_qregs.append(element)
        for element in rhs.cregs:
            if element not in self.cregs:
                combined_cregs.append(element)
        circuit = QuantumCircuit(*combined_qregs, *combined_cregs)
        for instruction_context in itertools.chain(self.data, rhs.data):
            circuit.append(*instruction_context)
        return circuit

    def extend(self, rhs):
        """
        Append rhs to self if self contains compatible registers.

        Two circuits are compatible if they contain the same registers
        or if they contain different registers with unique names. The
        returned circuit will contain all unique registers between both
        circuits.

        Modify and return self.
        """
        # Check registers in LHS are compatible with RHS
        self._check_compatible_regs(rhs)

        # Add new registers
        for element in rhs.qregs:
            if element not in self.qregs:
                self.qregs.append(element)
        for element in rhs.cregs:
            if element not in self.cregs:
                self.cregs.append(element)

        # Add new gates
        for instruction_context in rhs.data:
            self.append(*instruction_context)
        return self

    def qubits(self):
        """
        Returns a list of quantum bits in the order that the registers had been added.
        """
        return [qbit for qreg in self.qregs for qbit in qreg]

    def clbits(self):
        """
        Returns a list of classical bits in the order that the registers had been added.
        """
        return [cbit for creg in self.cregs for cbit in creg]

    def __add__(self, rhs):
        """Overload + to implement self.combine."""
        return self.combine(rhs)

    def __iadd__(self, rhs):
        """Overload += to implement self.extend."""
        return self.extend(rhs)

    def __len__(self):
        """Return number of operations in circuit."""
        return len(self.data)

    def __getitem__(self, item):
        """Return indexed operation."""
        return self.data[item]

    def append(self, instruction, qargs=None, cargs=None):
        """Append an instruction to the end of the circuit, modifying
        the circuit in place.

        Args:
            instruction (Instruction or Operator): Instruction instance to append
            qargs (list(tuple)): qubits to attach instruction to
            cargs (list(tuple)): clbits to attach instruction to

        Returns:
            Instruction: a handle to the instruction that was just added

        Raises:
            QiskitError: if the gate is of a different shape than the wires
                it is being attached to.
        """
        qargs = qargs or []
        cargs = cargs or []

        # Convert input to instruction
        if not isinstance(instruction, Instruction) and hasattr(instruction, 'to_instruction'):
            instruction = instruction.to_instruction()
        if not isinstance(instruction, Instruction):
            raise QiskitError('object is not an Instruction.')

        # do some compatibility checks
        self._check_dups(qargs)
        self._check_qargs(qargs)
        self._check_cargs(cargs)
        if instruction.num_qubits != len(qargs) or \
                instruction.num_clbits != len(cargs):
            raise QiskitError("instruction %s with %d qubits and %d clbits "
                              "cannot be appended onto %d qubits and %d clbits." %
                              (instruction.name,
                               instruction.num_qubits, instruction.num_clbits,
                               len(qargs), len(cargs)))

        # add the instruction onto the given wires
        instruction_context = instruction, qargs, cargs
        self.data.append(instruction_context)

        # track variable parameters in instruction
        for param_index, param in enumerate(instruction.params):
            if isinstance(param, Parameter):
                current_symbols = self.parameters

                if param in current_symbols:
                    self._parameter_table[param].append((instruction, param_index))
                else:
                    self._parameter_table[param] = [(instruction, param_index)]

        return instruction

    def _attach(self, instruction, qargs, cargs):
        """DEPRECATED after 0.8"""
        self.append(instruction, qargs, cargs)

    def add_register(self, *regs):
        """Add registers."""
        if not regs:
            return

        if any([isinstance(reg, int) for reg in regs]):
            # QuantumCircuit defined without registers
            if len(regs) == 1 and isinstance(regs[0], int):
                # QuantumCircuit with anonymous quantum wires e.g. QuantumCircuit(2)
                regs = (QuantumRegister(regs[0], 'q'),)
            elif len(regs) == 2 and all([isinstance(reg, int) for reg in regs]):
                # QuantumCircuit with anonymous wires e.g. QuantumCircuit(2, 3)
                regs = (QuantumRegister(regs[0], 'q'), ClassicalRegister(regs[1], 'c'))
            else:
                raise QiskitError("QuantumCircuit parameters can be Registers or Integers."
                                  " If Integers, up to 2 arguments. QuantumCircuit was called"
                                  " with %s." % (regs,))

        for register in regs:
            if register in self.qregs or register in self.cregs:
                raise QiskitError("register name \"%s\" already exists"
                                  % register.name)
            if isinstance(register, QuantumRegister):
                self.qregs.append(register)
            elif isinstance(register, ClassicalRegister):
                self.cregs.append(register)
            else:
                raise QiskitError("expected a register")

    def _check_dups(self, qubits):
        """Raise exception if list of qubits contains duplicates."""
        squbits = set(qubits)
        if len(squbits) != len(qubits):
            raise QiskitError("duplicate qubit arguments")

    def _check_qargs(self, qargs):
        """Raise exception if a qarg is not in this circuit or bad format."""
        if not all(isinstance(i, tuple) and
                   isinstance(i[0], QuantumRegister) and
                   isinstance(i[1], int) for i in qargs):
            raise QiskitError("qarg not (QuantumRegister, int) tuple")
        if not all(self.has_register(i[0]) for i in qargs):
            raise QiskitError("register not in this circuit")
        for qubit in qargs:
            qubit[0].check_range(qubit[1])

    def _check_cargs(self, cargs):
        """Raise exception if clbit is not in this circuit or bad format."""
        if not all(isinstance(i, tuple) and
                   isinstance(i[0], ClassicalRegister) and
                   isinstance(i[1], int) for i in cargs):
            raise QiskitError("carg not (ClassicalRegister, int) tuple")
        if not all(self.has_register(i[0]) for i in cargs):
            raise QiskitError("register not in this circuit")
        for clbit in cargs:
            clbit[0].check_range(clbit[1])

    def to_instruction(self):
        """Create an Instruction out of this circuit.

        Returns:
            Instruction: a composite instruction encapsulating this circuit
                (can be decomposed back)
        """
        from qiskit.converters.circuit_to_instruction import circuit_to_instruction
        return circuit_to_instruction(self)

    def decompose(self):
        """Call a decomposition pass on this circuit,
        to decompose one level (shallow decompose).

        Returns:
            QuantumCircuit: a circuit one level decomposed
        """
        from qiskit.transpiler.passes.decompose import Decompose
        from qiskit.converters.circuit_to_dag import circuit_to_dag
        from qiskit.converters.dag_to_circuit import dag_to_circuit
        pass_ = Decompose()
        decomposed_dag = pass_.run(circuit_to_dag(self))
        return dag_to_circuit(decomposed_dag)

    def _check_compatible_regs(self, rhs):
        """Raise exception if the circuits are defined on incompatible registers"""
        list1 = self.qregs + self.cregs
        list2 = rhs.qregs + rhs.cregs
        for element1 in list1:
            for element2 in list2:
                if element2.name == element1.name:
                    if element1 != element2:
                        raise QiskitError("circuits are not compatible")

    def qasm(self):
        """Return OpenQASM string."""
        string_temp = self.header + "\n"
        string_temp += self.extension_lib + "\n"
        for register in self.qregs:
            string_temp += register.qasm() + "\n"
        for register in self.cregs:
            string_temp += register.qasm() + "\n"
        for instruction, qargs, cargs in self.data:
            if instruction.name == 'measure':
                qubit = qargs[0]
                clbit = cargs[0]
                string_temp += "%s %s[%d] -> %s[%d];\n" % (instruction.qasm(),
                                                           qubit[0].name, qubit[1],
                                                           clbit[0].name, clbit[1])
            else:
                string_temp += "%s %s;\n" % (instruction.qasm(),
                                             ",".join(["%s[%d]" % (j[0].name, j[1])
                                                       for j in qargs + cargs]))
        return string_temp

    def draw(self, scale=0.7, filename=None, style=None, output='text',
             interactive=False, line_length=None, plot_barriers=True,
             reverse_bits=False, justify=None):
        """Draw the quantum circuit

        Using the output parameter you can specify the format. The choices are:
        0. text: ASCII art string
        1. latex: high-quality images, but heavy external software dependencies
        2. matplotlib: purely in Python with no external dependencies

        Defaults to an overcomplete basis, in order to not alter gates.

        Args:
            scale (float): scale of image to draw (shrink if < 1)
            filename (str): file path to save image to
            style (dict or str): dictionary of style or file name of style
                file. You can refer to the
                :ref:`Style Dict Doc <style-dict-doc>` for more information
                on the contents.
            output (str): Select the output method to use for drawing the
                circuit. Valid choices are `text`, `latex`, `latex_source`,
                `mpl`.
            interactive (bool): when set true show the circuit in a new window
                (for `mpl` this depends on the matplotlib backend being used
                supporting this). Note when used with either the `text` or the
                `latex_source` output type this has no effect and will be
                silently ignored.
            line_length (int): sets the length of the lines generated by `text`
            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.
            justify (string): Options are `left`, `right` or `none`, if anything
                else is supplied it defaults to left justified. It refers to where
                gates should be placed in the output circuit if there is an option.
                `none` results in each gate being placed in its own column. Currently
                only supported by text drawer.

        Returns:
            PIL.Image or matplotlib.figure or str or TextDrawing:
                * PIL.Image: (output `latex`) an in-memory representation of the
                  image of the circuit diagram.
                * matplotlib.figure: (output `mpl`) a matplotlib figure object
                  for the circuit diagram.
                * str: (output `latex_source`). The LaTeX source code.
                * TextDrawing: (output `text`). A drawing that can be printed as
                  ascii art

        Raises:
            VisualizationError: when an invalid output method is selected
        """
        from qiskit.tools import visualization
        return visualization.circuit_drawer(self, scale=scale,
                                            filename=filename, style=style,
                                            output=output,
                                            interactive=interactive,
                                            line_length=line_length,
                                            plot_barriers=plot_barriers,
                                            reverse_bits=reverse_bits,
                                            justify=justify)

    def size(self):
        """Returns total number of gate operations in circuit.

        Returns:
            int: Total number of gate operations.
        """
        gate_ops = 0
        for instr, _, _ in self.data:
            if instr.name not in ['barrier', 'snapshot']:
                gate_ops += 1
        return gate_ops

    def depth(self):
        """Return circuit depth (i.e. length of critical path).
        This does not include compiler or simulator directives
        such as 'barrier' or 'snapshot'.

        Returns:
            int: Depth of circuit.

        Notes:
            The circuit depth and the DAG depth need not bt the
            same.
        """
        # Labels the registers by ints
        # and then the qubit position in
        # a register is given by reg_int+qubit_num
        reg_offset = 0
        reg_map = {}
        for reg in self.qregs+self.cregs:
            reg_map[reg.name] = reg_offset
            reg_offset += reg.size

        # A list that holds the height of each qubit
        # and classical bit.
        op_stack = [0]*reg_offset
        # Here we are playing a modified version of
        # Tetris where we stack gates, but multi-qubit
        # gates, or measurements have a block for each
        # qubit or cbit that are connected by a virtual
        # line so that they all stacked at the same depth.
        # Conditional gates act on all cbits in the register
        # they are conditioned on.
        # We do not consider barriers or snapshots as
        # They are transpiler and simulator directives.
        # The max stack height is the circuit depth.
        for instr, qargs, cargs in self.data:
            if instr.name not in ['barrier', 'snapshot']:
                levels = []
                reg_ints = []
                for ind, reg in enumerate(qargs+cargs):
                    # Add to the stacks of the qubits and
                    # cbits used in the gate.
                    reg_ints.append(reg_map[reg[0].name]+reg[1])
                    levels.append(op_stack[reg_ints[ind]] + 1)
                if instr.control:
                    # Controls operate over all bits in the
                    # classical register they use.
                    cint = reg_map[instr.control[0].name]
                    for off in range(instr.control[0].size):
                        if cint+off not in reg_ints:
                            reg_ints.append(cint+off)
                            levels.append(op_stack[cint+off]+1)

                max_level = max(levels)
                for ind in reg_ints:
                    op_stack[ind] = max_level
        return max(op_stack)

    def width(self):
        """Return number of qubits plus clbits in circuit.

        Returns:
            int: Width of circuit.

        """
        return sum(reg.size for reg in self.qregs+self.cregs)

    def count_ops(self):
        """Count each operation kind in the circuit.

        Returns:
            dict: a breakdown of how many operations of each kind.
        """
        count_ops = {}
        for instr, _, _ in self.data:
            if instr.name in count_ops.keys():
                count_ops[instr.name] += 1
            else:
                count_ops[instr.name] = 1
        return count_ops

    def num_connected_components(self, unitary_only=False):
        """How many non-entangled subcircuits can the circuit be factored to.

        Args:
            unitary_only (bool): Compute only unitary part of graph.

        Returns:
            int: Number of connected components in circuit.
        """
        # Convert registers to ints (as done in depth).
        reg_offset = 0
        reg_map = {}

        if unitary_only:
            regs = self.qregs
        else:
            regs = self.qregs+self.cregs

        for reg in regs:
            reg_map[reg.name] = reg_offset
            reg_offset += reg.size
        # Start with each qubit or cbit being its own subgraph.
        sub_graphs = [[bit] for bit in range(reg_offset)]

        num_sub_graphs = len(sub_graphs)

        # Here we are traversing the gates and looking to see
        # which of the sub_graphs the gate joins together.
        for instr, qargs, cargs in self.data:
            if unitary_only:
                args = qargs
                num_qargs = len(args)
            else:
                args = qargs+cargs
                num_qargs = len(args) + (1 if instr.control else 0)

            if num_qargs >= 2 and instr.name not in ['barrier', 'snapshot']:
                graphs_touched = []
                num_touched = 0
                # Controls necessarily join all the cbits in the
                # register that they use.
                if instr.control and not unitary_only:
                    creg = instr.control[0]
                    creg_int = reg_map[creg.name]
                    for coff in range(creg.size):
                        temp_int = creg_int+coff
                        for k in range(num_sub_graphs):
                            if temp_int in sub_graphs[k]:
                                graphs_touched.append(k)
                                num_touched += 1
                                break

                for item in args:
                    reg_int = reg_map[item[0].name]+item[1]
                    for k in range(num_sub_graphs):
                        if reg_int in sub_graphs[k]:
                            if k not in graphs_touched:
                                graphs_touched.append(k)
                                num_touched += 1
                                break

                # If the gate touches more than one subgraph
                # join those graphs together and return
                # reduced number of subgraphs
                if num_touched > 1:
                    connections = []
                    for idx in graphs_touched:
                        connections.extend(sub_graphs[idx])
                    _sub_graphs = []
                    for idx in range(num_sub_graphs):
                        if idx not in graphs_touched:
                            _sub_graphs.append(sub_graphs[idx])
                    _sub_graphs.append(connections)
                    sub_graphs = _sub_graphs
                    num_sub_graphs -= (num_touched-1)
            # Cannot go lower than one so break
            if num_sub_graphs == 1:
                break
        return num_sub_graphs

    def num_unitary_factors(self):
        """Computes the number of tensor factors in the unitary
        (quantum) part of the circuit only.
        """
        return self.num_connected_components(unitary_only=True)

    def num_tensor_factors(self):
        """Computes the number of tensor factors in the unitary
        (quantum) part of the circuit only.

        Notes:
            This is here for backwards compatibility, and will be
            removed in a future release of qiskit. You should call
            `num_unitary_factors` instead.
        """
        return self.num_unitary_factors()

    def copy(self, name=None):
        """
        Args:
          name (str): name to be given to the copied circuit, if None then the name stays the same
        Returns:
          QuantumCircuit: a deepcopy of the current circuit, with the name updated if
                          it was provided
        """
        cpy = deepcopy(self)
        if name:
            cpy.name = name
        return cpy

    @staticmethod
    def from_qasm_file(path):
        """Take in a QASM file and generate a QuantumCircuit object.

        Args:
          path (str): Path to the file for a QASM program
        Return:
          QuantumCircuit: The QuantumCircuit object for the input QASM
        """
        qasm = Qasm(filename=path)
        return _circuit_from_qasm(qasm)

    @staticmethod
    def from_qasm_str(qasm_str):
        """Take in a QASM string and generate a QuantumCircuit object.

        Args:
          qasm_str (str): A QASM program string
        Return:
          QuantumCircuit: The QuantumCircuit object for the input QASM
        """
        qasm = Qasm(data=qasm_str)
        return _circuit_from_qasm(qasm)

    @property
    def parameters(self):
        """convenience function to get the parameters defined in the parameter table"""
        return set(self._parameter_table.keys())

    def bind_parameters(self, value_dict):
        """Assign parameters to values yielding a new circuit.

        Args:
            value_dict (dict): {parameter: value, ...}

        Raises:
            QiskitError: If value_dict contains parameters not present in the circuit

        Returns:
            QuantumCircuit: copy of self with assignment substitution.
        """
        new_circuit = self.copy()

        if value_dict.keys() > self.parameters:
            raise QiskitError('Cannot bind parameters ({}) not present in the circuit.'.format(
                [str(p) for p in value_dict.keys() - self.parameters]))

        for parameter, value in value_dict.items():
            new_circuit._bind_parameter(parameter, value)
        # clear evaluated expressions
        for parameter in value_dict:
            del new_circuit._parameter_table[parameter]
        return new_circuit

    def _bind_parameter(self, parameter, value):
        """Assigns a parameter value to matching instructions in-place."""
        for (instr, param_index) in self._parameter_table[parameter]:
            instr.params[param_index] = value

def _circuit_from_qasm(qasm):
    # pylint: disable=cyclic-import
    from qiskit.converters import ast_to_dag
    from qiskit.converters import dag_to_circuit
    ast = qasm.parse()
    dag = ast_to_dag(ast)
    return dag_to_circuit(dag)


# -*- coding: utf-8 -*-

# Copyright 2019, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.

# pylint: disable=invalid-name

"""
Visualization function for pulse envelope.
"""

import numpy as np
from scipy.interpolate import CubicSpline

from qiskit.exceptions import QiskitError

def pulse_drawer(samples, duration, dt=None, interp_method='None',
                 filename=None, interactive=False,
                 dpi=150, nop=1000, size=(6, 5)):
    """Plot the interpolated envelope of pulse

    Args:
        samples (ndarray): Data points of complex pulse envelope.
        duration (int): Pulse length (number of points).
        dt (float): Time interval of samples.
        interp_method (str): Method of interpolation
            (set `None` for turn off the interpolation).
        filename (str): Name required to save pulse image.
        interactive (bool): When set true show the circuit in a new window
            (this depends on the matplotlib backend being used supporting this).
        dpi (int): Resolution of saved image.
        nop (int): Data points for interpolation.
        size (tuple): Size of figure.
    Returns:
        matplotlib.figure: A matplotlib figure object for the pulse envelope.
    Raises:
        ImportError: when the output methods requieres non-installed libraries.
        QiskitError: when invalid interpolation method is specified.
    """

    try:
        from matplotlib import pyplot as plt
    except ImportError:
        raise ImportError('pulse_drawer need matplotlib. '
                          'Run "pip install matplotlib" before.')

    if dt:
        _dt = dt
    else:
        _dt = 1

    re_y = np.real(samples)
    im_y = np.imag(samples)

    image = plt.figure(figsize=size)
    ax0 = image.add_subplot(111)

    if interp_method == 'CubicSpline':
        # spline interpolation, use mid-point of dt
        time = np.arange(0, duration + 1) * _dt + 0.5 * _dt
        cs_ry = CubicSpline(time[:-1], re_y)
        cs_iy = CubicSpline(time[:-1], im_y)

        _time = np.linspace(0, duration * _dt, nop)
        _re_y = cs_ry(_time)
        _im_y = cs_iy(_time)
    elif interp_method == 'None':
        # pseudo-DAC output
        time = np.arange(0, duration + 1) * _dt

        _time = np.r_[time[0], np.repeat(time[1:-1], 2), time[-1]]
        _re_y = np.repeat(re_y, 2)
        _im_y = np.repeat(im_y, 2)
    else:
        raise QiskitError('Invalid interpolation method "%s"' % interp_method)

    # plot
    ax0.fill_between(x=_time, y1=_re_y, y2=np.zeros_like(_time),
                     facecolor='red', alpha=0.3,
                     edgecolor='red', linewidth=1.5,
                     label='real part')
    ax0.fill_between(x=_time, y1=_im_y, y2=np.zeros_like(_time),
                     facecolor='blue', alpha=0.3,
                     edgecolor='blue', linewidth=1.5,
                     label='imaginary part')

    ax0.set_xlim(0, duration * _dt)
    ax0.grid(b=True, linestyle='-')
    ax0.legend(bbox_to_anchor=(0.5, 1.00), loc='lower center',
               ncol=2, frameon=False, fontsize=14)

    if filename:
        image.savefig(filename, dpi=dpi, bbox_inches='tight')

    plt.close(image)

    if image and interactive:
        plt.show(image)

    return image


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

"""
Implementation of Sven Jandura's swap mapper submission for the 2018 QISKit
Developer Challenge, adapted to integrate into the transpiler architecture.

The role of the mapper pass is to modify the starting circuit to be compatible
with the target device's topology (the set of two-qubit gates available on the
hardware.) To do this, the mapper will insert SWAP gates to relocate the virtual
qubits for each upcoming gate onto a set of coupled physical qubits. However, as
SWAP gates are particularly lossy, the goal is to accomplish this remapping while
introducing the fewest possible additional SWAPs.

This algorithm searches through the available combinations of SWAP gates by means
of a narrowed best first/beam search, described as follows:

- Start with a layout of virtual qubits onto physical qubits.
- Find any gates in the input circuit which can be performed with the current
  layout and mark them as mapped.
- For all possible SWAP gates, calculate the layout that would result from their
  application and rank them according to the distance of the resulting layout
  over upcoming gates (see _calc_layout_distance.)
- For the four (SEARCH_WIDTH) highest-ranking SWAPs, repeat the above process on
  the layout that would be generated if they were applied.
- Repeat this process down to a depth of four (SEARCH_DEPTH) SWAPs away from the
  initial layout, for a total of 256 (SEARCH_WIDTH^SEARCH_DEPTH) prospective
  layouts.
- Choose the layout which maximizes the number of two-qubit which could be
  performed. Add its mapped gates, including the SWAPs generated, to the
  output circuit.
- Repeat the above until all gates from the initial circuit are mapped.

For more details on the algorithm, see Sven's blog post:
https://medium.com/qiskit/improving-a-quantum-compiler-48410d7a7084

"""

from copy import deepcopy

from qiskit import QuantumRegister
from qiskit.dagcircuit import DAGCircuit
from qiskit.extensions.standard import SwapGate
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler import Layout
from qiskit.dagcircuit import DAGNode

from .barrier_before_final_measurements import BarrierBeforeFinalMeasurements

SEARCH_DEPTH = 4
SEARCH_WIDTH = 4

class LookaheadSwap(TransformationPass):
    """Map input circuit onto a backend topology via insertion of SWAPs."""

    def __init__(self, coupling_map, initial_layout=None):
        """Initialize a LookaheadSwap instance.

        Arguments:
            coupling_map (CouplingMap): CouplingMap of the target backend.
            initial_layout (Layout): The initial layout of the DAG to analyze.
        """

        super().__init__()
        self._coupling_map = coupling_map
        self.initial_layout = initial_layout
        self.requires.append(BarrierBeforeFinalMeasurements())

    def run(self, dag):
        """Run one pass of the lookahead mapper on the provided DAG.

        Args:
            dag (DAGCircuit): the directed acyclic graph to be mapped
        Returns:
            DAGCircuit: A dag mapped to be compatible with the coupling_map in
                the property_set.
        Raises:
            TranspilerError: if the coupling map or the layout are not
            compatible with the DAG
        """
        coupling_map = self._coupling_map
        ordered_virtual_gates = list(dag.serial_layers())

        if self.initial_layout is None:
            if self.property_set["layout"]:
                self.initial_layout = self.property_set["layout"]
            else:
                self.initial_layout = Layout.generate_trivial_layout(*dag.qregs.values())

        if len(dag.qubits()) != len(self.initial_layout):
            raise TranspilerError('The layout does not match the amount of qubits in the DAG')

        if len(self._coupling_map.physical_qubits) != len(self.initial_layout):
            raise TranspilerError(
                "Mappers require to have the layout to be the same size as the coupling map")

        mapped_gates = []
        layout = self.initial_layout.copy()
        gates_remaining = ordered_virtual_gates.copy()

        while gates_remaining:
            best_step = _search_forward_n_swaps(layout, gates_remaining,
                                                coupling_map)

            layout = best_step['layout']
            gates_mapped = best_step['gates_mapped']
            gates_remaining = best_step['gates_remaining']

            mapped_gates.extend(gates_mapped)

        # Preserve input DAG's name, regs, wire_map, etc. but replace the graph.
        mapped_dag = _copy_circuit_metadata(dag, coupling_map)

        for node in mapped_gates:
            mapped_dag.apply_operation_back(op=node.op, qargs=node.qargs, cargs=node.cargs)

        return mapped_dag

def _search_forward_n_swaps(layout, gates, coupling_map,
                            depth=SEARCH_DEPTH, width=SEARCH_WIDTH):
    """Search for SWAPs which allow for application of largest number of gates.

    Arguments:
        layout (Layout): Map from virtual qubit index to physical qubit index.
        gates (list): Gates to be mapped.
        coupling_map (CouplingMap): CouplingMap of the target backend.
        depth (int): Number of SWAP layers to search before choosing a result.
        width (int): Number of SWAPs to consider at each layer.
    Returns:
        dict: Describes solution step found.
            layout (Layout): Virtual to physical qubit map after SWAPs.
            gates_remaining (list): Gates that could not be mapped.
            gates_mapped (list): Gates that were mapped, including added SWAPs.

    """

    gates_mapped, gates_remaining = _map_free_gates(layout, gates, coupling_map)

    base_step = {'layout': layout,
                 'swaps_added': 0,
                 'gates_mapped': gates_mapped,
                 'gates_remaining': gates_remaining}

    if not gates_remaining or depth == 0:
        return base_step

    possible_swaps = coupling_map.get_edges()

    def _score_swap(swap):
        """Calculate the relative score for a given SWAP."""
        trial_layout = layout.copy()
        trial_layout.swap(*swap)
        return _calc_layout_distance(gates, coupling_map, trial_layout)

    ranked_swaps = sorted(possible_swaps, key=_score_swap)

    best_swap, best_step = None, None
    for swap in ranked_swaps[:width]:
        trial_layout = layout.copy()
        trial_layout.swap(*swap)
        next_step = _search_forward_n_swaps(trial_layout, gates_remaining,
                                            coupling_map, depth - 1, width)

        # ranked_swaps already sorted by distance, so distance is the tie-breaker.
        if best_swap is None or _score_step(next_step) > _score_step(best_step):
            best_swap, best_step = swap, next_step

    best_swap_gate = _swap_ops_from_edge(best_swap, layout)
    return {
        'layout': best_step['layout'],
        'swaps_added': 1 + best_step['swaps_added'],
        'gates_remaining': best_step['gates_remaining'],
        'gates_mapped': gates_mapped + best_swap_gate + best_step['gates_mapped'],
    }

def _map_free_gates(layout, gates, coupling_map):
    """Map all gates that can be executed with the current layout.

    Args:
        layout (Layout): Map from virtual qubit index to physical qubit index.
        gates (list): Gates to be mapped.
        coupling_map (CouplingMap): CouplingMap for target device topology.

    Returns:
        tuple:
            mapped_gates (list): ops for gates that can be executed, mapped onto layout.
            remaining_gates (list): gates that cannot be executed on the layout.

    """

    blocked_qubits = set()

    mapped_gates = []
    remaining_gates = []

    for gate in gates:
        # Gates without a partition (barrier, snapshot, save, load, noise) may
        # still have associated qubits. Look for them in the qargs.
        if not gate['partition']:
            qubits = [n for n in gate['graph'].nodes() if n.type == 'op'][0].qargs

            if not qubits:
                continue

            if blocked_qubits.intersection(qubits):
                blocked_qubits.update(qubits)
                remaining_gates.append(gate)
            else:
                mapped_gate = _transform_gate_for_layout(gate, layout)
                mapped_gates.append(mapped_gate)
            continue

        qubits = gate['partition'][0]

        if blocked_qubits.intersection(qubits):
            blocked_qubits.update(qubits)
            remaining_gates.append(gate)
        elif len(qubits) == 1:
            mapped_gate = _transform_gate_for_layout(gate, layout)
            mapped_gates.append(mapped_gate)
        elif coupling_map.distance(*[layout[q] for q in qubits]) == 1:
            mapped_gate = _transform_gate_for_layout(gate, layout)
            mapped_gates.append(mapped_gate)
        else:
            blocked_qubits.update(qubits)
            remaining_gates.append(gate)

    return mapped_gates, remaining_gates

def _calc_layout_distance(gates, coupling_map, layout, max_gates=None):
    """Return the sum of the distances of two-qubit pairs in each CNOT in gates
    according to the layout and the coupling.
    """

    if max_gates is None:
        max_gates = 50 + 10 * len(coupling_map.physical_qubits)

    return sum(coupling_map.distance(*[layout[q] for q in gate['partition'][0]])
               for gate in gates[:max_gates]
               if gate['partition'] and len(gate['partition'][0]) == 2)

def _score_step(step):

    """Count the mapped two-qubit gates, less the number of added SWAPs."""
    # Each added swap will add 3 ops to gates_mapped, so subtract 3.
    return len([g for g in step['gates_mapped']
                if len(g.qargs) == 2]) - 3 * step['swaps_added']

def _copy_circuit_metadata(source_dag, coupling_map):
    """Return a copy of source_dag with metadata but empty.
    Generate only a single qreg in the output DAG, matching the size of the
    coupling_map."""

    target_dag = DAGCircuit()
    target_dag.name = source_dag.name

    for creg in source_dag.cregs.values():
        target_dag.add_creg(creg)

    device_qreg = QuantumRegister(len(coupling_map.physical_qubits), 'q')
    target_dag.add_qreg(device_qreg)

    return target_dag

def _transform_gate_for_layout(gate, layout):
    """Return op implementing a virtual gate on given layout."""

    mapped_op_node = deepcopy([n for n in gate['graph'].nodes() if n.type == 'op'][0])

    # Workaround until #1816, apply mapped to qargs to both DAGNode and op
    device_qreg = QuantumRegister(len(layout.get_physical_bits()), 'q')
    mapped_qargs = [(device_qreg, layout[a]) for a in mapped_op_node.qargs]
    mapped_op_node.qargs = mapped_op_node.op.qargs = mapped_qargs

    mapped_op_node.pop('name')

    return mapped_op_node

def _swap_ops_from_edge(edge, layout):
    """Generate list of ops to implement a SWAP gate along a coupling edge."""

    device_qreg = QuantumRegister(len(layout.get_physical_bits()), 'q')
    qreg_edge = [(device_qreg, i) for i in edge]

    # TODO shouldn't be making other nodes not by the DAG!!
    return [
        DAGNode({'op': SwapGate(), 'qargs': qreg_edge, 'cargs': [], 'type': 'op'})
    ]


# -*- coding: utf-8 -*-

# Copyright 2019, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.

# pylint: disable=no-member

"""
Directed graph object for representing coupling between physical qubits.

The nodes of the graph correspond to physical qubits (represented as integers) and the
directed edges indicate which physical qubits are coupled and the permitted direction of
CNOT gates. The object has a distance function that can be used to map quantum circuits
onto a device with this coupling.
"""
import numpy as np
import networkx as nx

from qiskit.transpiler.exceptions import CouplingError

class CouplingMap:
    """
    Directed graph specifying fixed coupling.

    Nodes correspond to physical qubits (integers) and directed edges correspond
    to permitted CNOT gates
    """

    def __init__(self, couplinglist=None):
        """
        Create coupling graph. By default, the generated coupling has no nodes.

        Args:
            couplinglist (list or None): An initial coupling graph, specified as
                an adjacency list containing couplings, e.g. [[0,1], [0,2], [1,2]].
        """

        # the coupling map graph
        self.graph = nx.DiGraph()
        # a dict of dicts from node pairs to distances
        self._dist_matrix = None
        # a sorted list of physical qubits (integers) in this coupling map
        self._qubit_list = None

        if couplinglist is not None:
            for source, target in couplinglist:
                self.add_edge(source, target)

    def size(self):
        """Return the number of physical qubits in this graph."""
        return len(self.graph.nodes)

    def get_edges(self):
        """
        Gets the list of edges in the coupling graph.

        Returns:
            Tuple(int,int): Each edge is a pair of physical qubits.
        """
        return [edge for edge in self.graph.edges()]

    def add_physical_qubit(self, physical_qubit):
        """Add a physical qubit to the coupling graph as a node.

        physical_qubit (int): An integer representing a physical qubit.

        Raises:
            CouplingError: if trying to add duplicate qubit
        """
        if not isinstance(physical_qubit, int):
            raise CouplingError("Physical qubits should be integers.")
        if physical_qubit in self.physical_qubits:
            raise CouplingError(
                "The physical qubit %s is already in the coupling graph" % physical_qubit)
        self.graph.add_node(physical_qubit)
        self._dist_matrix = None  # invalidate
        self._qubit_list = None  # invalidate

    def add_edge(self, src, dst):
        """
        Add directed edge to coupling graph.

        src (int): source physical qubit
        dst (int): destination physical qubit
        """
        if src not in self.physical_qubits:
            self.add_physical_qubit(src)
        if dst not in self.physical_qubits:
            self.add_physical_qubit(dst)
        self.graph.add_edge(src, dst)
        self._dist_matrix = None  # invalidate

    def subgraph(self, nodelist):
        """Return a CouplingMap object for a subgraph of self.

        nodelist (list): list of integer node labels
        """
        subcoupling = CouplingMap()
        subcoupling.graph = self.graph.subgraph(nodelist)
        for node in nodelist:
            if node not in subcoupling.physical_qubits:
                subcoupling.add_physical_qubit(node)
        return subcoupling

    @property
    def physical_qubits(self):
        """Returns a sorted list of physical_qubits"""
        if self._qubit_list is None:
            self._qubit_list = sorted([pqubit for pqubit in self.graph.nodes])
        return self._qubit_list

    def is_connected(self):
        """
        Test if the graph is connected.

        Return True if connected, False otherwise
        """
        try:
            return nx.is_weakly_connected(self.graph)
        except nx.exception.NetworkXException:
            return False

    def _compute_distance_matrix(self):
        """Compute the full distance matrix on pairs of nodes.

        The distance map self._dist_matrix is computed from the graph using
        all_pairs_shortest_path_length.
        """
        if not self.is_connected():
            raise CouplingError("coupling graph not connected")
        lengths = nx.all_pairs_shortest_path_length(self.graph.to_undirected(as_view=True))
        lengths = dict(lengths)
        size = len(lengths)
        cmap = np.zeros((size, size))
        for idx in range(size):
            cmap[idx, np.fromiter(lengths[idx].keys(), dtype=int)] = np.fromiter(
                lengths[idx].values(), dtype=int)
        self._dist_matrix = cmap

    def distance(self, physical_qubit1, physical_qubit2):
        """Returns the undirected distance between physical_qubit1 and physical_qubit2.

        Args:
            physical_qubit1 (int): A physical qubit
            physical_qubit2 (int): Another physical qubit

        Returns:
            int: The undirected distance

        Raises:
            CouplingError: if the qubits do not exist in the CouplingMap
        """
        if physical_qubit1 not in self.physical_qubits:
            raise CouplingError("%s not in coupling graph" % (physical_qubit1,))
        if physical_qubit2 not in self.physical_qubits:
            raise CouplingError("%s not in coupling graph" % (physical_qubit2,))
        if self._dist_matrix is None:
            self._compute_distance_matrix()
        return self._dist_matrix[physical_qubit1, physical_qubit2]

    def shortest_undirected_path(self, physical_qubit1, physical_qubit2):
        """Returns the shortest undirected path between physical_qubit1 and physical_qubit2.
        Args:
            physical_qubit1 (int): A physical qubit
            physical_qubit2 (int): Another physical qubit
        Returns:
            List: The shortest undirected path
        Raises:
            CouplingError: When there is no path between physical_qubit1, physical_qubit2.
        """
        try:
            return nx.shortest_path(self.graph.to_undirected(as_view=True), source=physical_qubit1,
                                    target=physical_qubit2)
        except nx.exception.NetworkXNoPath:
            raise CouplingError(
                "Nodes %s and %s are not connected" % (str(physical_qubit1), str(physical_qubit2)))

    def __str__(self):
        """Return a string representation of the coupling graph."""
        string = ""
        if self.get_edges():
            string += "["
            string += ", ".join(["[%s, %s]" % (src, dst) for (src, dst) in self.get_edges()])
            string += "]"
        return string


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

"""THIS FILE IS DEPRECATED AND WILL BE REMOVED IN RELEASE 0.9.
"""
import warnings

from qiskit.converters import dag_to_circuit, circuit_to_dag
from qiskit.transpiler import CouplingMap
from qiskit import compiler
from qiskit.transpiler.preset_passmanagers import (default_pass_manager_simulator,
                                                   default_pass_manager)

def transpile(circuits, backend=None, basis_gates=None, coupling_map=None,
              initial_layout=None, seed_mapper=None, pass_manager=None):
    """transpile one or more circuits.

    Args:
        circuits (QuantumCircuit or list[QuantumCircuit]): circuits to compile
        backend (BaseBackend): a backend to compile for
        basis_gates (list[str]): list of basis gate names supported by the
            target. Default: ['u1','u2','u3','cx','id']
        coupling_map (list): coupling map (perhaps custom) to target in mapping

        initial_layout (Layout or dict or list):
            Initial position of virtual qubits on physical qubits. The final
            layout is not guaranteed to be the same, as the transpiler may permute
            qubits through swaps or other means.

        seed_mapper (int): random seed for the swap_mapper
        pass_manager (PassManager): a pass_manager for the transpiler stages

    Returns:
        QuantumCircuit or list[QuantumCircuit]: transpiled circuit(s).

    Raises:
        TranspilerError: in case of bad inputs to transpiler or errors in passes
    """
    warnings.warn("qiskit.transpiler.transpile() has been deprecated and will be "
                  "removed in the 0.9 release. Use qiskit.compiler.transpile() instead.",
                  DeprecationWarning)
    return compiler.transpile(circuits=circuits, backend=backend,
                              basis_gates=basis_gates, coupling_map=coupling_map,
                              initial_layout=initial_layout, seed_transpiler=seed_mapper,
                              pass_manager=pass_manager)

def transpile_dag(dag, basis_gates=None, coupling_map=None,
                  initial_layout=None, seed_mapper=None, pass_manager=None):
    """Deprecated - Use qiskit.compiler.transpile for transpiling from
    circuits to circuits.
    Transform a dag circuit into another dag circuit
    (transpile), through consecutive passes on the dag.

    Args:
        dag (DAGCircuit): dag circuit to transform via transpilation
        basis_gates (list[str]): list of basis gate names supported by the
            target. Default: ['u1','u2','u3','cx','id']
        coupling_map (list): A graph of coupling::

            [
             [control0(int), target0(int)],
             [control1(int), target1(int)],
            ]

            eg. [[0, 2], [1, 2], [1, 3], [3, 4]}

        initial_layout (Layout or None): A layout object
        seed_mapper (int): random seed_mapper for the swap mapper
        pass_manager (PassManager): pass manager instance for the transpilation process
            If None, a default set of passes are run.
            Otherwise, the passes defined in it will run.
            If contains no passes in it, no dag transformations occur.

    Returns:
        DAGCircuit: transformed dag
    """
    warnings.warn("transpile_dag has been deprecated and will be removed in the "
                  "0.9 release. Circuits can be transpiled directly to other "
                  "circuits with the transpile function.", DeprecationWarning)

    if basis_gates is None:
        basis_gates = ['u1', 'u2', 'u3', 'cx', 'id']

    if pass_manager is None:
        # default set of passes

        # if a coupling map is given compile to the map
        if coupling_map:
            pass_manager = default_pass_manager(basis_gates,
                                                CouplingMap(coupling_map),
                                                initial_layout,
                                                seed_transpiler=seed_mapper)
        else:
            pass_manager = default_pass_manager_simulator(basis_gates)

    # run the passes specified by the pass manager
    # TODO return the property set too. See #1086
    name = dag.name
    circuit = dag_to_circuit(dag)
    circuit = pass_manager.run(circuit)
    dag = circuit_to_dag(circuit)
    dag.name = name

    return dag


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

"""
controlled-u1 gate.
"""
from qiskit.circuit import CompositeGate
from qiskit.circuit import Gate
from qiskit.circuit import QuantumCircuit
from qiskit.circuit import QuantumRegister
from qiskit.circuit.decorators import _op_expand, _to_bits
from qiskit.extensions.standard.u1 import U1Gate
from qiskit.extensions.standard.cx import CnotGate

class Cu1Gate(Gate):
    """controlled-u1 gate."""

    def __init__(self, theta):
        """Create new cu1 gate."""
        super().__init__("cu1", 2, [theta])

    def _define(self):
        """
        gate cu1(lambda) a,b
        { u1(lambda/2) a; cx a,b;
          u1(-lambda/2) b; cx a,b;
          u1(lambda/2) b;
        }
        """
        definition = []
        q = QuantumRegister(2, "q")
        rule = [
            (U1Gate(self.params[0] / 2), [q[0]], []),
            (CnotGate(), [q[0], q[1]], []),
            (U1Gate(-self.params[0] / 2), [q[1]], []),
            (CnotGate(), [q[0], q[1]], []),
            (U1Gate(self.params[0] / 2), [q[1]], [])
        ]
        for inst in rule:
            definition.append(inst)
        self.definition = definition

    def inverse(self):
        """Invert this gate."""
        return Cu1Gate(-self.params[0])

@_to_bits(2)
@_op_expand(2)
def cu1(self, theta, ctl, tgt):
    """Apply cu1 from ctl to tgt with angle theta."""
    return self.append(Cu1Gate(theta), [ctl, tgt], [])

QuantumCircuit.cu1 = cu1
CompositeGate.cu1 = cu1


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

"""
Instruction collection.
"""
from qiskit.exceptions import QiskitError
from .instruction import Instruction

class InstructionSet:
    """Instruction collection, and their contexts."""

    def __init__(self):
        """New collection of instructions.

        The context (qargs and cargs that each instruction is attached to),
        is also stored separately for each instruction.
        """
        self.instructions = []
        self.qargs = []
        self.cargs = []

    def __len__(self):
        """Return number of instructions in set"""
        return len(self.instructions)

    def __getitem__(self, i):
        """Return instruction at index"""
        return self.instructions[i]

    def add(self, gate, qargs, cargs):
        """Add an instruction and its context (where it's attached)."""
        if not isinstance(gate, Instruction):
            raise QiskitError("attempt to add non-Instruction" +
                              " to InstructionSet")
        self.instructions.append(gate)
        self.qargs.append(qargs)
        self.cargs.append(cargs)

    def inverse(self):
        """Invert all instructions."""
        for index, instruction in enumerate(self.instructions):
            self.instructions[index] = instruction.inverse()
        return self

    def q_if(self, *qregs):
        """Add controls to all instructions."""
        for gate in self.instructions:
            gate.q_if(*qregs)
        return self

    def c_if(self, classical, val):
        """Add classical control register to all instructions."""
        for gate in self.instructions:
            gate.c_if(classical, val)
        return self