'''
.. module:: skrf.plotting
========================================
plotting (:mod:`skrf.plotting`)
========================================


This module provides general plotting functions.

Plots and Charts
------------------

.. autosummary::
    :toctree: generated/

    smith
    plot_smith
    plot_rectangular
    plot_polar
    plot_complex_rectangular
    plot_complex_polar
    plot_v_frequency
    plot_it_all
    
    plot_minmax_bounds_component
    plot_minmax_bounds_s_db
    plot_minmax_bounds_s_db10
    plot_minmax_bounds_s_time_db
    
    plot_uncertainty_bounds_component
    plot_uncertainty_bounds_s
    plot_uncertainty_bounds_s_db
    plot_uncertainty_bounds_s_time_db
    
    plot_passivity    
    plot_logsigma
    
    plot_circuit_graph

    plot_contour

Misc Functions
-----------------

.. autosummary::
    :toctree: generated/

    stylely
    save_all_figs
    add_markers_to_lines
    legend_off
    func_on_all_figs
    scrape_legend
    signature

'''
import os

import matplotlib as mpl
# if running on remote mode on a linux server which does not have a display (like Docker images for CI)
if os.name == 'posix' and not os.environ.get('DISPLAY', '') and mpl.rcParams['backend'] != 'agg': 
    print('No display found. Using non-interactive Agg backend.') 
    # https://matplotlib.org/faq/usage_faq.html 
    mpl.use('Agg') 
from matplotlib import ticker
import matplotlib.pyplot as plb
import numpy as npy
from matplotlib.patches import Circle   # for drawing smith chart
from matplotlib.pyplot import quiver
from matplotlib import rcParams
from matplotlib.dates import date2num

from . import network, frequency, calibration, networkSet, circuit
from . import mathFunctions as mf
from . util import now_string_2_dt
import matplotlib.tri as tri

try:
    import networkx as nx
except ImportError as e:
    pass

#from matplotlib.lines import Line2D            # for drawing smith chart

SI_PREFIXES_ASCII = 'yzafpnum kMGTPEZY'
SI_CONVERSION = dict([(key, 10**((8-i)*3)) for i, key in enumerate(SI_PREFIXES_ASCII)])


def scale_frequency_ticks(ax, funit):
    if funit.lower() == "hz":
        prefix = " "
        scale = 1
    elif len(funit) == 3:
        prefix = funit[0]
        scale = SI_CONVERSION[prefix]
    else:
        raise ValueError("invalid funit {}".format(funit))
    ticks_x = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x * scale))
    ax.xaxis.set_major_formatter(ticks_x)


def smith(smithR=1, chart_type = 'z', draw_labels = False, border=False,
    ax=None, ref_imm = 1.0, draw_vswr=None):
    '''
    plots the smith chart of a given radius

    Parameters
    -----------
    smithR : number
            radius of smith chart
    chart_type : ['z','y','zy', 'yz']
            Contour type. Possible values are
             * *'z'* : lines of constant impedance
             * *'y'* : lines of constant admittance
             * *'zy'* : lines of constant impedance stronger than admittance
             * *'yz'* : lines of constant admittance stronger than impedance
    draw_labels : Boolean
             annotate real and imaginary parts of impedance on the
             chart (only if smithR=1)
    border : Boolean
        draw a rectangular border with axis ticks, around the perimeter
        of the figure. Not used if draw_labels = True

    ax : matplotlib.axes object
            existing axes to draw smith chart on

    ref_imm : number
            Reference immittance for center of Smith chart. Only changes
            labels, if printed.

    draw_vswr : list of numbers, Boolean or None
        draw VSWR circles. If True, default values are used.

    '''
    ##TODO: fix this function so it doesnt suck
    if ax == None:
        ax1 = plb.gca()
    else:
        ax1 = ax

    # contour holds matplotlib instances of: pathes.Circle, and lines.Line2D, which
    # are the contours on the smith chart
    contour = []

    # these are hard-coded on purpose,as they should always be present
    rHeavyList = [0,1]
    xHeavyList = [1,-1]

    #TODO: fix this
    # these could be dynamically coded in the future, but work good'nuff for now
    if not draw_labels:
        rLightList = npy.logspace(3,-5,9,base=.5)
        xLightList = npy.hstack([npy.logspace(2,-5,8,base=.5), -1*npy.logspace(2,-5,8,base=.5)])
    else:
        rLightList = npy.array( [ 0.2, 0.5, 1.0, 2.0, 5.0 ] )
        xLightList = npy.array( [ 0.2, 0.5, 1.0, 2.0 , 5.0, -0.2, -0.5, -1.0, -2.0, -5.0 ] )

    # vswr lines
    if isinstance(draw_vswr, (tuple,list)):
        vswrVeryLightList = draw_vswr
    elif draw_vswr is True:
        # use the default I like
        vswrVeryLightList = [1.5, 2.0, 3.0, 5.0]
    else:
        vswrVeryLightList = []

    # cheap way to make a ok-looking smith chart at larger than 1 radii
    if smithR > 1:
        rMax = (1.+smithR)/(1.-smithR)
        rLightList = npy.hstack([ npy.linspace(0,rMax,11)  , rLightList ])

    if chart_type.startswith('y'):
        y_flip_sign = -1
    else:
        y_flip_sign = 1

    # draw impedance and/or admittance
    both_charts = chart_type in ('zy', 'yz')


    # loops through Verylight, Light and Heavy lists and draws circles using patches
    # for analysis of this see R.M. Weikles Microwave II notes (from uva)

    superLightColor = dict(ec='whitesmoke', fc='none')
    veryLightColor = dict(ec='lightgrey', fc='none')
    lightColor = dict(ec='grey', fc='none')
    heavyColor = dict(ec='black', fc='none')

    # vswr circules verylight
    for vswr in vswrVeryLightList:
        radius = (vswr-1.0) / (vswr+1.0)
        contour.append( Circle((0, 0), radius, **veryLightColor))

    # impedance/admittance circles
    for r in rLightList:
        center = (r/(1.+r)*y_flip_sign,0 )
        radius = 1./(1+r)
        if both_charts:
            contour.insert(0, Circle((-center[0], center[1]), radius, **superLightColor))
        contour.append(Circle(center, radius, **lightColor))
    for x in xLightList:
        center = (1*y_flip_sign,1./x)
        radius = 1./x
        if both_charts:
            contour.insert(0, Circle( (-center[0], center[1]), radius, **superLightColor))
        contour.append(Circle(center, radius, **lightColor))

    for r in rHeavyList:
        center = (r/(1.+r)*y_flip_sign,0 )
        radius = 1./(1+r)
        contour.append(Circle(center, radius, **heavyColor))
    for x in xHeavyList:
        center = (1*y_flip_sign,1./x)
        radius = 1./x
        contour.append(Circle(center, radius, **heavyColor))

    # clipping circle
    clipc = Circle( [0,0], smithR, ec='k',fc='None',visible=True)
    ax1.add_patch( clipc)

    #draw x and y axis
    ax1.axhline(0, color='k', lw=.1, clip_path=clipc)
    ax1.axvline(1*y_flip_sign, color='k', clip_path=clipc)
    ax1.grid(0)
    # Set axis limits by plotting white points so zooming works properly
    ax1.plot(smithR*npy.array([-1.1, 1.1]), smithR*npy.array([-1.1, 1.1]), 'w.', markersize = 0)
    ax1.axis('image') # Combination of 'equal' and 'tight'


    if not border:
        ax1.yaxis.set_ticks([])
        ax1.xaxis.set_ticks([])
        for loc, spine in ax1.spines.items():
            spine.set_color('none')


    if draw_labels:
        #Clear axis
        ax1.yaxis.set_ticks([])
        ax1.xaxis.set_ticks([])
        for loc, spine in ax1.spines.items():
            spine.set_color('none')

        # Make annotations only if the radius is 1
        if smithR == 1:
            #Make room for annotation
            ax1.plot(npy.array([-1.25, 1.25]), npy.array([-1.1, 1.1]), 'w.', markersize = 0)
            ax1.axis('image')

            #Annotate real part
            for value in rLightList:
                # Set radius of real part's label; offset slightly left (Z
                # chart, y_flip_sign == 1) or right (Y chart, y_flip_sign == -1)
                # so label doesn't overlap chart's circles
                rho = (value - 1)/(value + 1) - y_flip_sign*0.01
                if y_flip_sign == 1:
                    halignstyle = "right"
                else:
                    halignstyle = "left"
                if y_flip_sign == -1:  # 'y' and 'yz' charts
                    value = 1/value                          
                ax1.annotate(str(value*ref_imm), xy=(rho*smithR, 0.01),
                    xytext=(rho*smithR, 0.01), ha = halignstyle, va = "baseline")

            #Annotate imaginary part
            radialScaleFactor = 1.01 # Scale radius of label position by this
                                     # factor. Making it >1 places the label
                                     # outside the Smith chart's circle
            for value in xLightList:
                #Transforms from complex to cartesian
                S = (1j*value - 1) / (1j*value + 1)
                S *= smithR * radialScaleFactor
                rhox = S.real
                rhoy = S.imag * y_flip_sign

                # Choose alignment anchor point based on label's value
                if ((value == 1.0) or (value == -1.0)):
                    halignstyle = "center"
                elif (rhox < 0.0):
                    halignstyle = "right"
                else:
                    halignstyle = "left"

                if (rhoy < 0):
                    valignstyle = "top"
                else:
                    valignstyle = "bottom"
                if y_flip_sign == -1:  # 'y' and 'yz' charts
                    value = 1/value                    
                #Annotate value
                ax1.annotate(str(value*ref_imm) + 'j', xy=(rhox, rhoy),
                             xytext=(rhox, rhoy), ha = halignstyle, va = valignstyle)

            #Annotate 0 and inf
            if y_flip_sign == 1:  # z and zy charts
                label_left, label_right = '0.0', '$\infty$'
            else:  # y and yz charts
                label_left, label_right = '$\infty$', '0.0'
            ax1.annotate(label_left, xy=(-1.02, 0), xytext=(-1.02, 0),
                             ha = "right", va = "center")
            ax1.annotate(label_right, xy=(radialScaleFactor, 0), xytext=(radialScaleFactor, 0),
                             ha = "left", va = "center")

            # annotate vswr circles
            for vswr in vswrVeryLightList:
                rhoy = (vswr-1.0) / (vswr+1.0)

                ax1.annotate(str(vswr), xy=(0, rhoy*smithR),
                    xytext=(0, rhoy*smithR), ha="center", va="bottom",
                    color='grey', size='smaller')

    # loop though contours and draw them on the given axes
    for currentContour in contour:
        cc=ax1.add_patch(currentContour)
        cc.set_clip_path(clipc)


def plot_rectangular(x, y, x_label=None, y_label=None, title=None,
                     show_legend=True, axis='tight', ax=None, *args, **kwargs):
    '''
    plots rectangular data and optionally label axes.

    Parameters
    ------------
    x : array-like, of complex data
        data to plot
    y : array-like, of complex data
        data to plot
    x_label : string
        x-axis label
    y_label : string
        y-axis label
    title : string
        plot title
    show_legend : Boolean
        controls the drawing of the legend
    axis : str
        whether or not to autoscale the axis
    ax : :class:`matplotlib.axes.AxesSubplot` object
        axes to draw on
    *args, **kwargs : passed to pylab.plot

    '''
    if ax is None:
        ax = plb.gca()

    my_plot = ax.plot(x, y, *args, **kwargs)

    if x_label is not None:
        ax.set_xlabel(x_label)

    if y_label is not None:
        ax.set_ylabel(y_label)

    if title is not None:
        ax.set_title(title)

    if show_legend:
        # only show legend if they provide a label
        if 'label' in kwargs:
            ax.legend()

    if axis is not None:
        ax.autoscale(True, 'x', True)
        ax.autoscale(True, 'y', False)

    if plb.isinteractive():
        plb.draw()

    return my_plot


def plot_polar(theta, r, x_label=None, y_label=None, title=None,
    show_legend=True, axis_equal=False, ax=None, *args, **kwargs):
    '''
    plots polar data on a polar plot and optionally label axes.

    Parameters
    ------------
    theta : array-like
        data to plot
    r : array-like

    x_label : string
        x-axis label
    y_label : string
        y-axis label
    title : string
        plot title
    show_legend : Boolean
        controls the drawing of the legend
    ax : :class:`matplotlib.axes.AxesSubplot` object
        axes to draw on
    *args,**kwargs : passed to pylab.plot

    See Also
    ----------
    plot_rectangular : plots rectangular data
    plot_complex_rectangular : plot complex data on complex plane
    plot_polar : plot polar data
    plot_complex_polar : plot complex data on polar plane
    plot_smith : plot complex data on smith chart

    '''
    if ax is None:
        ax = plb.gca(polar=True)

    ax.plot(theta, r, *args, **kwargs)

    if x_label is not None:
        ax.set_xlabel(x_label)

    if y_label is not None:
        ax.set_ylabel(y_label)

    if title is not None:
        ax.set_title(title)

    if show_legend:
        # only show legend if they provide a label
        if 'label' in kwargs:
            ax.legend()

    if axis_equal:
        ax.axis('equal')

    if plb.isinteractive():
        plb.draw()

def plot_complex_rectangular(z, x_label='Real', y_label='Imag',
    title='Complex Plane', show_legend=True, axis='equal', ax=None,
    *args, **kwargs):
    '''
    plot complex data on the complex plane

    Parameters
    ------------
    z : array-like, of complex data
        data to plot
    x_label : string
        x-axis label
    y_label : string
        y-axis label
    title : string
        plot title
    show_legend : Boolean
        controls the drawing of the legend
    ax : :class:`matplotlib.axes.AxesSubplot` object
        axes to draw on
    *args,**kwargs : passed to pylab.plot

    See Also
    ----------
    plot_rectangular : plots rectangular data
    plot_complex_rectangular : plot complex data on complex plane
    plot_polar : plot polar data
    plot_complex_polar : plot complex data on polar plane
    plot_smith : plot complex data on smith chart

    '''
    x = npy.real(z)
    y = npy.imag(z)
    plot_rectangular(x=x, y=y, x_label=x_label, y_label=y_label,
        title=title, show_legend=show_legend, axis=axis,
        ax=ax, *args, **kwargs)


def plot_complex_polar(z, x_label=None, y_label=None,
    title=None, show_legend=True, axis_equal=False, ax=None,
    *args, **kwargs):
    '''
    plot complex data in polar format.

    Parameters
    ------------
    z : array-like, of complex data
        data to plot
    x_label : string
        x-axis label
    y_label : string
        y-axis label
    title : string
        plot title
    show_legend : Boolean
        controls the drawing of the legend
    ax : :class:`matplotlib.axes.AxesSubplot` object
        axes to draw on
    *args,**kwargs : passed to pylab.plot

    See Also
    ----------
    plot_rectangular : plots rectangular data
    plot_complex_rectangular : plot complex data on complex plane
    plot_polar : plot polar data
    plot_complex_polar : plot complex data on polar plane
    plot_smith : plot complex data on smith chart
    '''
    theta = npy.angle(z)
    r = npy.abs(z)
    plot_polar(theta=theta, r=r, x_label=x_label, y_label=y_label,
        title=title, show_legend=show_legend, axis_equal=axis_equal,
        ax=ax, *args, **kwargs)


def plot_smith(s, smith_r=1, chart_type='z', x_label='Real',
    y_label='Imaginary', title='Complex Plane', show_legend=True,
    axis='equal', ax=None, force_chart = False, draw_vswr=None, *args, **kwargs):
    '''
    plot complex data on smith chart

    Parameters
    ------------
    s : complex array-like
        reflection-coeffient-like data to plot
    smith_r : number
        radius of smith chart
    chart_type : ['z','y']
        Contour type for chart.
         * *'z'* : lines of constant impedance
         * *'y'* : lines of constant admittance
    x_label : string
        x-axis label
    y_label : string
        y-axis label
    title : string
        plot title
    show_legend : Boolean
        controls the drawing of the legend
    axis_equal: Boolean
        sets axis to be equal increments (calls axis('equal'))
    force_chart : Boolean
        forces the re-drawing of smith chart
    ax : :class:`matplotlib.axes.AxesSubplot` object
        axes to draw on
    *args,**kwargs : passed to pylab.plot

    See Also
    ----------
    plot_rectangular : plots rectangular data
    plot_complex_rectangular : plot complex data on complex plane
    plot_polar : plot polar data
    plot_complex_polar : plot complex data on polar plane
    plot_smith : plot complex data on smith chart
    '''

    if ax is None:
        ax = plb.gca()

    # test if smith chart is already drawn
    if not force_chart:
        if len(ax.patches) == 0:
            smith(ax=ax, smithR = smith_r, chart_type=chart_type, draw_vswr=draw_vswr)

    plot_complex_rectangular(s, x_label=x_label, y_label=y_label,
        title=title, show_legend=show_legend, axis=axis,
        ax=ax, *args, **kwargs)

    ax.axis(smith_r*npy.array([-1.1, 1.1, -1.1, 1.1]))
    if plb.isinteractive():
        plb.draw()


def subplot_params(ntwk, param='s', proj='db', size_per_port=4, newfig=True,
                   add_titles=True, keep_it_tight=True,  subplot_kw={}, *args, **kw):
    '''
    Plot all networks parameters individually on subplots

    Parameters
    --------------


    '''
    if newfig:
        f,axs= plb.subplots(ntwk.nports,ntwk.nports,
                            figsize =(size_per_port*ntwk.nports,
                                      size_per_port*ntwk.nports ),
                                      **subplot_kw)
    else:
        f = plb.gcf()
        axs = npy.array(f.get_axes())

    for ports,ax in zip(ntwk.port_tuples, axs.flatten()):
        plot_func = ntwk.__getattribute__('plot_%s_%s'%(param, proj))
        plot_func(m=ports[0], n=ports[1], ax=ax,*args, **kw)
        if add_titles:
            ax.set_title('%s%i%i'%(param.upper(),ports[0]+1, ports[1]+1))
    if keep_it_tight:
       plb.tight_layout()
    return f,axs

def shade_bands(edges, y_range=None,cmap='prism', **kwargs):
    '''
    Shades frequency bands.

    when plotting data over a set of frequency bands it is nice to
    have each band visually separated from the other. The kwarg `alpha`
    is useful.

    Parameters
    --------------
    edges : array-like
        x-values separating regions of a given shade
    y_range : tuple
        y-values to shade in
    cmap : str
        see matplotlib.cm  or matplotlib.colormaps for acceptable values
    \*\* : key word arguments
        passed to `matplotlib.fill_between`

    Examples
    -----------
    >>> rf.shade_bands([325,500,750,1100], alpha=.2)
    '''
    cmap = plb.cm.get_cmap(cmap)
    y_range=plb.gca().get_ylim()
    axis = plb.axis()
    for k in range(len(edges)-1):
        plb.fill_between(
            [edges[k],edges[k+1]],
            y_range[0], y_range[1],
            color = cmap(1.0*k/len(edges)),
            **kwargs)
    plb.axis(axis)

def save_all_figs(dir = './', format=None, replace_spaces = True, echo = True):
    '''
    Save all open Figures to disk.

    Parameters
    ------------
    dir : string
            path to save figures into
    format : None, or list of strings
            the types of formats to save figures as. The elements of this
            list are passed to :matplotlib:`savefig`. This is a list so that
            you can save each figure in multiple formats.
    echo : bool
            True prints filenames as they are saved
    '''
    if dir[-1] != '/':
        dir = dir + '/'
    for fignum in plb.get_fignums():
        fileName = plb.figure(fignum).get_axes()[0].get_title()
        if replace_spaces:
            fileName = fileName.replace(' ','_')
        if fileName == '':
            fileName = 'unnamedPlot'
        if format is None:
            plb.savefig(dir+fileName)
            if echo:
                print((dir+fileName))
        else:
            for fmt in format:
                plb.savefig(dir+fileName+'.'+fmt, format=fmt)
                if echo:
                    print((dir+fileName+'.'+fmt))
saf = save_all_figs

def add_markers_to_lines(ax=None,marker_list=['o','D','s','+','x'], markevery=10):
    '''
    adds markers to existing lings on a plot

    this is convinient if you have already have a plot made, but then
    need to add markers afterwards, so that it can be interpreted in
    black and white. The markevery argument makes the markers less
    frequent than the data, which is generally what you want.

    Parameters
    -----------
    ax : matplotlib.Axes
        axis which to add markers to, defaults to gca()
    marker_list : list of marker characters
        see matplotlib.plot help for possible marker characters
    markevery : int
        markevery number of points with a marker.

    '''
    if ax is None:
        ax=plb.gca()
    lines = ax.get_lines()
    if len(lines) > len (marker_list ):
        marker_list *= 3
    [k[0].set_marker(k[1]) for k in zip(lines, marker_list)]
    [line.set_markevery(markevery) for line in lines]

def legend_off(ax=None):
    '''
    turn off the legend for a given axes.

    if no axes is given then it will use current axes.

    Parameters
    -----------
    ax : matplotlib.Axes object
        axes to operate on
    '''
    if ax is None:
        plb.gca().legend_.set_visible(0)
    else:
        ax.legend_.set_visible(0)

def scrape_legend(n=None, ax=None):
    '''
    scrapes a legend with redundant labels

    Given a legend of m entries of n groups, this will remove all but
    every m/nth entry. This is used when you plot many lines representing
    the same thing, and only want one label entry in the legend  for the
    whole ensemble of lines

    '''

    if ax is None:
        ax = plb.gca()

    handles, labels = ax.get_legend_handles_labels()

    if n is None:
        n =len ( set(labels))

    if n>len(handles):
        raise ValueError('number of entries is too large')

    k_list = [int(k) for k in npy.linspace(0,len(handles)-1,n)]
    ax.legend([handles[k] for k in k_list], [labels[k] for k in k_list])

def func_on_all_figs(func, *args, **kwargs):
    '''
    runs a function after making all open figures current.

    useful if you need to change the properties of many open figures
    at once, like turn off the grid.

    Parameters
    ----------
    func : function
        function to call
    \*args, \*\*kwargs : pased to func

    Examples
    ----------
    >>> rf.func_on_all_figs(grid,alpha=.3)
    '''
    for fig_n in plb.get_fignums():
        fig = plb.figure(fig_n)
        for ax_n in fig.axes:
            fig.add_axes(ax_n) # trick to make axes current
            func(*args, **kwargs)
            plb.draw()

foaf = func_on_all_figs

def plot_vector(a, off=0+0j, *args, **kwargs):
    '''
    plot a 2d vector
    '''
    return quiver(off.real,off.imag,a.real,a.imag,scale_units ='xy',
           angles='xy',scale=1, *args, **kwargs)


def colors():
    return [c['color'] for c in rcParams['axes.prop_cycle']]


PRIMARY_PROPERTIES = network.PRIMARY_PROPERTIES
COMPONENT_FUNC_DICT = network.COMPONENT_FUNC_DICT
Y_LABEL_DICT = network.Y_LABEL_DICT


# TODO: remove this as it takes up ~70% cpu time of this init
def setup_matplotlib_plotting():
    frequency.Frequency.labelXAxis = labelXAxis
    frequency.Frequency.plot = plot_v_frequency

    __generate_plot_functions(network.Network)
    network.Network.plot = plot
    network.Network.plot_passivity = plot_passivity
    network.Network.plot_reciprocity = plot_reciprocity
    network.Network.plot_reciprocity2 = plot_reciprocity2
    network.Network.plot_s_db_time = plot_s_db_time
    network.Network.plot_s_smith = plot_s_smith
    network.Network.plot_it_all = plot_it_all

    calibration.Calibration.plot_errors = plot_calibration_errors
    calibration.Calibration.plot_caled_ntwks = plot_caled_ntwks
    calibration.Calibration.plot_residuals = plot_residuals

    networkSet.NetworkSet.animate = animate
    networkSet.NetworkSet.plot_uncertainty_bounds_component = plot_uncertainty_bounds_component
    networkSet.NetworkSet.plot_minmax_bounds_component = plot_minmax_bounds_component
    networkSet.NetworkSet.plot_uncertainty_bounds_s_db = plot_uncertainty_bounds_s_db
    networkSet.NetworkSet.plot_minmax_bounds_s_db = plot_minmax_bounds_s_db
    networkSet.NetworkSet.plot_minmax_bounds_s_db10 = plot_minmax_bounds_s_db10
    networkSet.NetworkSet.plot_uncertainty_bounds_s_time_db = plot_uncertainty_bounds_s_time_db
    networkSet.NetworkSet.plot_minmax_bounds_s_time_db = plot_minmax_bounds_s_time_db
    networkSet.NetworkSet.plot_uncertainty_decomposition = plot_uncertainty_decomposition
    networkSet.NetworkSet.plot_uncertainty_bounds_s = plot_uncertainty_bounds_s
    networkSet.NetworkSet.plot_logsigma = plot_logsigma
    networkSet.NetworkSet.signature = signature

    circuit.Circuit.plot_graph = plot_circuit_graph

def __generate_plot_functions(self):
    '''
    '''
    for prop_name in PRIMARY_PROPERTIES:

        def plot_prop_polar(self,
                            m=None, n=None, ax=None,
                            show_legend=True, prop_name=prop_name, *args, **kwargs):

            # create index lists, if not provided by user
            if m is None:
                M = range(self.number_of_ports)
            else:
                M = [m]
            if n is None:
                N = range(self.number_of_ports)
            else:
                N = [n]

            if 'label'  not in kwargs.keys():
                gen_label = True
            else:
                gen_label = False

            # was_interactive = plb.isinteractive
            # if was_interactive:
            #     plb.interactive(False)

            for m in M:
                for n in N:
                    # set the legend label for this trace to the networks
                    # name if it exists, and they didn't pass a name key in
                    # the kwargs
                    if gen_label:
                        if self.name is None:
                            if plb.rcParams['text.usetex']:
                                label_string = '$%s_{%i%i}$'%\
                                (prop_name[0].upper(),m+1,n+1)
                            else:
                                label_string = '%s%i%i'%\
                                (prop_name[0].upper(),m+1,n+1)
                        else:
                            if plb.rcParams['text.usetex']:
                                label_string = self.name+', $%s_{%i%i}$'%\
                                (prop_name[0].upper(),m+1,n+1)
                            else:
                                label_string = self.name+', %s%i%i'%\
                                (prop_name[0].upper(),m+1,n+1)
                        kwargs['label'] = label_string

                    # plot the desired attribute vs frequency
                    plot_complex_polar(
                        z = getattr(self,prop_name)[:,m,n],
                         show_legend = show_legend, ax = ax,
                        *args, **kwargs)

            # if was_interactive:
            #     plb.interactive(True)
            #     plb.draw()
            #     plb.show()

        plot_prop_polar.__doc__ = '''
plot the Network attribute :attr:`%s` vs frequency.

Parameters
-----------
m : int, optional
    first index of s-parameter matrix, if None will use all
n : int, optional
    secon index of the s-parameter matrix, if None will use all
ax : :class:`matplotlib.Axes` object, optional
    An existing Axes object to plot on
show_legend : Boolean
    draw legend or not
attribute : string
    Network attribute to plot
y_label : string, optional
    the y-axis label

\*args,\\**kwargs : arguments, keyword arguments
    passed to :func:`matplotlib.plot`

Notes
-------
This function is dynamically generated upon Network
initialization. This is accomplished by calling
:func:`plot_vs_frequency_generic`

Examples
------------
>>> myntwk.plot_%s(m=1,n=0,color='r')
''' % (prop_name, prop_name)
        # setattr(self.__class__,'plot_%s_polar'%(prop_name), \
        setattr(self, 'plot_%s_polar'%(prop_name), plot_prop_polar)

        def plot_prop_rect(self,
                           m=None, n=None, ax=None,
                           show_legend=True, prop_name=prop_name, *args, **kwargs):

            # create index lists, if not provided by user
            if m is None:
                M = range(self.number_of_ports)
            else:
                M = [m]
            if n is None:
                N = range(self.number_of_ports)
            else:
                N = [n]

            if 'label'  not in kwargs.keys():
                gen_label = True
            else:
                gen_label = False

            #was_interactive = plb.isinteractive
            #if was_interactive:
            #    plb.interactive(False)

            for m in M:
                for n in N:
                    # set the legend label for this trace to the networks
                    # name if it exists, and they didn't pass a name key in
                    # the kwargs
                    if gen_label:
                        if self.name is None:
                            if plb.rcParams['text.usetex']:
                                label_string = '$%s_{%i%i}$'%\
                                (prop_name[0].upper(),m+1,n+1)
                            else:
                                label_string = '%s%i%i'%\
                                (prop_name[0].upper(),m+1,n+1)
                        else:
                            if plb.rcParams['text.usetex']:
                                label_string = self.name+', $%s_{%i%i}$'%\
                                (prop_name[0].upper(),m+1,n+1)
                            else:
                                label_string = self.name+', %s%i%i'%\
                                (prop_name[0].upper(),m+1,n+1)
                        kwargs['label'] = label_string

                    # plot the desired attribute vs frequency
                    plot_complex_rectangular(
                        z=getattr(self, prop_name)[:, m, n],
                        show_legend=show_legend, ax=ax,
                        *args, **kwargs)

            #if was_interactive:
            #    plb.interactive(True)
            #    plb.draw()
            #    plb.show()

        plot_prop_rect.__doc__ = '''
plot the Network attribute :attr:`%s` vs frequency.

Parameters
-----------
m : int, optional
    first index of s-parameter matrix, if None will use all
n : int, optional
    secon index of the s-parameter matrix, if None will use all
ax : :class:`matplotlib.Axes` object, optional
    An existing Axes object to plot on
show_legend : Boolean
    draw legend or not
attribute : string
    Network attribute to plot
y_label : string, optional
    the y-axis label

\*args,\\**kwargs : arguments, keyword arguments
    passed to :func:`matplotlib.plot`

Notes
-------
This function is dynamically generated upon Network
initialization. This is accomplished by calling
:func:`plot_vs_frequency_generic`

Examples
------------
>>> myntwk.plot_%s(m=1,n=0,color='r')
''' % (prop_name, prop_name)

        # setattr(self.__class__,'plot_%s_complex'%(prop_name), \
        setattr(self,'plot_%s_complex'%(prop_name), \
            plot_prop_rect)


        for func_name in COMPONENT_FUNC_DICT:
            attribute = '%s_%s' % (prop_name, func_name)
            y_label = Y_LABEL_DICT[func_name]

            def plot_func(self,  m=None, n=None, ax=None,
                          show_legend=True, attribute=attribute,
                          y_label=y_label, logx=False, pad=0, window='hamming', z0=50, *args, **kwargs):

                # create index lists, if not provided by user
                if m is None:
                    M = range(self.number_of_ports)
                else:
                    M = [m]
                if n is None:
                    N = range(self.number_of_ports)
                else:
                    N = [n]

                if 'label'  not in kwargs.keys():
                    gen_label = True
                else:
                    gen_label = False

                #TODO: turn off interactive plotting for performance
                # this didnt work because it required a show()
                # to be called, which in turn, disrupted testCases
                #
                # was_interactive = plb.isinteractive
                # if was_interactive:
                #     plb.interactive(False)
                for m in M:
                    for n in N:
                        # set the legend label for this trace to the networks
                        # name if it exists, and they didn't pass a name key in
                        # the kwargs
                        if gen_label:
                            if self.name is None:
                                if plb.rcParams['text.usetex']:
                                    label_string = '$%s_{%i%i}$'%\
                                    (attribute[0].upper(),m+1,n+1)
                                else:
                                    label_string = '%s%i%i'%\
                                    (attribute[0].upper(),m+1,n+1)
                            else:
                                if plb.rcParams['text.usetex']:
                                    label_string = self.name+', $%s_{%i%i}$'%\
                                    (attribute[0].upper(),m+1,n+1)
                                else:
                                    label_string = self.name+', %s%i%i'%\
                                    (attribute[0].upper(),m+1,n+1)
                            kwargs['label'] = label_string

                        # quick and dirty way to plot step and impulse response
                        if 'time_impulse' in attribute:
                            xlabel = 'Time (ns)'
                            x,y = self.impulse_response(pad=pad, window=window)
                            # default is reflexion coefficient axis
                            if attribute[0].lower() == 'z':
                                # if they want impedance axis, give it to them
                                y_label = 'Z (ohm)'
                                y[x ==  1.] =  1. + 1e-12  # solve numerical singularity
                                y[x == -1.] = -1. + 1e-12  # solve numerical singularity
                                y = z0 * (1+y) / (1-y)
                            plot_rectangular(x=x * 1e9,
                                             y=y,
                                             x_label=xlabel,
                                             y_label=y_label,
                                             show_legend=show_legend, ax=ax,
                                             *args, **kwargs)
                        elif 'time_step' in attribute:
                            xlabel = 'Time (ns)'
                            x, y = self.step_response(pad=pad, window=window)
                            # default is reflexion coefficient axis
                            if attribute[0].lower() == 'z':
                                # if they want impedance axis, give it to them
                                y_label = 'Z (ohm)'
                                y[x ==  1.] =  1. + 1e-12  # solve numerical singularity
                                y[x == -1.] = -1. + 1e-12  # solve numerical singularity
                                y = z0 * (1+y) / (1-y)
                            plot_rectangular(x=x * 1e9,
                                             y=y,
                                             x_label=xlabel,
                                             y_label=y_label,
                                             show_legend=show_legend, ax=ax,
                                             *args, **kwargs)

                        else:
                            # plot the desired attribute vs frequency
                            if 'time' in attribute:
                                xlabel = 'Time (ns)'
                                x = self.frequency.t_ns

                            else:
                                xlabel = 'Frequency (%s)' % self.frequency.unit
                                # x = self.frequency.f_scaled
                                x = self.frequency.f  # always plot f, and then scale the ticks instead

                                # scale the ticklabels according to the frequency unit and set log-scale if desired:
                                if ax is None:
                                    ax = plb.gca()
                                if logx:
                                    ax.set_xscale('log')

                                scale_frequency_ticks(ax, self.frequency.unit)
								
								

                            plot_rectangular(x=x,
                                             y=getattr(self, attribute)[:, m, n],
                                             x_label=xlabel,
                                             y_label=y_label,
                                             show_legend=show_legend, ax=ax,
                                             *args, **kwargs)
                #if was_interactive:
                #    plb.interactive(True)
                #    plb.draw()
                #    #plb.show()

            plot_func.__doc__ = '''
    plot the Network attribute :attr:`%s` vs frequency.

    Parameters
    -----------
    m : int, optional
        first index of s-parameter matrix, if None will use all
    n : int, optional
        secon index of the s-parameter matrix, if None will use all
    ax : :class:`matplotlib.Axes` object, optional
        An existing Axes object to plot on
    show_legend : Boolean
        draw legend or not
    attribute : string
        Network attribute to plot
    y_label : string, optional
        the y-axis label
    logx : Boolean, optional
        Enable logarithmic x-axis, default off

    \*args,\\**kwargs : arguments, keyword arguments
        passed to :func:`matplotlib.plot`

    Notes
    -------
    This function is dynamically generated upon Network
    initialization. This is accomplished by calling
    :func:`plot_vs_frequency_generic`

    Examples
    ------------
    >>> myntwk.plot_%s(m=1,n=0,color='r')
    '''%(attribute,attribute)

            # setattr(self.__class__,'plot_%s'%(attribute), \
            setattr(self,'plot_%s'%(attribute), \
                plot_func)


def labelXAxis(self, ax=None):
    '''
    Label the x-axis of a plot.

    Sets the labels of a plot using :func:`matplotlib.x_label` with
    string containing the frequency unit.

    Parameters
    ---------------
    ax : :class:`matplotlib.Axes`, optional
            Axes on which to label the plot, defaults what is
            returned by :func:`matplotlib.gca()`
    '''
    if ax is None:
        ax = plb.gca()
    ax.set_xlabel('Frequency (%s)' % self.unit)


def plot_v_frequency(self, y, *args, **kwargs):
    '''
    Plot something vs this frequency

    This plots whatever is given vs. `self.f_scaled` and then
    calls `labelXAxis`.
    '''

    try:
        if len(npy.shape(y)) > 2:
            # perhaps the dimensions are empty, try to squeeze it down
            y = y.squeeze()
            if len(npy.shape(y)) > 2:
                # the dimensions are full, so lets loop and plot each
                for m in range(npy.shape(y)[1]):
                    for n in range(npy.shape(y)[2]):
                        self.plot(y[:, m, n], *args, **kwargs)
                return
        if len(y) == len(self):
            pass
        else:

            raise IndexError(['thing to plot doesn\'t have same'
                              ' number of points as f'])
    except(TypeError):
        y = y * npy.ones(len(self))

    # plb.plot(self.f_scaled, y, *args, **kwargs)
    plb.plot(self.f, y, *args, **kwargs)
    ax = plb.gca()
    scale_frequency_ticks(ax, self.unit)
    plb.autoscale(axis='x', tight=True)
    self.labelXAxis()


## specific ploting functions
def plot(self, *args, **kw):
    '''
    Plot something vs frequency
    '''
    return self.frequency.plot(*args, **kw)


def plot_passivity(self, port=None, label_prefix=None, *args, **kwargs):
    '''
    Plot dB(diag(passivity metric)) vs frequency

    Notes
    -------
    This plot does not completely capture the passivity metric, which
    is a test for `unitary-ness` of the s-matrix. However, it may
    be  used to display a measure of power dissipated in a network.

    See Also
    -----------
    passivity
    '''
    name = '' if self.name is None else self.name

    if port is None:
        ports = range(self.nports)
    else:
        ports = [port]
    for k in ports:
        if label_prefix == None:
            label = name + ', port %i' % (k + 1)
        else:
            label = label_prefix + ', port %i' % (k + 1)
        self.frequency.plot(mf.complex_2_db(self.passivity[:, k, k]),
                            label=label,
                            *args, **kwargs)

    plb.legend()
    plb.draw()


def plot_reciprocity(self, db=False, *args, **kwargs):
    '''
    Plot reciprocity metric

    See Also
    -----------
    reciprocity
    '''
    for m in range(self.nports):
        for n in range(self.nports):
            if m > n:
                if 'label' not in kwargs.keys():
                    kwargs['label'] = 'ports %i%i' % (m, n)
                y = self.reciprocity[:, m, n].flatten()
                if db:
                    y = mf.complex_2_db(y)
                self.frequency.plot(y, *args, **kwargs)

    plb.legend()
    plb.draw()


def plot_reciprocity2(self, db=False, *args, **kwargs):
    '''
    Plot reciprocity metric #2

    this is distance of the determinant of the wave-cascading matrix
    from unity.

    .. math::

            abs(1 - S/S^T )



    See Also
    -----------
    reciprocity
    '''
    for m in range(self.nports):
        for n in range(self.nports):
            if m > n:
                if 'label' not in kwargs.keys():
                    kwargs['label'] = 'ports %i%i' % (m, n)
                y = self.reciprocity2[:, m, n].flatten()
                if db:
                    y = mf.complex_2_db(y)
                self.frequency.plot(y, *args, **kwargs)

    plb.legend()
    plb.draw()


def plot_s_db_time(self,center_to_dc=None,*args,**kwargs):
    return self.windowed(center_to_dc=center_to_dc).plot_s_time_db(*args,**kwargs)


# plotting
def plot_s_smith(self,m=None, n=None,r=1,ax = None, show_legend=True,\
        chart_type='z', draw_labels=False, label_axes=False, draw_vswr=None, *args,**kwargs):
    '''
    plots the scattering parameter on a smith chart

    plots indices `m`, `n`, where `m` and `n` can be integers or
    lists of integers.


    Parameters
    -----------
    m : int, optional
            first index
    n : int, optional
            second index
    ax : matplotlib.Axes object, optional
            axes to plot on. in case you want to update an existing
            plot.
    show_legend : boolean, optional
            to turn legend show legend of not, optional
    chart_type : ['z','y']
        draw impedance or addmitance contours
    draw_labels : Boolean
        annotate chart with impedance values
    label_axes : Boolean
        Label axis with titles `Real` and `Imaginary`
    border : Boolean
        draw rectangular border around image with ticks
    draw_vswr : list of numbers, Boolean or None
        draw VSWR circles. If True, default values are used.

    \*args : arguments, optional
            passed to the matplotlib.plot command
    \*\*kwargs : keyword arguments, optional
            passed to the matplotlib.plot command


    See Also
    --------
    plot_vs_frequency_generic - generic plotting function
    smith -  draws a smith chart

    Examples
    ---------
    >>> myntwk.plot_s_smith()
    >>> myntwk.plot_s_smith(m=0,n=1,color='b', marker='x')
    '''
    # TODO: prevent this from re-drawing smith chart if one alread
    # exists on current set of axes

    # get current axis if user doesnt supply and axis
    if ax is None:
        ax = plb.gca()


    if m is None:
        M = range(self.number_of_ports)
    else:
        M = [m]
    if n is None:
        N = range(self.number_of_ports)
    else:
        N = [n]

    if 'label'  not in kwargs.keys():
        generate_label=True
    else:
        generate_label=False

    for m in M:
        for n in N:
            # set the legend label for this trace to the networks name if it
            # exists, and they didnt pass a name key in the kwargs
            if generate_label:
                if self.name is None:
                    if plb.rcParams['text.usetex']:
                        label_string = '$S_{'+repr(m+1) + repr(n+1)+'}$'
                    else:
                        label_string = 'S'+repr(m+1) + repr(n+1)
                else:
                    if plb.rcParams['text.usetex']:
                        label_string = self.name+', $S_{'+repr(m+1) + \
                                repr(n+1)+'}$'
                    else:
                        label_string = self.name+', S'+repr(m+1) + repr(n+1)

                kwargs['label'] = label_string

            # plot the desired attribute vs frequency
            if len (ax.patches) == 0:
                smith(ax=ax, smithR = r, chart_type=chart_type, draw_labels=draw_labels, draw_vswr=draw_vswr)
            ax.plot(self.s[:,m,n].real,  self.s[:,m,n].imag, *args,**kwargs)

    #draw legend
    if show_legend:
        ax.legend()
    ax.axis(npy.array([-1.1,1.1,-1.1,1.1])*r)

    if label_axes:
        ax.set_xlabel('Real')
        ax.set_ylabel('Imaginary')


def plot_it_all(self,*args, **kwargs):
    '''
    Plots dB, deg, smith, and complex in subplots

    Plots the magnitude in dB in subplot 1, the phase in degrees in
    subplot 2, a smith chart in subplot 3, and a complex plot in
    subplot 4.

    Parameters
    -----------
    \*args : arguments, optional
            passed to the matplotlib.plot command
    \*\*kwargs : keyword arguments, optional
            passed to the matplotlib.plot command

    See Also
    --------
    plot_s_db - plot magnitude (in dB) of s-parameters vs frequency
    plot_s_deg - plot phase of s-parameters (in degrees) vs frequency
    plot_s_smith - plot complex s-parameters on smith chart
    plot_s_complex - plot complex s-parameters in the complex plane

    Examples
    ---------
    >>> from skrf.data import ring_slot
    >>> ring_slot.plot_it_all()
    '''
    plb.subplot(221)
    getattr(self,'plot_s_db')(*args, **kwargs)
    plb.subplot(222)
    getattr(self,'plot_s_deg')(*args, **kwargs)
    plb.subplot(223)
    getattr(self,'plot_s_smith')(*args, **kwargs)
    plb.subplot(224)
    getattr(self,'plot_s_complex')(*args, **kwargs)


def stylely(rc_dict={}, style_file = 'skrf.mplstyle'):
    '''
    loads the rc-params from the specified file (file must be located in skrf/data)
    '''

    from skrf.data import pwd # delayed to solve circular import
    mpl.style.use(os.path.join(pwd, style_file))
    mpl.rc(rc_dict)


def plot_calibration_errors(self, *args, **kwargs):
    '''
    Plots biased, unbiased and total error in dB scaled

    See Also
    ---------
    biased_error
    unbiased_error
    total_error
    '''
    port_list = self.biased_error.port_tuples
    for m,n in port_list:
        plb.figure()
        plb.title('S%i%i'%(m+1,n+1))
        self.unbiased_error.plot_s_db(m,n,**kwargs)
        self.biased_error.plot_s_db(m,n,**kwargs)
        self.total_error.plot_s_db(m,n,**kwargs)
        plb.ylim(-100,0)


def plot_caled_ntwks(self, attr='s_smith', show_legend=False,**kwargs):
    '''
    Plots corrected calibration standards

    Given that the calibration is overdetermined, this may be used
    as a heuristic verification of calibration quality.

    Parameters
    ------------------
    attr : str
        Network property to plot, ie 's_db', 's_smith', etc
    show_legend : bool
        draw a legend or not
    \\*\\*kwargs : kwargs
        passed to the plot method of Network
    '''
    ns = networkSet.NetworkSet(self.caled_ntwks)
    kwargs.update({'show_legend':show_legend})

    if ns[0].nports ==1:
        ns.__getattribute__('plot_'+attr)(0,0, **kwargs)
    elif ns[0].nports ==2:
        plb.figure(figsize = (8,8))
        for k,mn in enumerate([(0, 0), (1, 1), (0, 1), (1, 0)]):
            plb.subplot(221+k)
            plb.title('S%i%i'%(mn[0]+1,mn[1]+1))
            ns.__getattribute__('plot_'+attr)(*mn, **kwargs)
    else:
        raise NotImplementedError
    plb.tight_layout()


def plot_residuals(self, attr='s_db', **kwargs):
    '''
    Plot residual networks.

    Given that the calibration is overdetermined, this may be used
    as a metric of the calibration's *goodness of fit*

    Parameters
    ------------------
    attr : str
        Network property to plot, ie 's_db', 's_smith', etc
    \\*\\*kwargs : kwargs
        passed to the plot method of Network

    See Also
    --------
    Calibration.residual_networks
    '''

    networkSet.NetworkSet(self.residual_ntwks).__getattribute__('plot_'+attr)(**kwargs)


# Network Set Plotting Commands
def animate(self, attr='s_deg', ylims=(-5, 5), xlims=None, show=True,
            savefigs=False, dir_='.', *args, **kwargs):
    '''
    animate a property of the networkset

    This loops through all elements in the NetworkSet and calls
    a plotting attribute (ie Network.plot_`attr`), with given \*args
    and \*\*kwargs.

    Parameters
    --------------
    attr : str
        plotting property of a Network (ie 's_db', 's_deg', etc)
    ylims : tuple
        passed to ylim. needed to have consistent y-limits accross frames
    xlims : tuple
        passed to xlim
    show : bool
        show each frame as its animated
    savefigs : bool
        save each frame as a png

    \*args, \*\*kwargs :
        passed to the Network plotting function

    Notes
    --------
    using `label=None` will speed up animation significantly,
    because it prevents the legend from drawing

    to create video paste this:

        !avconv -r 10 -i out_%5d.png  -vcodec huffyuv out.avi

    or (depending on your ffmpeg version)

        !ffmpeg -r 10 -i out_%5d.png  -vcodec huffyuv out.avi

    Examples
    ------------
    >>>ns.animate('s_deg', ylims=(-5,5),label=None)

    '''
    was_interactive = plb.isinteractive()
    plb.ioff()

    for idx, k in enumerate(self):
        plb.clf()
        if 'time' in attr:
            tmp_ntwk = k.windowed()
            tmp_ntwk.__getattribute__('plot_' + attr)(*args, **kwargs)
        else:
            k.__getattribute__('plot_' + attr)(*args, **kwargs)
        if ylims is not None:
            plb.ylim(ylims)
        if xlims is not None:
            plb.xlim(xlims)
        # rf.legend_off()
        plb.draw();
        if show:
            plb.show()
        if savefigs:
            fname = os.path.join(dir_, 'out_%.5i' % idx + '.png')
            plb.savefig(fname)

    if savefigs:
        print('\n\n')
    if was_interactive:
        plb.ion()


#------------------------------
#
# NetworkSet plotting functions
#
#------------------------------

def plot_uncertainty_bounds_component(
        self, attribute, m=None, n=None,
        type='shade', n_deviations=3, alpha=.3, color_error=None, markevery_error=20,
        ax=None, ppf=None, kwargs_error={}, *args, **kwargs):
    '''
    plots mean value of a NetworkSet with +/- uncertainty bounds in an Network's attribute. 
    
    This is designed to represent uncertainty in a scalar component of the s-parameter. 
    for example plotting the uncertainty in the magnitude would be expressed by,
    .. maths::
            mean(abs(s)) +- std(abs(s))

    the order of mean and abs is important.


    Parameters
    ----------
    attribute: str
        attribute of Network type to analyze     
    m: int
        first index of attribute matrix 
    n: int
        second index of attribute matrix     
    type: str
        ['shade' | 'bar'], type of plot to draw
    n_deviations: float
        number of std deviations to plot as bounds
    alpha: float
        passed to matplotlib.fill_between() command. [number, 0-1]
    color_error: str
        color of the +- std dev fill shading. Default is None.
    markevery_error: float
        tbd
    type: str
        if type=='bar', this controls frequency of error bars
    ax: matplotlib axe object
        Axes to plot on. Default is None.
    ppf: function
        post processing function. a function applied to the
        upper and lower bounds. Default is None
    *args,**kwargs: 
        passed to Network.plot_s_re command used to plot mean response
     kwargs_error: dict
         dictionary of kwargs to pass to the fill_between or 
         errorbar plot command depending on value of type.


    Note
    ----
    for phase uncertainty you probably want s_deg_unwrap, or
    similar. uncerainty for wrapped phase blows up at +-pi.

    '''

    if ax is None:
        ax = plb.gca()

    if m is None:
        M = range(self[0].number_of_ports)
    else:
        M = [m]
    if n is None:
        N = range(self[0].number_of_ports)
    else:
        N = [n]

    for m in M:
        for n in N:

            plot_attribute = attribute

            ntwk_mean = self.__getattribute__('mean_'+attribute)
            ntwk_std = self.__getattribute__('std_'+attribute)
            ntwk_std.s = n_deviations * ntwk_std.s

            upper_bound = (ntwk_mean.s[:, m, n] + ntwk_std.s[:, m, n]).squeeze()
            lower_bound = (ntwk_mean.s[:, m, n] - ntwk_std.s[:, m, n]).squeeze()

            if ppf is not None:
                if type == 'bar':
                    raise NotImplementedError('the \'ppf\' options don\'t work correctly with the bar-type error plots')
                ntwk_mean.s = ppf(ntwk_mean.s)
                upper_bound = ppf(upper_bound)
                lower_bound = ppf(lower_bound)
                lower_bound[npy.isnan(lower_bound)] = min(lower_bound)
                if ppf in [mf.magnitude_2_db, mf.mag_2_db]:  # quickfix of wrong ylabels due to usage of ppf for *_db plots
                    if attribute == 's_mag':
                        plot_attribute = 's_db'
                    elif attribute == 's_time_mag':
                        plot_attribute = 's_time_db'

            if type == 'shade':
                ntwk_mean.plot_s_re(ax=ax, m=m, n=n, *args, **kwargs)
                if color_error is None:
                    color_error = ax.get_lines()[-1].get_color()
                ax.fill_between(ntwk_mean.frequency.f,
                                lower_bound.real, upper_bound.real, alpha=alpha, color=color_error,
                                **kwargs_error)
                # ax.plot(ntwk_mean.frequency.f_scaled, ntwk_mean.s[:,m,n],*args,**kwargs)

            elif type == 'bar':
                ntwk_mean.plot_s_re(ax=ax, m=m, n=n, *args, **kwargs)
                if color_error is None:
                    color_error = ax.get_lines()[-1].get_color()
                ax.errorbar(ntwk_mean.frequency.f[::markevery_error],
                            ntwk_mean.s_re[:, m, n].squeeze()[::markevery_error],
                            yerr=ntwk_std.s_mag[:, m, n].squeeze()[::markevery_error],
                            color=color_error, **kwargs_error)

            else:
                raise(ValueError('incorrect plot type'))

            ax.set_ylabel(Y_LABEL_DICT.get(plot_attribute[2:], ''))  # use only the function of the attribute
            scale_frequency_ticks(ax, ntwk_mean.frequency.unit)
            ax.axis('tight')


def plot_minmax_bounds_component(self, attribute, m=0, n=0,
                                 type='shade', alpha=.3, color_error=None, markevery_error=20,
                                 ax=None, ppf=None, kwargs_error={}, *args, **kwargs):
    '''
    plots mean value of the NetworkSet with +/- uncertainty bounds in an Network's attribute. 
    
    This is designed to represent uncertainty in a scalar component of the s-parameter. for example
    plotting the uncertainty in the magnitude would be expressed by,
    .. maths::
            mean(abs(s)) +- std(abs(s))

    the order of mean and abs is important.

    Parameters
    ----------
    attribute: str
        attribute of Network type to analyze     
    m: int
        first index of attribute matrix 
    n: int
        second index of attribute matrix     
    type: str
        ['shade' | 'bar'], type of plot to draw
    n_deviations: float
        number of std deviations to plot as bounds
    alpha: float
        passed to matplotlib.fill_between() command. [number, 0-1]
    color_error: str
        color of the +- std dev fill shading. Default is None.
    markevery_error: float
        tbd
    type: str
        if type=='bar', this controls frequency of error bars
    ax: matplotlib axe object
        Axes to plot on. Default is None.
    ppf: function
        post processing function. a function applied to the
        upper and lower bounds. Default is None
    *args,**kwargs: 
        passed to Network.plot_s_re command used to plot mean response
     kwargs_error: dict
         dictionary of kwargs to pass to the fill_between or 
         errorbar plot command depending on value of type.


    Note
    ----
    for phase uncertainty you probably want s_deg_unwrap, or
    similar.  uncertainty for wrapped phase blows up at +-pi.

    '''

    if ax is None:
        ax = plb.gca()

    ntwk_mean = self.__getattribute__('mean_'+attribute)
    ntwk_std = self.__getattribute__('std_'+attribute)

    lower_bound = self.__getattribute__('min_'+attribute).s_re[:,m,n].squeeze()
    upper_bound = self.__getattribute__('max_'+attribute).s_re[:,m,n].squeeze()

    if ppf is not None:
        if type =='bar':
            raise NotImplementedError('the \'ppf\' options don\'t work correctly with the bar-type error plots')
        ntwk_mean.s = ppf(ntwk_mean.s)
        upper_bound = ppf(upper_bound)
        lower_bound = ppf(lower_bound)
        lower_bound[npy.isnan(lower_bound)]=min(lower_bound)
        if ppf in [mf.magnitude_2_db, mf.mag_2_db]: # quickfix of wrong ylabels due to usage of ppf for *_db plots
            if attribute == 's_mag':
                attribute = 's_db'
            elif attribute == 's_time_mag':
                attribute = 's_time_db'

    if type == 'shade':
        ntwk_mean.plot_s_re(ax=ax,m=m,n=n,*args, **kwargs)
        if color_error is None:
            color_error = ax.get_lines()[-1].get_color()
        ax.fill_between(ntwk_mean.frequency.f,
                        lower_bound, upper_bound, alpha=alpha, color=color_error,
                        **kwargs_error)
        #ax.plot(ntwk_mean.frequency.f_scaled,ntwk_mean.s[:,m,n],*args,**kwargs)
    elif type =='bar':
        raise (NotImplementedError)
        ntwk_mean.plot_s_re(ax=ax, m=m, n=n, *args, **kwargs)
        if color_error is None:
            color_error = ax.get_lines()[-1].get_color()
        ax.errorbar(ntwk_mean.frequency.f[::markevery_error],
                    ntwk_mean.s_re[:,m,n].squeeze()[::markevery_error],
                    yerr=ntwk_std.s_mag[:,m,n].squeeze()[::markevery_error],
                    color=color_error,**kwargs_error)

    else:
        raise(ValueError('incorrect plot type'))

    ax.set_ylabel(Y_LABEL_DICT.get(attribute[2:], ''))  # use only the function of the attribute
    scale_frequency_ticks(ax, ntwk_mean.frequency.unit)
    ax.axis('tight')

def plot_uncertainty_bounds_s_db(self, *args, **kwargs):
    '''
    Calls ``plot_uncertainty_bounds(attribute='s_mag','ppf':mf.magnitude_2_db*args,**kwargs)``
    
    see plot_uncertainty_bounds for help

    '''
    kwargs.update({'attribute':'s_mag','ppf':mf.magnitude_2_db})
    self.plot_uncertainty_bounds_component(*args,**kwargs)

def plot_minmax_bounds_s_db(self, *args, **kwargs):
    '''
    Calls ``plot_uncertainty_bounds(attribute= 's_mag','ppf':mf.magnitude_2_db*args,**kwargs)``

    see plot_uncertainty_bounds for help

    '''
    kwargs.update({'attribute':'s_mag','ppf':mf.magnitude_2_db})
    self.plot_minmax_bounds_component(*args,**kwargs)

def plot_minmax_bounds_s_db10(self,*args, **kwargs):
    '''
    Calls ``plot_uncertainty_bounds(attribute= 's_mag','ppf':mf.magnitude_2_db*args,**kwargs)``
    
    see plot_uncertainty_bounds for help

    '''
    kwargs.update({'attribute':'s_mag','ppf':mf.mag_2_db10})
    self.plot_minmax_bounds_component(*args,**kwargs)

def plot_uncertainty_bounds_s_time_db(self,*args, **kwargs):
    '''
    Calls ``plot_uncertainty_bounds(attribute= 's_mag','ppf':mf.magnitude_2_db*args,**kwargs)``
    
    see plot_uncertainty_bounds for help

    '''
    kwargs.update({'attribute':'s_time_mag','ppf':mf.magnitude_2_db})
    self.plot_uncertainty_bounds_component(*args,**kwargs)

def plot_minmax_bounds_s_time_db(self,*args, **kwargs):
    '''
    Calls ``plot_uncertainty_bounds(attribute= 's_mag','ppf':mf.magnitude_2_db*args,**kwargs)``
    
    see plot_uncertainty_bounds for help

    '''
    kwargs.update({'attribute':'s_time_mag','ppf':mf.magnitude_2_db})
    self.plot_minmax_bounds_component(*args,**kwargs)

def plot_uncertainty_decomposition(self, m=0,n=0):
    '''
    plots the total and  component-wise uncertainty

    Parameters
    --------------
    m : int
        first s-parameters index
    n :
        second s-parameter index

    '''
    if self.name is not None:
        plb.title(r'Uncertainty Decomposition: %s $S_{%i%i}$'%(self.name,m,n))
    self.std_s.plot_s_mag(label='Distance', m=m,n=n)
    self.std_s_re.plot_s_mag(label='Real',  m=m,n=n)
    self.std_s_im.plot_s_mag(label='Imaginary',  m=m,n=n)
    self.std_s_mag.plot_s_mag(label='Magnitude',  m=m,n=n)
    self.std_s_arcl.plot_s_mag(label='Arc-length',  m=m,n=n)

def plot_uncertainty_bounds_s(self, multiplier =200, *args, **kwargs):
    '''
    Plots complex uncertainty bounds plot on smith chart.

    This function plots the complex uncertainty of a NetworkSet
    as circles on the smith chart. At each frequency a circle
    with radii proportional to the complex standard deviation
    of the set at that frequency is drawn. Due to the fact that
    the `markersize` argument is in pixels, the radii can scaled by
    the input argument  `multiplier`.

    default kwargs are
        {
        'marker':'o',
        'color':'b',
        'mew':0,
        'ls':'',
        'alpha':.1,
        'label':None,
        }

    Parameters
    -------------
    multipliter : float
        controls the circle sizes, by multiples of the standard
        deviation.



    '''
    default_kwargs = {
        'marker':'o',
        'color':'b',
        'mew':0,
        'ls':'',
        'alpha':.1,
        'label':None,
        }
    default_kwargs.update(**kwargs)



    if plb.isinteractive():
        was_interactive = True
        plb.interactive(0)
    else:
        was_interactive = False

    [self.mean_s[k].plot_s_smith(*args, ms = self.std_s[k].s_mag*multiplier, **default_kwargs) for k in range(len(self[0]))]

    if was_interactive:
        plb.interactive(1)
    plb.draw()
    plb.show()

def plot_logsigma(self, label_axis=True, *args,**kwargs):
    '''
    plots the uncertainty for the set in units of log-sigma.
    
    Log-sigma is the complex standard deviation, plotted in units
    of dB's.

    Parameters
    ------------
    \\*args, \\*\\*kwargs : arguments
        passed to self.std_s.plot_s_db()
    '''
    self.std_s.plot_s_db(*args,**kwargs)
    if label_axis:
        plb.ylabel('Standard Deviation(dB)')

def signature(self, m=0, n=0, component='s_mag',
              vmax=None, vs_time=False, cbar_label=None,
              *args, **kwargs):
    '''
    Visualization of a NetworkSet.

    Creates a colored image representing the some component
    of each Network in the  NetworkSet, vs frequency.


    Parameters
    ------------
    m : int
        first s-parameters index
    n : int
        second s-parameter index
    component : ['s_mag','s_db','s_deg' ..]
        scalar component of Network to visualize. should
        be a property of the Network object.
    vmax : number
        sets upper limit of colorbar, if None, will be set to
        3*mean of the magnitude of the complex difference
    vs_time: Boolean
        if True, then we assume each Network.name was made with
        rf.now_string, and we make the y-axis a datetime axis
    cbar_label: String
        label for the colorbar

    \*args,\*\*kw : arguments, keyword arguments
        passed to :func:`~pylab.imshow`


    '''

    mat = npy.array([self[k].__getattribute__(component)[:, m, n] \
                     for k in range(len(self))])

    # if vmax is None:
    #    vmax = 3*mat.mean()

    if vs_time:
        # create a datetime index
        dt_idx = [now_string_2_dt(k.name) for k in self]
        mpl_times = date2num(dt_idx)
        y_max = mpl_times[0]
        y_min = mpl_times[-1]

    else:
        y_min = len(self)
        y_max = 0

    # creates x and y scales
    freq = self[0].frequency
    extent = [freq.f_scaled[0], freq.f_scaled[-1], y_min, y_max]

    # set default imshow kwargs
    kw = {'extent': extent, 'aspect': 'auto', 'interpolation': 'nearest',
          'vmax': vmax}
    # update the users kwargs
    kw.update(kwargs)
    img = plb.imshow(mat, *args, **kw)

    if vs_time:
        ax = plb.gca()
        ax.yaxis_date()
        # date_format = plb.DateFormatter('%M:%S.%f')
        # ax.yaxis.set_major_formatter(date_format)
        # cbar.set_label('Magntidue (dB)')
        plb.ylabel('Time')
    else:
        plb.ylabel('Network #')

    plb.grid(0)
    freq.labelXAxis()

    cbar = plb.colorbar()
    if cbar_label is not None:
        cbar.set_label(cbar_label)

    return img

def plot_circuit_graph(self, **kwargs):
    '''
    Plot the graph of the circuit using networkx drawing capabilities.

    Customisation options with default values:
    ::
        'network_shape': 's'
        'network_color': 'gray'
        'network_size', 300
        'network_fontsize': 7
        'inter_shape': 'o'
        'inter_color': 'lightblue'
        'inter_size', 300
        'port_shape': '>'
        'port_color': 'red'
        'port_size', 300
        'port_fontsize': 7
        'edges_fontsize': 5
        'network_labels': False
        'edge_labels': False
        'inter_labels': False
        'port_labels': False
        'label_shift_x': 0
        'label_shift_y': 0

    '''
    # Get the circuit graph. Will raise an error if the networkx package
    # is not installed.
    G = self.G

    # default values
    network_labels = kwargs.pop('network_labels', False)
    network_shape = kwargs.pop('network_shape', 's')
    network_color = kwargs.pop('network_color', 'gray')
    network_fontsize = kwargs.pop('network_fontsize', 7)
    network_size = kwargs.pop('network_size', 300)
    inter_labels = kwargs.pop('inter_labels', False)
    inter_shape = kwargs.pop('inter_shape', 'o')
    inter_color = kwargs.pop('inter_color', 'lightblue')
    inter_size = kwargs.pop('inter_size', 300)
    port_labels = kwargs.pop('port_labels', False)
    port_shape = kwargs.pop('port_shape', '>')
    port_color = kwargs.pop('port_color', 'red')
    port_size = kwargs.pop('port_size', 300)
    port_fontsize = kwargs.pop('port_fontsize', 7)
    edge_labels = kwargs.pop('edge_labels', False)
    edge_fontsize = kwargs.pop('edge_fontsize', 5)
    label_shift_x = kwargs.pop('label_shift_x', 0)
    label_shift_y = kwargs.pop('label_shift_y', 0)

    
    # sort between network nodes and port nodes
    all_ntw_names = [ntw.name for ntw in self.networks_list()]
    port_names = [ntw_name for ntw_name in all_ntw_names if 'port' in ntw_name]
    ntw_names = [ntw_name for ntw_name in all_ntw_names if 'port' not in ntw_name]
    # generate connectins nodes names
    int_names = ['X'+str(k) for k in range(self.connections_nb)]

    fig, ax = plb.subplots(figsize=(10,8))

    pos = nx.spring_layout(G)

    # draw Networks
    nx.draw_networkx_nodes(G, pos, port_names, ax=ax,
                           node_size=port_size,
                           node_color=port_color, node_shape=port_shape)
    nx.draw_networkx_nodes(G, pos, ntw_names, ax=ax,
                           node_size=network_size,
                           node_color=network_color, node_shape=network_shape)
    # draw intersections
    nx.draw_networkx_nodes(G, pos, int_names, ax=ax,
                           node_size=inter_size,
                           node_color=inter_color, node_shape=inter_shape)
    # labels shifts
    pos_labels = {}
    for node, coords in pos.items():
        pos_labels[node] = (coords[0] + label_shift_x,
                            coords[1] + label_shift_y)

    # network labels
    if network_labels:
        network_labels = {lab:lab for lab in ntw_names}

        nx.draw_networkx_labels(G, pos_labels, labels=network_labels,
                                font_size=network_fontsize, ax=ax)

    # intersection labels
    if inter_labels:
        inter_labels = {'X'+str(k):'X'+str(k) for k in range(self.connections_nb)}

        nx.draw_networkx_labels(G, pos_labels, labels=inter_labels,
                                font_size=network_fontsize, ax=ax)

    # port labels
    if port_labels:
        port_labels = {lab:lab for lab in port_names}

        nx.draw_networkx_labels(G, pos_labels, labels=port_labels,
                                font_size=port_fontsize, ax=ax)

    # draw edges
    nx.draw_networkx_edges(G, pos, ax=ax)
    if edge_labels:
        edge_labels = self.edge_labels
        nx.draw_networkx_edge_labels(G, pos,
                                      edge_labels=edge_labels, label_pos=0.5,
                                      font_size=edge_fontsize, ax=ax)
    # remove x and y axis and labels
    plb.axis('off')
    plb.tight_layout()




def plot_contour(freq, x, y, z, min0max1, graph=True, cmap='plasma_r', title='',  **kwargs):
    '''
    Countour plot

    Parameters
    ----------
    freq : :skrf.Frequency:
        Frequency object.
    x : array
        x points
    y : array
        y points.
    z : array
        z points.
    min0max1 : int
        0 for min, 1 for max.
    graph : bool, optional
        plot graph if True. The default is True.
    cmap : str, optional
        Colormap label. The default is 'plasma_r'.
    title : str, optional
        Figure title. The default is ''.
    **kwargs : dict
        Other parameters passed to `matplotlib.plot()`.

    Returns
    -------
    GAMopt : :skrf.Network:
        Network
    VALopt : float
        min or max.

    '''
    ri =  npy.linspace(0,1, 50); 
    ti =  npy.linspace(0,2*npy.pi, 150);
    Ri , Ti = npy.meshgrid(ri, ti)
    xi = npy.linspace(-1,1, 50);    
    Xi, Yi = npy.meshgrid(xi, xi)
    triang = tri.Triangulation(x, y)
    interpolator = tri.LinearTriInterpolator(triang, z)
    Zi = interpolator(Xi, Yi)
    if min0max1 == 1 : 
        VALopt = npy.max(z)
    else : 
        VALopt = npy.min(z)
    GAMopt = network.Network(f=[freq], s=x[z==VALopt] +1j*y[z==VALopt])

    if graph : 
        fig, ax = plb.subplots(**kwargs)
        an = npy.linspace(0, 2*npy.pi, 50)
        cs, sn = npy.cos(an), npy.sin(an)
        plb.plot(cs, sn, color='k', lw=0.25)
        plb.plot(cs, sn*0, color='g', lw=0.25)
        plb.plot((1+cs)/2, sn/2, color='k', lw=0.25)
        plb.axis('equal')
        ax.set_axis_off()
        ax.contour(Xi, Yi, Zi, levels=20, vmin=Zi.min(), vmax= Zi.max(), linewidths=0.5,  colors='k')
        cntr1 = ax.contourf(Xi, Yi, Zi, levels=20, vmin=Zi.min(), vmax= Zi.max(),cmap=cmap)
        fig.colorbar(cntr1, ax=ax)
        ax.plot(x, y, 'o', ms=0.3, color='k')
        ax.set(xlim=(-1, 1), ylim=(-1, 1))
        plb.title(title)
        plb.show()
    return GAMopt, VALopt









