mathmat.vis

MathMat Visualization Toolbox.

  1"""MathMat Visualization Toolbox."""
  2
  3import matplotlib.pyplot as plt
  4from matplotlib import use as use_mpl_backend
  5import numpy as np
  6
  7from mathmat import Matrix, EPS
  8
  9# Configure Matplotlib
 10use_mpl_backend("TkAgg")
 11params = {"ytick.color": "black",
 12          "xtick.color": "black",
 13          "axes.labelcolor": "black",
 14          "axes.edgecolor": "black",
 15          "axes.formatter.use_mathtext": True,
 16          "text.usetex": False,
 17          "font.family": "serif",
 18          "font.serif": "cmr10",
 19          "mathtext.fontset": "cm",
 20          "figure.dpi": 100,
 21          "figure.autolayout": True,
 22          "savefig.dpi": 300,
 23          "savefig.transparent": True,
 24          "savefig.bbox": "tight",
 25          "font.size": 12,
 26          "lines.linewidth": 2}
 27plt.rcParams.update(params)
 28
 29
 30class Plot:
 31    """A Plot is the base class for all visualizations.
 32    Do not use Plot directly, as it does not show any data.
 33    Instead, extend it or use a specific type of Plot."""
 34
 35    def __init__(self, legend_labels=None, title=None, x_label=None,
 36                 y_label=None, **mpl_kwargs):
 37        if type(legend_labels) is not list \
 38                and legend_labels is not None:
 39            legend_labels = [legend_labels]
 40        self.legend_labels = legend_labels
 41        self.title = title
 42        self.x_label = x_label
 43        self.y_label = y_label
 44        self._mpl_kwargs = mpl_kwargs
 45
 46    def plot_on(self, axes):
 47        """Show the Plot on the specified matplotlib Axes."""
 48        if self.legend_labels is not None:
 49            axes.legend(self.legend_labels)
 50        if self.title is not None:
 51            axes.set_title(self.title)
 52        if self.x_label is not None:
 53            axes.set_xlabel(self.x_label)
 54        if self.y_label is not None:
 55            axes.set_ylabel(self.y_label)
 56        axes.margins(x=0, y=0)
 57
 58
 59class DataPlot(Plot):
 60    """A DataPlot visualizes data. Do not use it directly, instead
 61    call LinePlot or ScatterPlot."""
 62
 63    def __init__(self, Xs, Ys, formats=None, legend_labels=None, log_x=False,
 64                 log_y=False, title=None, x_label=None,
 65                 y_label=None, **mpl_kwargs):
 66        super().__init__(legend_labels, title, x_label, y_label, **mpl_kwargs)
 67        self.Xs = np.array(Xs)
 68        self.Ys = np.array(Ys)
 69        self.formats = formats
 70        self.log_x = log_x
 71        self.log_y = log_y
 72        self._plt_args = (self.Xs.T, self.Ys.T) if self.formats is None else \
 73            (self.Xs, self.Ys, self.formats)
 74
 75    def plot_on(self, axes):
 76        if self.log_x:
 77            axes.set_xscale("log")
 78        else:
 79            axes.set_xscale("linear")
 80        if self.log_y:
 81            axes.set_yscale("log")
 82        else:
 83            axes.set_yscale("linear")
 84        super().plot_on(axes)
 85
 86
 87class LinePlot(DataPlot):
 88    """A LinePlot visualizes data as lines.
 89    `Xs` and `Ys` must be are array-like objects of equal length.
 90    Optionally, `formats` is an array of equal length containing
 91    format specifications for `matplotlib`.
 92    `log_x` and `log_y` are used to specify logarithmic scaling of axes."""
 93
 94    def plot_on(self, axes):
 95        axes.plot(*self._plt_args, **self._mpl_kwargs)
 96        super().plot_on(axes)
 97
 98
 99class ScatterPlot(DataPlot):
100    """A LinePlot visualizes data as points.
101    `Xs` and `Ys` must be are array-like objects of equal length.
102    Optionally, `formats` is an array of equal length containing
103    format specifications for `matplotlib`.
104    `log_x` and `log_y` are used to specify logarithmic scaling of axes."""
105
106    def plot_on(self, axes):
107        axes.scatter(*self._plt_args, **self._mpl_kwargs)
108        super().plot_on(axes)
109
110
111class Histogram(Plot):
112    """A Histogram visualizes data by grouping it based on values.
113    `log_x` and `log_y` are used to specify logarithmic scaling of axes."""
114
115    def __init__(self, Xs, bins=None,
116                 legend_labels=None, title=None, x_label=None, y_label=None,
117                 **mpl_kwargs):
118        super().__init__(legend_labels, title, x_label, y_label,
119                         **mpl_kwargs)
120        self.Xs = np.array(Xs)
121        self.bins = min(len(Xs), 10) if bins is None else bins
122
123    def plot_on(self, axes):
124        axes.hist(self.Xs, self.bins, **self._mpl_kwargs)
125        super().plot_on(axes)
126
127
128class MagnitudePlot(Plot):
129    """A MagnitudePlot shows the magnitude of the entries of a Matrix.
130    Densifies."""
131
132    def __init__(self, M,
133                 legend_labels=None, title=None, x_label=None, y_label=None,
134                 **mpl_kwargs):
135        super().__init__(legend_labels, title, x_label, y_label, **mpl_kwargs)
136        if not isinstance(M, Matrix):
137            M = Matrix(M)
138        if M.is_sparse():
139            M = M.to_dense()
140        if M.is_complex():
141            M = Matrix(np.absolute(M.entries))
142        self.M = M
143        if "cmap" not in self._mpl_kwargs:
144            self._mpl_kwargs["cmap"] = "gray_r"
145
146    def plot_on(self, axes):
147        pos = axes.matshow(self.M.entries, **self._mpl_kwargs)
148        plt.gcf().colorbar(pos, ax=axes, fraction=0.046)
149        super().plot_on(axes)
150
151
152class SparsityPlot(MagnitudePlot):
153    """A SparsityPlot shows the sparsity of a Matrix object.
154    Optionally specify a tolerance to treat entries as zero. Densifies."""
155
156    def __init__(self, M, tolerance=EPS,
157                 legend_labels=None, title=None, x_label=None, y_label=None,
158                 **mpl_kwargs):
159        super().__init__(M, legend_labels, title, x_label, y_label,
160                         **mpl_kwargs)
161        self.M = Matrix(~np.isclose(self.M.entries, 0, atol=tolerance))
162
163    def plot_on(self, axes):
164        axes.matshow(self.M.entries, **self._mpl_kwargs)
165        Plot.plot_on(self, axes)
166
167
168class OrthogonalityPlot(MagnitudePlot):
169    """An OrthogonalityPlot treats a Matrix as a set of column vectors,
170    and shows the magnitude of each dot product by computing M^H M."""
171
172    def __init__(self, M,
173                 legend_labels=None, title=None, x_label=None, y_label=None,
174                 **mpl_kwargs):
175        super().__init__(M, legend_labels, title, x_label, y_label,
176                         **mpl_kwargs)
177        self.M = Matrix(
178            np.abs((self.M.conjugate().transpose() @ self.M).entries))
179
180
181def single(plot, **mpl_kwargs):
182    """Show a single Plot on a new figure."""
183    if not isinstance(plot, Plot):
184        raise ValueError("Cannot plot {}.".format(str(plot)))
185
186    figure = plt.figure(**mpl_kwargs)
187    plot.plot_on(figure.gca())
188    return figure
189
190
191def tiled(plot_array, sup_title=None, sup_x_label=None, sup_y_label=None,
192          **mpl_kwargs):
193    """Tile the given plots in a grid.
194    `plot_array` can be a single plot, a one-dimensional list,
195    which will be rendered as a row of plots, or a two dimensional
196    list, which will be rendered as a grid of plots."""
197    if isinstance(plot_array, Plot):
198        plot_array = [[plot_array]]
199    if type(plot_array) is list or type(plot_array) is tuple:
200        if type(plot_array[0]) is list or type(plot_array[0]) is tuple:
201            nr = len(plot_array)
202            nc = len(plot_array[0])
203        else:
204            nr = 1
205            nc = len(plot_array)
206            plot_array = [plot_array]
207    else:
208        raise ValueError(
209            "Cannot interpret the plot array {}.".format(str(plot_array)))
210
211    figure, axes_arr = plt.subplots(nr, nc, **mpl_kwargs)
212    if len(axes_arr.shape) == 1:
213        axes_arr = np.array([axes_arr], dtype=object)
214    # Plot the super-titles and labels if specified
215    if sup_title is not None:
216        figure.suptitle(sup_title)
217    if sup_x_label is not None:
218        figure.supxlabel(sup_x_label)
219    if sup_y_label is not None:
220        figure.supylabel(sup_y_label)
221
222    # Generate all plots
223    for row in range(nr):
224        for col in range(nc):
225            plot_array[row][col].plot_on(axes_arr[row][col])
226
227    return figure
params = {'ytick.color': 'black', 'xtick.color': 'black', 'axes.labelcolor': 'black', 'axes.edgecolor': 'black', 'axes.formatter.use_mathtext': True, 'text.usetex': False, 'font.family': 'serif', 'font.serif': 'cmr10', 'mathtext.fontset': 'cm', 'figure.dpi': 100, 'figure.autolayout': True, 'savefig.dpi': 300, 'savefig.transparent': True, 'savefig.bbox': 'tight', 'font.size': 12, 'lines.linewidth': 2}
class Plot:
31class Plot:
32    """A Plot is the base class for all visualizations.
33    Do not use Plot directly, as it does not show any data.
34    Instead, extend it or use a specific type of Plot."""
35
36    def __init__(self, legend_labels=None, title=None, x_label=None,
37                 y_label=None, **mpl_kwargs):
38        if type(legend_labels) is not list \
39                and legend_labels is not None:
40            legend_labels = [legend_labels]
41        self.legend_labels = legend_labels
42        self.title = title
43        self.x_label = x_label
44        self.y_label = y_label
45        self._mpl_kwargs = mpl_kwargs
46
47    def plot_on(self, axes):
48        """Show the Plot on the specified matplotlib Axes."""
49        if self.legend_labels is not None:
50            axes.legend(self.legend_labels)
51        if self.title is not None:
52            axes.set_title(self.title)
53        if self.x_label is not None:
54            axes.set_xlabel(self.x_label)
55        if self.y_label is not None:
56            axes.set_ylabel(self.y_label)
57        axes.margins(x=0, y=0)

A Plot is the base class for all visualizations. Do not use Plot directly, as it does not show any data. Instead, extend it or use a specific type of Plot.

Plot( legend_labels=None, title=None, x_label=None, y_label=None, **mpl_kwargs)
36    def __init__(self, legend_labels=None, title=None, x_label=None,
37                 y_label=None, **mpl_kwargs):
38        if type(legend_labels) is not list \
39                and legend_labels is not None:
40            legend_labels = [legend_labels]
41        self.legend_labels = legend_labels
42        self.title = title
43        self.x_label = x_label
44        self.y_label = y_label
45        self._mpl_kwargs = mpl_kwargs
legend_labels
title
x_label
y_label
def plot_on(self, axes):
47    def plot_on(self, axes):
48        """Show the Plot on the specified matplotlib Axes."""
49        if self.legend_labels is not None:
50            axes.legend(self.legend_labels)
51        if self.title is not None:
52            axes.set_title(self.title)
53        if self.x_label is not None:
54            axes.set_xlabel(self.x_label)
55        if self.y_label is not None:
56            axes.set_ylabel(self.y_label)
57        axes.margins(x=0, y=0)

Show the Plot on the specified matplotlib Axes.

class DataPlot(Plot):
60class DataPlot(Plot):
61    """A DataPlot visualizes data. Do not use it directly, instead
62    call LinePlot or ScatterPlot."""
63
64    def __init__(self, Xs, Ys, formats=None, legend_labels=None, log_x=False,
65                 log_y=False, title=None, x_label=None,
66                 y_label=None, **mpl_kwargs):
67        super().__init__(legend_labels, title, x_label, y_label, **mpl_kwargs)
68        self.Xs = np.array(Xs)
69        self.Ys = np.array(Ys)
70        self.formats = formats
71        self.log_x = log_x
72        self.log_y = log_y
73        self._plt_args = (self.Xs.T, self.Ys.T) if self.formats is None else \
74            (self.Xs, self.Ys, self.formats)
75
76    def plot_on(self, axes):
77        if self.log_x:
78            axes.set_xscale("log")
79        else:
80            axes.set_xscale("linear")
81        if self.log_y:
82            axes.set_yscale("log")
83        else:
84            axes.set_yscale("linear")
85        super().plot_on(axes)

A DataPlot visualizes data. Do not use it directly, instead call LinePlot or ScatterPlot.

DataPlot( Xs, Ys, formats=None, legend_labels=None, log_x=False, log_y=False, title=None, x_label=None, y_label=None, **mpl_kwargs)
64    def __init__(self, Xs, Ys, formats=None, legend_labels=None, log_x=False,
65                 log_y=False, title=None, x_label=None,
66                 y_label=None, **mpl_kwargs):
67        super().__init__(legend_labels, title, x_label, y_label, **mpl_kwargs)
68        self.Xs = np.array(Xs)
69        self.Ys = np.array(Ys)
70        self.formats = formats
71        self.log_x = log_x
72        self.log_y = log_y
73        self._plt_args = (self.Xs.T, self.Ys.T) if self.formats is None else \
74            (self.Xs, self.Ys, self.formats)
Xs
Ys
formats
log_x
log_y
def plot_on(self, axes):
76    def plot_on(self, axes):
77        if self.log_x:
78            axes.set_xscale("log")
79        else:
80            axes.set_xscale("linear")
81        if self.log_y:
82            axes.set_yscale("log")
83        else:
84            axes.set_yscale("linear")
85        super().plot_on(axes)

Show the Plot on the specified matplotlib Axes.

Inherited Members
Plot
legend_labels
title
x_label
y_label
class LinePlot(DataPlot):
88class LinePlot(DataPlot):
89    """A LinePlot visualizes data as lines.
90    `Xs` and `Ys` must be are array-like objects of equal length.
91    Optionally, `formats` is an array of equal length containing
92    format specifications for `matplotlib`.
93    `log_x` and `log_y` are used to specify logarithmic scaling of axes."""
94
95    def plot_on(self, axes):
96        axes.plot(*self._plt_args, **self._mpl_kwargs)
97        super().plot_on(axes)

A LinePlot visualizes data as lines. Xs and Ys must be are array-like objects of equal length. Optionally, formats is an array of equal length containing format specifications for matplotlib. log_x and log_y are used to specify logarithmic scaling of axes.

def plot_on(self, axes):
95    def plot_on(self, axes):
96        axes.plot(*self._plt_args, **self._mpl_kwargs)
97        super().plot_on(axes)

Show the Plot on the specified matplotlib Axes.

class ScatterPlot(DataPlot):
100class ScatterPlot(DataPlot):
101    """A LinePlot visualizes data as points.
102    `Xs` and `Ys` must be are array-like objects of equal length.
103    Optionally, `formats` is an array of equal length containing
104    format specifications for `matplotlib`.
105    `log_x` and `log_y` are used to specify logarithmic scaling of axes."""
106
107    def plot_on(self, axes):
108        axes.scatter(*self._plt_args, **self._mpl_kwargs)
109        super().plot_on(axes)

A LinePlot visualizes data as points. Xs and Ys must be are array-like objects of equal length. Optionally, formats is an array of equal length containing format specifications for matplotlib. log_x and log_y are used to specify logarithmic scaling of axes.

def plot_on(self, axes):
107    def plot_on(self, axes):
108        axes.scatter(*self._plt_args, **self._mpl_kwargs)
109        super().plot_on(axes)

Show the Plot on the specified matplotlib Axes.

class Histogram(Plot):
112class Histogram(Plot):
113    """A Histogram visualizes data by grouping it based on values.
114    `log_x` and `log_y` are used to specify logarithmic scaling of axes."""
115
116    def __init__(self, Xs, bins=None,
117                 legend_labels=None, title=None, x_label=None, y_label=None,
118                 **mpl_kwargs):
119        super().__init__(legend_labels, title, x_label, y_label,
120                         **mpl_kwargs)
121        self.Xs = np.array(Xs)
122        self.bins = min(len(Xs), 10) if bins is None else bins
123
124    def plot_on(self, axes):
125        axes.hist(self.Xs, self.bins, **self._mpl_kwargs)
126        super().plot_on(axes)

A Histogram visualizes data by grouping it based on values. log_x and log_y are used to specify logarithmic scaling of axes.

Histogram( Xs, bins=None, legend_labels=None, title=None, x_label=None, y_label=None, **mpl_kwargs)
116    def __init__(self, Xs, bins=None,
117                 legend_labels=None, title=None, x_label=None, y_label=None,
118                 **mpl_kwargs):
119        super().__init__(legend_labels, title, x_label, y_label,
120                         **mpl_kwargs)
121        self.Xs = np.array(Xs)
122        self.bins = min(len(Xs), 10) if bins is None else bins
Xs
bins
def plot_on(self, axes):
124    def plot_on(self, axes):
125        axes.hist(self.Xs, self.bins, **self._mpl_kwargs)
126        super().plot_on(axes)

Show the Plot on the specified matplotlib Axes.

Inherited Members
Plot
legend_labels
title
x_label
y_label
class MagnitudePlot(Plot):
129class MagnitudePlot(Plot):
130    """A MagnitudePlot shows the magnitude of the entries of a Matrix.
131    Densifies."""
132
133    def __init__(self, M,
134                 legend_labels=None, title=None, x_label=None, y_label=None,
135                 **mpl_kwargs):
136        super().__init__(legend_labels, title, x_label, y_label, **mpl_kwargs)
137        if not isinstance(M, Matrix):
138            M = Matrix(M)
139        if M.is_sparse():
140            M = M.to_dense()
141        if M.is_complex():
142            M = Matrix(np.absolute(M.entries))
143        self.M = M
144        if "cmap" not in self._mpl_kwargs:
145            self._mpl_kwargs["cmap"] = "gray_r"
146
147    def plot_on(self, axes):
148        pos = axes.matshow(self.M.entries, **self._mpl_kwargs)
149        plt.gcf().colorbar(pos, ax=axes, fraction=0.046)
150        super().plot_on(axes)

A MagnitudePlot shows the magnitude of the entries of a Matrix. Densifies.

MagnitudePlot( M, legend_labels=None, title=None, x_label=None, y_label=None, **mpl_kwargs)
133    def __init__(self, M,
134                 legend_labels=None, title=None, x_label=None, y_label=None,
135                 **mpl_kwargs):
136        super().__init__(legend_labels, title, x_label, y_label, **mpl_kwargs)
137        if not isinstance(M, Matrix):
138            M = Matrix(M)
139        if M.is_sparse():
140            M = M.to_dense()
141        if M.is_complex():
142            M = Matrix(np.absolute(M.entries))
143        self.M = M
144        if "cmap" not in self._mpl_kwargs:
145            self._mpl_kwargs["cmap"] = "gray_r"
M
def plot_on(self, axes):
147    def plot_on(self, axes):
148        pos = axes.matshow(self.M.entries, **self._mpl_kwargs)
149        plt.gcf().colorbar(pos, ax=axes, fraction=0.046)
150        super().plot_on(axes)

Show the Plot on the specified matplotlib Axes.

Inherited Members
Plot
legend_labels
title
x_label
y_label
class SparsityPlot(MagnitudePlot):
153class SparsityPlot(MagnitudePlot):
154    """A SparsityPlot shows the sparsity of a Matrix object.
155    Optionally specify a tolerance to treat entries as zero. Densifies."""
156
157    def __init__(self, M, tolerance=EPS,
158                 legend_labels=None, title=None, x_label=None, y_label=None,
159                 **mpl_kwargs):
160        super().__init__(M, legend_labels, title, x_label, y_label,
161                         **mpl_kwargs)
162        self.M = Matrix(~np.isclose(self.M.entries, 0, atol=tolerance))
163
164    def plot_on(self, axes):
165        axes.matshow(self.M.entries, **self._mpl_kwargs)
166        Plot.plot_on(self, axes)

A SparsityPlot shows the sparsity of a Matrix object. Optionally specify a tolerance to treat entries as zero. Densifies.

SparsityPlot( M, tolerance=2.220446049250313e-16, legend_labels=None, title=None, x_label=None, y_label=None, **mpl_kwargs)
157    def __init__(self, M, tolerance=EPS,
158                 legend_labels=None, title=None, x_label=None, y_label=None,
159                 **mpl_kwargs):
160        super().__init__(M, legend_labels, title, x_label, y_label,
161                         **mpl_kwargs)
162        self.M = Matrix(~np.isclose(self.M.entries, 0, atol=tolerance))
M
def plot_on(self, axes):
164    def plot_on(self, axes):
165        axes.matshow(self.M.entries, **self._mpl_kwargs)
166        Plot.plot_on(self, axes)

Show the Plot on the specified matplotlib Axes.

Inherited Members
Plot
legend_labels
title
x_label
y_label
class OrthogonalityPlot(MagnitudePlot):
169class OrthogonalityPlot(MagnitudePlot):
170    """An OrthogonalityPlot treats a Matrix as a set of column vectors,
171    and shows the magnitude of each dot product by computing M^H M."""
172
173    def __init__(self, M,
174                 legend_labels=None, title=None, x_label=None, y_label=None,
175                 **mpl_kwargs):
176        super().__init__(M, legend_labels, title, x_label, y_label,
177                         **mpl_kwargs)
178        self.M = Matrix(
179            np.abs((self.M.conjugate().transpose() @ self.M).entries))

An OrthogonalityPlot treats a Matrix as a set of column vectors, and shows the magnitude of each dot product by computing M^H M.

OrthogonalityPlot( M, legend_labels=None, title=None, x_label=None, y_label=None, **mpl_kwargs)
173    def __init__(self, M,
174                 legend_labels=None, title=None, x_label=None, y_label=None,
175                 **mpl_kwargs):
176        super().__init__(M, legend_labels, title, x_label, y_label,
177                         **mpl_kwargs)
178        self.M = Matrix(
179            np.abs((self.M.conjugate().transpose() @ self.M).entries))
M
def single(plot, **mpl_kwargs):
182def single(plot, **mpl_kwargs):
183    """Show a single Plot on a new figure."""
184    if not isinstance(plot, Plot):
185        raise ValueError("Cannot plot {}.".format(str(plot)))
186
187    figure = plt.figure(**mpl_kwargs)
188    plot.plot_on(figure.gca())
189    return figure

Show a single Plot on a new figure.

def tiled( plot_array, sup_title=None, sup_x_label=None, sup_y_label=None, **mpl_kwargs):
192def tiled(plot_array, sup_title=None, sup_x_label=None, sup_y_label=None,
193          **mpl_kwargs):
194    """Tile the given plots in a grid.
195    `plot_array` can be a single plot, a one-dimensional list,
196    which will be rendered as a row of plots, or a two dimensional
197    list, which will be rendered as a grid of plots."""
198    if isinstance(plot_array, Plot):
199        plot_array = [[plot_array]]
200    if type(plot_array) is list or type(plot_array) is tuple:
201        if type(plot_array[0]) is list or type(plot_array[0]) is tuple:
202            nr = len(plot_array)
203            nc = len(plot_array[0])
204        else:
205            nr = 1
206            nc = len(plot_array)
207            plot_array = [plot_array]
208    else:
209        raise ValueError(
210            "Cannot interpret the plot array {}.".format(str(plot_array)))
211
212    figure, axes_arr = plt.subplots(nr, nc, **mpl_kwargs)
213    if len(axes_arr.shape) == 1:
214        axes_arr = np.array([axes_arr], dtype=object)
215    # Plot the super-titles and labels if specified
216    if sup_title is not None:
217        figure.suptitle(sup_title)
218    if sup_x_label is not None:
219        figure.supxlabel(sup_x_label)
220    if sup_y_label is not None:
221        figure.supylabel(sup_y_label)
222
223    # Generate all plots
224    for row in range(nr):
225        for col in range(nc):
226            plot_array[row][col].plot_on(axes_arr[row][col])
227
228    return figure

Tile the given plots in a grid. plot_array can be a single plot, a one-dimensional list, which will be rendered as a row of plots, or a two dimensional list, which will be rendered as a grid of plots.