
# This file is part of PyEMMA.
#
# Copyright (c) 2015, 2014 Computational Molecular Biology Group, Freie Universitaet Berlin (GER)
#
# PyEMMA is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


from __future__ import absolute_import
import math
import numpy as np
import six

from pyemma.util import types as _types
from six.moves import range

__author__ = 'noe, marscher'


class NetworkPlot(object):
    r"""Plot of network with nodes and arcs """

    def __init__(self, A, pos=None, xpos=None, ypos=None):
        r"""

        Parameters
        ----------
        A : ndarray(n,n)
            weight matrix or adjacency matrix of the network to visualize
        pos : ndarray(n,2)
            user-defined positions
        xpos : ndarray(n,)
            user-defined x-positions
        ypos : ndarray(n,)
            user-defined y-positions

        Examples
        --------
        We define first define a reactive flux by taking the following transition
        matrix and computing TPT from state 2 to 3.

        >>> import numpy as np
        >>> P = np.array([[0.8,  0.15, 0.05,  0.0,  0.0],
        ...               [0.1,  0.75, 0.05, 0.05, 0.05],
        ...               [0.05,  0.1,  0.8,  0.0,  0.05],
        ...               [0.0,  0.2, 0.0,  0.8,  0.0],
        ...               [0.0,  0.02, 0.02, 0.0,  0.96]])
        >>> from pyemma import msm
        >>> F = msm.tpt(msm.markov_model(P), [2], [3])

        now plot the gross flux
        >>> NetworkPlot(F.gross_flux).plot_network() # doctest:+ELLIPSIS
        <matplotlib.figure.Figure...

        """
        from matplotlib import pylab as plt

        if A.shape[0] >= 50:
            import warnings
            warnings.warn("The layout optimization method will take a long"
                          " time for large networks! It is recommended to"
                          " coarse grain your model first!")
        self.A = A
        if pos is not None and len(pos) < len(A):
            raise ValueError('Given less positions (%i) than states (%i)'
                             % (len(pos), len(A)))
        if xpos is not None and len(xpos) < len(A):
            raise ValueError('Given less positions (%i) than states (%i)'
                             % (len(xpos), len(A)))
        if ypos is not None and len(ypos) < len(A):
            raise ValueError('Given less positions (%i) than states (%i)'
                             % (len(ypos), len(A)))
        self.pos = pos
        self.xpos = xpos
        self.ypos = ypos
        self.plt = plt

    def _draw_arrow(self, x1, y1, x2, y2, Dx, Dy, label="", width=1.0,
                    arrow_curvature=1.0, color="grey",
                    patchA=None, patchB=None, shrinkA=0, shrinkB=0):
        """
        Draws a slightly curved arrow from (x1,y1) to (x2,y2).
        Will allow the given patches at start end end.

        """
        plt = self.plt
        # set arrow properties
        dist = math.sqrt(
            ((x2 - x1) / float(Dx))**2 + ((y2 - y1) / float(Dy))**2)
        arrow_curvature *= 0.075  # standard scale
        rad = arrow_curvature / (dist)
        tail_width = width
        head_width = max(0.5, 2 * width)
        head_length = head_width
        plt.annotate("",
                     xy=(x2, y2),
                     xycoords='data',
                     xytext=(x1, y1),
                     textcoords='data',
                     arrowprops=dict(arrowstyle='simple,head_length=%f,head_width=%f,tail_width=%f'
                                     % (head_length, head_width, tail_width),
                                     color=color, shrinkA=shrinkA, shrinkB=shrinkB,
                                     patchA=patchA, patchB=patchB,
                                     connectionstyle="arc3,rad=%f" % -rad),
                     zorder=0)
        # weighted center position
        center = np.array([0.55 * x1 + 0.45 * x2, 0.55 * y1 + 0.45 * y2])
        v = np.array([x2 - x1, y2 - y1])  # 1->2 vector
        vabs = np.abs(v)
        vnorm = np.array([v[1], -v[0]])  # orthogonal vector
        vnorm /= math.sqrt(np.dot(vnorm, vnorm))  # normalize
        # cross product to determine the direction into which vnorm points
        z = np.cross(v, vnorm)
        if z < 0:
            vnorm *= -1
        offset = 0.5 * arrow_curvature * \
            ((vabs[0] / (vabs[0] + vabs[1]))
             * Dx + (vabs[1] / (vabs[0] + vabs[1])) * Dy)
        ptext = center + offset * vnorm
        plt.text(ptext[0], ptext[1], label, size=14,
                 horizontalalignment='center', verticalalignment='center', zorder=1)

    def plot_network(self,
                     state_sizes=None, state_scale=1.0, state_colors='#ff5500', state_labels='auto',
                     arrow_scale=1.0, arrow_curvature=1.0, arrow_labels='weights',
                     arrow_label_format='%10.2f', max_width=12, max_height=12,
                     figpadding=0.2, xticks=False, yticks=False, show_frame=False,
                     **textkwargs):
        """
        Draws a network using discs and curved arrows.

        The thicknesses and labels of the arrows are taken from the off-diagonal matrix elements in A.

        """
        plt = self.plt

        if self.pos is None:
            self.layout_automatic()
        # number of nodes
        n = len(self.pos)
        # get bounds and pad figure
        xmin = np.min(self.pos[:, 0])
        xmax = np.max(self.pos[:, 0])
        Dx = xmax - xmin
        xmin -= Dx * figpadding
        xmax += Dx * figpadding
        Dx *= 1 + figpadding
        ymin = np.min(self.pos[:, 1])
        ymax = np.max(self.pos[:, 1])
        Dy = ymax - ymin
        ymin -= Dy * figpadding
        ymax += Dy * figpadding
        Dy *= 1 + figpadding
        # sizes of nodes
        if state_sizes is None:
            state_sizes = 0.5 * state_scale * \
                min(Dx, Dy)**2 * np.ones(n) / float(n)
        else:
            state_sizes = 0.5 * state_scale * \
                min(Dx, Dy)**2 * state_sizes / (np.max(state_sizes) * float(n))
        # automatic arrow rescaling
        arrow_scale *= 1.0 / \
            (np.max(self.A - np.diag(np.diag(self.A))) * math.sqrt(n))
        # size figure
        if (Dx / max_width > Dy / max_height):
            figsize = (max_width, Dy * (max_width / Dx))
        else:
            figsize = (Dx / Dy * max_height, max_height)
        fig = plt.gcf()
        fig.set_size_inches(figsize, forward=True)
        # font sizes
        from matplotlib import rcParams
        old_fontsize = rcParams['font.size']
        rcParams['font.size'] = 20
        # remove axis labels
        frame = plt.gca()
        if not xticks:
            frame.axes.get_xaxis().set_ticks([])
        if not yticks:
            frame.axes.get_yaxis().set_ticks([])
        # show or suppress frame
        frame.set_frame_on(show_frame)
        # set node labels
        if state_labels is 'auto':
            state_labels=[str(i) for i in np.arange(n)]
        else:
            assert len(state_labels)==n, "Mistmatch between nstates and nr. state_labels (%u vs %u)"%(n, len(state_labels))
        # set node colors
        if state_colors is None:
            state_colors = '#ff5500'  # None is not acceptable
        if isinstance(state_colors, str):
            state_colors = [state_colors] * n
        if isinstance(state_colors, list):
            assert len(state_colors)==n, "Mistmatch between nstates and nr. state_colors (%u vs %u)"%(n, len(state_colors))
        try:
            colorscales = _types.ensure_ndarray(state_colors, ndim=1, kind='numeric')
            colorscales /= colorscales.max()
            state_colors = [plt.cm.binary(int(256.0 * colorscales[i])) for i in range(n)]
        except:
            pass  # assume we have a list of strings now.

        # set arrow labels
        if isinstance(arrow_labels, np.ndarray):
            L = arrow_labels
        elif isinstance(arrow_labels, six.string_types) and arrow_labels.lower() == 'weights':
            L = self.A[:, :]
        elif arrow_labels is None:
            L = np.empty(np.shape(self.A), dtype=object)
            L[:, :] = ''
        else:
            rcParams['font.size'] = old_fontsize
            raise ValueError('invalid arrow labels')

        # Set the default values for the text dictionary
        textkwargs.setdefault('size', 14)
        textkwargs.setdefault('horizontalalignment', 'center')
        textkwargs.setdefault('verticalalignment', 'center')
        textkwargs.setdefault('color','black')

        # draw circles
        circles = []
        for i in range(n):
            fig = plt.gcf()
            # choose color
            c = plt.Circle(self.pos[i], radius=math.sqrt(
                0.5 * state_sizes[i]) / 2.0, color=state_colors[i], zorder=2)
            circles.append(c)
            fig.gca().add_artist(c)
            # add annotation
            plt.text(self.pos[i][0], self.pos[i][1], state_labels[i], zorder=3,
                     **textkwargs)

        assert len(circles) == n, "%i != %i" % (len(circles), n)

        # draw arrows
        for i in range(n):
            for j in range(i + 1, n):
                if (abs(self.A[i, j]) > 0):
                    self._draw_arrow(self.pos[i, 0], self.pos[i, 1],
                                     self.pos[j, 0], self.pos[j, 1], Dx, Dy,
                                     label=arrow_label_format%L[i, j],
                                     width=arrow_scale * self.A[i, j],
                                     arrow_curvature=arrow_curvature,
                                     patchA=circles[i], patchB=circles[j],
                                     shrinkA=3, shrinkB=0)
                if (abs(self.A[j, i]) > 0):
                    self._draw_arrow(self.pos[j, 0], self.pos[j, 1],
                                     self.pos[i, 0], self.pos[i, 1], Dx, Dy,
                                     label=arrow_label_format%L[j, i],
                                     width=arrow_scale * self.A[j, i],
                                     arrow_curvature=arrow_curvature,
                                     patchA=circles[j], patchB=circles[i],
                                     shrinkA=3, shrinkB=0)

        # plot
        plt.xlim(xmin, xmax)
        plt.ylim(ymin, ymax)
        rcParams['font.size'] = old_fontsize
        return fig

    def _find_best_positions(self, G):
        """Finds best positions for the given graph (given as adjacency matrix)
        nodes by minimizing a network potential.
        """
        initpos = None
        holddim = None
        if self.xpos is not None:
            y = np.random.random(len(self.xpos))
            initpos = np.vstack((self.xpos, y)).T
            holddim = 0
        elif self.ypos is not None:
            x = np.zeros_like(self.xpos)
            initpos = np.vstack((x, self.ypos)).T
            holddim = 1
        # nothing to do
        elif self.xpos is not None and self.ypos is not None:
            return np.array([self.xpos, self.ypos]), 0
        from pyemma.plots._ext.fruchterman_reingold import _fruchterman_reingold
        best_pos = _fruchterman_reingold(G, pos=initpos, dim=2, hold_dim=holddim)

        # rescale fixed to user settings and balance the other coordinate
        if self.xpos is not None:
            # rescale x to fixed value
            best_pos[:, 0] *= (np.max(self.xpos) - np.min(self.xpos)
                               ) / (np.max(best_pos[:, 0]) - np.min(best_pos[:, 0]))
            best_pos[:, 0] += np.min(self.xpos) - np.min(best_pos[:, 0])
            # rescale y to balance
            if np.max(best_pos[:, 1]) - np.min(best_pos[:, 1]) > 0.01:
                best_pos[:, 1] *= (np.max(self.xpos) - np.min(self.xpos)
                                   ) / (np.max(best_pos[:, 1]) - np.min(best_pos[:, 1]))
        if self.ypos is not None:
            best_pos[:, 1] *= (np.max(self.ypos) - np.min(self.ypos)
                               ) / (np.max(best_pos[:, 1]) - np.min(best_pos[:, 1]))
            best_pos[:, 1] += np.min(self.ypos) - np.min(best_pos[:, 1])
            # rescale x to balance
            if np.max(best_pos[:, 0]) - np.min(best_pos[:, 0]) > 0.01:
                best_pos[:, 0] *= (np.max(self.ypos) - np.min(self.ypos)
                                   ) / (np.max(best_pos[:, 0]) - np.min(best_pos[:, 0]))

        return best_pos

    def layout_automatic(self):
        n = len(self.A)
        I, J = np.where(self.A > 0.0)
        # note: against intuition this has to be of type float
        A = np.zeros((n, n))
        A[I, J] = 1

        self.pos = self._find_best_positions(A)


def plot_markov_model(P, pos=None, state_sizes=None, state_scale=1.0,
                      state_colors='#ff5500', state_labels='auto', minflux=1e-6,
                      arrow_scale=1.0, arrow_curvature=1.0,
                      arrow_labels='weights', arrow_label_format='%2.e',
                      max_width=12, max_height=12, figpadding=0.2, show_frame=False,
                      **textkwargs):
    r"""Network representation of MSM transition matrix

    This visualization is not optimized for large matrices. It is meant to be
    used for the visualization of small models with up to 10-20 states, e.g.
    obtained by a HMM coarse-graining. If used with large network, the automatic
    node positioning will be very slow and may still look ugly.

    Parameters
    ----------
    P : ndarray(n,n) or MSM object with attribute 'transition matrix'
        Transition matrix or MSM object
    pos : ndarray(n,2), optional, default=None
        User-defined positions to draw the states on. If not given, will try
        to place them automatically.
    state_sizes : ndarray(n), optional, default=None
        User-defined areas of the discs drawn for each state. If not given,
        the stationary probability of P will be used.
    state_colors : string, ndarray(n), or list, optional, default='#ff5500' (orange)
        string :
            a Hex code for a single color used for all states
        array :
            n values in [0,1] which will result in a grayscale plot
        list :
            of len = nstates, with a color for each state. The list can mix strings, RGB values and hex codes,
            e.g. :py:obj:`state_colors` = ['g', 'red', [.23, .34, .35], '#ff5500'] is possible.
    state_labels : list of strings, optional, default is 'auto'
        A list with a label for each state, to be displayed at the center
        of each node/state. If left to 'auto', the labels are automatically set to the state indices.
    minflux : float, optional, default=1e-6
        The minimal flux (p_i * p_ij) for a transition to be drawn
    arrow_scale : float, optional, default=1.0
        Relative arrow scale. Set to a value different from 1 to increase
        or decrease the arrow width.
    arrow_curvature : float, optional, default=1.0
        Relative arrow curvature. Set to a value different from 1 to make
        arrows more or less curved.
    arrow_labels : 'weights', None or a ndarray(n,n) with label strings. Optional, default='weights'
        Strings to be placed upon arrows. If None, no labels will be used.
        If 'weights', the elements of P will be used. If a matrix of strings is
        given by the user these will be used.
    arrow_label_format : str, optional, default='%10.2f'
        The numeric format to print the arrow labels
    max_width = 12
        The maximum figure width
    max_height = 12
        The maximum figure height
    figpadding = 0.2
        The relative figure size used for the padding
    show_frame: boolean (default=False)
        Draw a frame around the network.
    textkwargs : optional argument for the text of the state labels.
        See http://matplotlib.org/api/text_api.html#matplotlib.text.Text for more info

    Returns
    -------
    fig, pos : matplotlib.Figure, ndarray(n,2)
    a Figure object containing the plot and the positions of states. 
    Can be used later to plot a different network representation (e.g. the flux)

    Examples
    --------
    >>> P = np.array([[0.8,  0.15, 0.05,  0.0,  0.0],
    ...              [0.1,  0.75, 0.05, 0.05, 0.05],
    ...              [0.05,  0.1,  0.8,  0.0,  0.05],
    ...              [0.0,  0.2, 0.0,  0.8,  0.0],
    ...              [0.0,  0.02, 0.02, 0.0,  0.96]])
    >>> plot_markov_model(P) # doctest:+ELLIPSIS
    (<matplotlib.figure.Figure..., array...)

    """
    from pyemma.msm import analysis as msmana
    if isinstance(P, np.ndarray):
        P = P.copy()
    else:
        # MSM object? then get transition matrix first
        P = P.transition_matrix.copy()
    if state_sizes is None:
        state_sizes = msmana.stationary_distribution(P)
    if minflux > 0:
        F = np.dot(np.diag(msmana.stationary_distribution(P)), P)
        I, J = np.where(F < minflux)
        P[I, J] = 0.0
    plot = NetworkPlot(P, pos=pos)
    ax = plot.plot_network(state_sizes=state_sizes, state_scale=state_scale,
                           state_colors=state_colors, state_labels=state_labels,
                           arrow_scale=arrow_scale, arrow_curvature=arrow_curvature,
                           arrow_labels=arrow_labels,
                           arrow_label_format=arrow_label_format,
                           max_width=max_width, max_height=max_height,
                           figpadding=figpadding, xticks=False, yticks=False, show_frame=show_frame,
                           **textkwargs)
    return ax, plot.pos


def plot_flux(flux, pos=None, state_sizes=None, flux_scale=1.0,
              state_scale=1.0, state_colors='#ff5500', state_labels='auto', minflux=1e-9,
              arrow_scale=1.0, arrow_curvature=1.0, arrow_labels='weights',
              arrow_label_format='%2.e', max_width=12, max_height=12,
              figpadding=0.2, attribute_to_plot='net_flux',
              show_frame=False, show_committor=True,
              **textkwargs):
    r"""Network representation of reactive flux

    This visualization is not optimized for large fluxes. It is meant to be used
    for the visualization of small models with up to 10-20 states, e.g. obtained
    by a PCCA-based coarse-graining of the full flux. If used with large
    network, the automatic node positioning will be very slow and may still look
    ugly.

    Parameters
    ----------
    flux : :class:`ReactiveFlux <pyemma.msm.flux.ReactiveFlux>`
        reactive flux object
    pos : ndarray(n,2), optional, default=None
        User-defined positions to draw the states on. If not given, will set the
        x coordinates equal to the committor probability and try to place the y
        coordinates automatically
    state_sizes : ndarray(n), optional, default=None
        User-defined areas of the discs drawn for each state. If not given, the
        stationary probability of P will be used
    state_colors : string, ndarray(n), or list, optional, default='#ff5500' (orange)
        string :
            a Hex code for a single color used for all states
        array :
            n values in [0,1] which will result in a grayscale plot
        list :
            of len = nstates, with a color for each state. The list can mix strings, RGB values and hex codes,
            e.g. :py:obj:`state_colors` = ['g', 'red', [.23, .34, .35], '#ff5500'] is possible.
    state_labels : list of strings, optional, default is 'auto'
        A list with a label for each state, to be displayed at the center
        of each node/state. If left to 'auto', the labels are automatically set to the state indices.
    minflux : float, optional, default=1e-9
        The minimal flux for a transition to be drawn
    arrow_scale : float, optional, default=1.0
        Relative arrow scale. Set to a value different from 1 to increase or
        decrease the arrow width.
    arrow_curvature : float, optional, default=1.0
        Relative arrow curvature. Set to a value different from 1 to make arrows
        more or less curved.
    arrow_labels : 'weights', None or a ndarray(n,n) with label strings. Optional, default='weights'
        Strings to be placed upon arrows. If None, no labels will be used. If
        'weights', the elements of P will be used. If a matrix of strings is
        given by the user these will be used.
    arrow_label_format : str, optional, default='%10.2f'
        The numeric format to print the arrow labels
    max_width : int (default = 12)
        The maximum figure width
    max_height: int (default = 12)
        The maximum figure height
    figpadding: float (default = 0.2)
        The relative figure size used for the padding
    show_frame: boolean (default=False)
        Draw a frame around the network.
    show_committor: boolean (default=False)
        Print the committor value on the x-axis.
    textkwargs : optional argument for the text of the state labels.
        See http://matplotlib.org/api/text_api.html#matplotlib.text.Text for more info

    Returns
    -------
    (fig, pos) : matpotlib.Figure instance, ndarray
        Axes instances containing the plot. Use pyplot.show() to display it.
        The positions of states. Can be used later to plot a different network
        representation (e.g. the flux).

    Examples
    --------
    We define first define a reactive flux by taking the following transition
    matrix and computing TPT from state 2 to 3

    >>> import numpy as np
    >>> P = np.array([[0.8,  0.15, 0.05,  0.0,  0.0],
    ...               [0.1,  0.75, 0.05, 0.05, 0.05],
    ...               [0.05,  0.1,  0.8,  0.0,  0.05],
    ...               [0.0,  0.2, 0.0,  0.8,  0.0],
    ...               [0.0,  0.02, 0.02, 0.0,  0.96]])
    >>> from pyemma import msm
    >>> F = msm.tpt(msm.markov_model(P), [2], [3])
    >>> F.flux[:] *= 100

    Scale the flux by 100 is basically a change of units to get numbers close
    to 1 (avoid printing many zeros). Now we visualize the flux:

    >>> plot_flux(F) # doctest:+ELLIPSIS
    (<matplotlib.figure.Figure..., array...)

    """
    from matplotlib import pylab as plt
    F = flux_scale * getattr(flux, attribute_to_plot)
    c = flux.committor
    if state_sizes is None:
        state_sizes = flux.stationary_distribution
    plot = NetworkPlot(F, pos=pos, xpos=c)
    if minflux > 0:
        I, J = np.where(F < minflux)
        F[I, J] = 0.0
    ax = plot.plot_network(state_sizes=state_sizes, state_scale=state_scale,
                           state_colors=state_colors, state_labels=state_labels,
                           arrow_scale=arrow_scale, arrow_curvature=arrow_curvature,
                           arrow_labels=arrow_labels,
                           arrow_label_format=arrow_label_format,
                           max_width=max_width, max_height=max_height,
                           figpadding=figpadding, xticks=show_committor, yticks=False, show_frame=show_frame,
                           **textkwargs)
    if show_committor:
        
        plt.xlabel('Committor probability')
    return ax, plot.pos


def plot_network(weights, pos=None, xpos=None, ypos=None, state_sizes=None,
                state_scale=1.0, state_colors='#ff5500', minflux=1e-9,
                arrow_scale=1.0, arrow_curvature=1.0, arrow_labels='weights',
                arrow_label_format='%2.e', max_width=12, max_height=12,
                figpadding=0.2, attribute_to_plot='net_flux',
                show_frame=False, xticks=False, yticks=False,
                ):
    r"""Network representation of given matrix

    This visualization is not optimized for large networks. It is meant to be
    used for the visualization of small models with up to 10-20 states. If used
    with large network, the automatic node positioning will be very slow and
    may still look ugly.

    Parameters
    ----------
    flux : :class:`ReactiveFlux <pyemma.msm.flux.ReactiveFlux>`
        reactive flux object
    pos : ndarray(n,2), optional, default=None
        User-defined positions to draw the states on.
    xpos : ndarray(n,), optional, default=None
        Fixes the x positions while the y positions are optimized
    ypos : ndarray(n,), optional, default=None
        Fixes the y positions while the x positions are optimized
    state_sizes : ndarray(n), optional, default=None
        User-defined areas of the discs drawn for each state. If not given, the
        stationary probability of P will be used
    state_colors : string, ndarray(n), or list, optional, default='#ff5500' (orange)
        string :
            a Hex code for a single color used for all states
        array :
            n values in [0,1] which will result in a grayscale plot
        list :
            of len = nstates, with a color for each state. The list can mix strings, RGB values and hex codes,
            e.g. :py:obj:`state_colors` = ['g', 'red', [.23, .34, .35], '#ff5500'] is possible.
    state_labels : list of strings, optional, default is 'auto'
        A list with a label for each state, to be displayed at the center
        of each node/state. If left to 'auto', the labels are automatically set to the state indices.
    minflux : float, optional, default=1e-9
        The minimal flux for a transition to be drawn
    arrow_scale : float, optional, default=1.0
        Relative arrow scale. Set to a value different from 1 to increase or
        decrease the arrow width.
    arrow_curvature : float, optional, default=1.0
        Relative arrow curvature. Set to a value different from 1 to make arrows
        more or less curved.
    arrow_labels : 'weights', None or a ndarray(n,n) with label strings. Optional, default='weights'
        Strings to be placed upon arrows. If None, no labels will be used. If
        'weights', the elements of P will be used. If a matrix of strings is
        given by the user these will be used.
    arrow_label_format : str, optional, default='%10.2f'
        The numeric format to print the arrow labels
    max_width : int (default = 12)
        The maximum figure width
    max_height: int (default = 12)
        The maximum figure height
    figpadding: float (default = 0.2)
        The relative figure size used for the padding
    show_frame: boolean (default=False)
        Draw a frame around the network.
    xticks: boolean (default=False)
        Show x ticks
    yticks: boolean (default=False)
        Show y ticks

    Returns
    -------
    (fig, pos) : matpotlib.Figure instance, ndarray
        Axes instances containing the plot. Use pyplot.show() to display it.
        The positions of states. Can be used later to plot a different network
        representation (e.g. the flux).

    Examples
    --------
    We define first define a reactive flux by taking the following transition
    matrix and computing TPT from state 2 to 3

    >>> import numpy as np
    >>> P = np.array([[0.8,  0.15, 0.05,  0.0,  0.0],
    ...               [0.1,  0.75, 0.05, 0.05, 0.05],
    ...               [0.05,  0.1,  0.8,  0.0,  0.05],
    ...               [0.0,  0.2, 0.0,  0.8,  0.0],
    ...               [0.0,  0.02, 0.02, 0.0,  0.96]])

    Scale the flux by 100 is basically a change of units to get numbers close
    to 1 (avoid printing many zeros). Now we visualize the flux:

    >>> plot_network(P) # doctest:+ELLIPSIS
    (<matplotlib.figure.Figure..., array...)

    """
    plot = NetworkPlot(weights, pos=pos, xpos=xpos, ypos=ypos)
    ax = plot.plot_network(state_sizes=state_sizes, state_scale=state_scale,
                           state_colors=state_colors,
                           arrow_scale=arrow_scale, arrow_curvature=arrow_curvature,
                           arrow_labels=arrow_labels,
                           arrow_label_format=arrow_label_format,
                           max_width=max_width, max_height=max_height,
                           figpadding=figpadding, xticks=xticks, yticks=yticks, show_frame=show_frame)
    return ax, plot.pos
