"""Plotting functions for (vector) representations of persistence diagrams."""
# License: GNU AGPLv3
import numpy as np
import plotly.graph_objs as gobj
[docs]def plot_betti_curves(betti_numbers, samplings, homology_dimensions=None,
                      plotly_params=None):
    """Plot Betti curves by homology dimension.
    Parameters
    ----------
    betti_numbers : ndarray of shape (n_homology_dimensions, n_bins)
        Betti numbers, i.e. the y-coordinates of Betti curves. Entry i along
        axis 0 is assumed to contain the Betti numbers for a discretised Betti
        curve in homology dimension i.
    samplings : ndarray of shape (n_homology_dimensions, n_bins)
        Filtration parameter values to be used as the x-coordinates of the
        Betti curves.
    homology_dimensions : list, tuple or None, optional, default: ``None``
        Which homology dimensions to include in the plot. If ``None``,
        all available homology dimensions will be used.
    plotly_params : dict or None, optional, default: ``None``
        Custom parameters to configure the plotly figure. Allowed keys are
        ``"traces"`` and ``"layout"``, and the corresponding values should be
        dictionaries containing keyword arguments as would be fed to the
        :meth:`update_traces` and :meth:`update_layout` methods of
        :class:`plotly.graph_objects.Figure`.
    Returns
    -------
    fig : :class:`plotly.graph_objects.Figure` object
        Figure representing the Betti curves.
    """
    if homology_dimensions is None:
        _homology_dimensions = list(range(betti_numbers.shape[0]))
    else:
        _homology_dimensions = homology_dimensions
    layout = {
        "xaxis1": {
            "title": "Filtration parameter",
            "side": "bottom",
            "type": "linear",
            "ticks": "outside",
            "anchor": "x1",
            "showline": True,
            "zeroline": True,
            "showexponent": "all",
            "exponentformat": "e"
            },
        "yaxis1": {
            "title": "Betti number",
            "side": "left",
            "type": "linear",
            "ticks": "outside",
            "anchor": "y1",
            "showline": True,
            "zeroline": True,
            "showexponent": "all",
            "exponentformat": "e"
            },
        "plot_bgcolor": "white"
        }
    fig = gobj.Figure(layout=layout)
    fig.update_xaxes(zeroline=True, linewidth=1, linecolor="black",
                     mirror=False)
    fig.update_yaxes(zeroline=True, linewidth=1, linecolor="black",
                     mirror=False)
    for dim in _homology_dimensions:
        fig.add_trace(gobj.Scatter(x=samplings[dim],
                                   y=betti_numbers[dim],
                                   mode="lines", showlegend=True,
                                   hoverinfo="none",
                                   name=f"H{int(dim)}"))
    # Update traces and layout according to user input
    if plotly_params:
        fig.update_traces(plotly_params.get("traces", None))
        fig.update_layout(plotly_params.get("layout", None))
    return fig 
[docs]def plot_betti_surfaces(betti_curves, samplings=None,
                        homology_dimensions=None, plotly_params=None):
    """Plot Betti surfaces (Betti numbers against "time" and filtration
    parameter) by homology dimension.
    Parameters
    ----------
    betti_curves : ndarray of shape (n_samples, n_homology_dimensions, \
        n_bins)
        Collection whose each entry contains the Betti numbers for
        ``n_homology_dimensions`` discretised Betti curves. Index i along axis
        1 is assumed to correspond to homology dimension i.
    samplings : ndarray of shape (n_homology_dimensions, n_bins)
        Filtration parameter values to be used as one of the independent
        variables when plotting the Betti surfaces. The other independent
        variable is "time", i.e. the sample index.
    homology_dimensions : list, tuple or None, optional, default: ``None``
        Homology dimensions for which the Betti surfaces should be plotted.
        If ``None``, all available dimensions will be used.
    samplings : ndarray of shape (n_homology_dimensions, n_bins)
        For each homology dimension, (filtration parameter) values to be used
        on the x-axis against the corresponding values in `betti_curves` on the
        y-axis.
    plotly_params : dict or None, optional, default: ``None``
        Custom parameters to configure the plotly figure. Allowed keys are
        ``"traces"`` and ``"layout"``, and the corresponding values should be
        dictionaries containing keyword arguments as would be fed to the
        :meth:`update_traces` and :meth:`update_layout` methods of
        :class:`plotly.graph_objects.Figure`.
    Returns
    -------
    figs/fig : tuple of :class:`plotly.graph_objects.Figure`/\
        :class:`plotly.graph_objects.Figure` object
        If ``n_samples > 1``, a tuple of figures representing the Betti
        surfaces, with one figure per dimension in `homology_dimensions`.
        Otherwise, a single figure representing the Betti curve of the
        single sample present.
    """
    if homology_dimensions is None:
        _homology_dimensions = list(range(betti_curves.shape[1]))
    else:
        _homology_dimensions = homology_dimensions
    scene = {
        "xaxis": {
            "title": "Filtration parameter",
            "type": "linear",
            "showexponent": "all",
            "exponentformat": "e"
            },
        "yaxis": {
            "title": "Time",
            "type": "linear",
            "showexponent": "all",
            "exponentformat": "e"
            },
        "zaxis": {
            "title": "Betti number",
            "type": "linear",
            "showexponent": "all",
            "exponentformat": "e"
            }
        }
    if betti_curves.shape[0] == 1:
        return plot_betti_curves(
            betti_curves[0], samplings,
            homology_dimensions=homology_dimensions,
            plotly_params=plotly_params
            )
    else:
        figs = []
        for dim in _homology_dimensions:
            fig = gobj.Figure()
            fig.update_layout(scene=scene,
                              title=f"Betti surface for homology "
                                    f"dimension {int(dim)}")
            fig.add_trace(gobj.Surface(x=samplings[dim],
                                       y=np.arange(betti_curves.shape[0]),
                                       z=betti_curves[:, dim],
                                       connectgaps=True, hoverinfo="none"))
            # Update traces and layout according to user input
            if plotly_params:
                fig.update_traces(plotly_params.get("traces", None))
                fig.update_layout(plotly_params.get("layout", None))
            figs.append(fig)
        return tuple(figs)