|
| 1 | +import matplotlib.pyplot as plt |
| 2 | +import numpy as np |
| 3 | +import matplotlib.collections as collections |
| 4 | +import pytheus.theseus as th |
| 5 | +import pytheus.analyzer as anal |
| 6 | +import matplotlib.patheffects as pe |
| 7 | +from pytheus.fancy_classes import Graph |
| 8 | +import json |
| 9 | +import os |
| 10 | +import pytheus.leiwand |
| 11 | + |
| 12 | + |
| 13 | +def drawEdge(edge, verts, ind, mult, ax, scale_max=None, max_thickness=10, |
| 14 | + show_val=False, fs=15, markersize=25): |
| 15 | + colors = ['dodgerblue', 'firebrick', 'limegreen', 'darkorange', 'purple', 'yellow', 'cyan'] |
| 16 | + col1 = colors[int(edge[2])] |
| 17 | + col2 = colors[int(edge[3])] |
| 18 | + |
| 19 | + vert1 = np.array(verts[int(edge[0])]) |
| 20 | + vert2 = np.array(verts[int(edge[1])]) |
| 21 | + if not np.array_equal(vert1, vert2): |
| 22 | + diff = vert1 - vert2 |
| 23 | + rect = [diff[1], -diff[0]] |
| 24 | + rect /= np.linalg.norm(rect) |
| 25 | + hp = (vert1 + vert2) / 2 + (2 * ind - mult + 1) * 0.05 * rect |
| 26 | + else: |
| 27 | + hp = vert1 * 1.2 |
| 28 | + |
| 29 | + if scale_max is None: |
| 30 | + lw = max_thickness |
| 31 | + |
| 32 | + else: |
| 33 | + lw = np.max([abs(max_thickness * edge[4]) / scale_max, 0.5]) |
| 34 | + |
| 35 | + try: |
| 36 | + transparency = 0.2 + abs(edge[4]) * 0.8 |
| 37 | + transparency = min(transparency, 1) |
| 38 | + except IndexError: |
| 39 | + transparency = 1 |
| 40 | + except TypeError: |
| 41 | + transparency = 1 |
| 42 | + |
| 43 | + ax.plot([vert1[0], hp[0]], [vert1[1], hp[1]], color=col1, linewidth=lw, alpha=transparency) |
| 44 | + ax.plot([hp[0], vert2[0]], [hp[1], vert2[1]], col2, linewidth=lw, alpha=transparency) |
| 45 | + |
| 46 | + if show_val: |
| 47 | + |
| 48 | + if transparency > 0.5 and col1 == "blue": |
| 49 | + font_col = 'white' |
| 50 | + else: |
| 51 | + font_col = 'black' |
| 52 | + latex_weight = '${}$'.format(anal.num_in_str(edge[4])) |
| 53 | + if latex_weight == '$$': |
| 54 | + latex_weight = str(edge[4]) |
| 55 | + ax.text(np.mean([0.9 * vert1[0], hp[0]]), np.mean([0.9 * vert1[1], hp[1]]), |
| 56 | + latex_weight, |
| 57 | + bbox={'facecolor': col1, 'alpha': transparency, 'edgecolor': col2, 'pad': 1}, c=font_col, |
| 58 | + ha='center', va='center', rotation=0, fontweight='heavy', fontsize=fs) |
| 59 | + try: |
| 60 | + if edge[4] < 0: |
| 61 | + ax.plot(hp[0], hp[1], marker="d", markersize=markersize, markeredgewidth="3", markeredgecolor="black", |
| 62 | + color="white") |
| 63 | + except: |
| 64 | + pass |
| 65 | + |
| 66 | + |
| 67 | +def graphPlot(graph, scaled_weights=False, show=True, max_thickness=10, |
| 68 | + weight_product=False, ax_fig=(), add_title='', |
| 69 | + show_value_for_each_edge=False, fontsize=30, zorder=11, |
| 70 | + markersize=25, number_nodes=True, filename=''): |
| 71 | + edge_dict = th.edgeBleach(graph.edges) |
| 72 | + |
| 73 | + num_vertices = len(np.unique(np.array(graph.edges)[:, :2])) |
| 74 | + |
| 75 | + angles = np.linspace(0, 2 * np.pi * (num_vertices - 1) / num_vertices, num_vertices) |
| 76 | + |
| 77 | + rad = 0.9 |
| 78 | + vertcoords = [] |
| 79 | + for angle in angles: |
| 80 | + x = rad * np.cos(angle) |
| 81 | + y = rad * np.sin(angle) |
| 82 | + vertcoords.append(tuple([x, y])) |
| 83 | + |
| 84 | + vertnums = list(range(num_vertices)) |
| 85 | + verts = dict(zip(vertnums, vertcoords)) |
| 86 | + |
| 87 | + if scaled_weights: |
| 88 | + try: |
| 89 | + scale_max = np.max(np.abs(np.array(graph.edges)[:, 4])) |
| 90 | + except: |
| 91 | + scale_max = None |
| 92 | + else: |
| 93 | + scale_max = None |
| 94 | + |
| 95 | + if len(ax_fig) == 0: |
| 96 | + fig, ax = plt.subplots(figsize=(10, 10)) |
| 97 | + else: |
| 98 | + fig, ax = ax_fig |
| 99 | + |
| 100 | + for uc_edge in edge_dict.keys(): |
| 101 | + mult = len(edge_dict[uc_edge]) |
| 102 | + for ii, coloring in enumerate(edge_dict[uc_edge]): |
| 103 | + drawEdge(uc_edge + coloring + tuple([graph[tuple(uc_edge + coloring)]]), verts, ii, mult, ax, |
| 104 | + scale_max=scale_max, max_thickness=max_thickness, |
| 105 | + show_val=show_value_for_each_edge, fs=0.8 * fontsize, markersize=markersize) |
| 106 | + |
| 107 | + circ = [] |
| 108 | + if number_nodes: |
| 109 | + node_labels = verts.keys() |
| 110 | + else: |
| 111 | + node_labels = list(map(chr, range(97, 123))) |
| 112 | + for vert, coords in zip(node_labels, verts.values()): |
| 113 | + circ.append(plt.Circle(coords, 0.1, alpha=0.5)) |
| 114 | + ax.text(coords[0], coords[1], str(vert), zorder=zorder, |
| 115 | + ha='center', va='center', size=fontsize) |
| 116 | + |
| 117 | + circ = collections.PatchCollection(circ, zorder=zorder - 1) |
| 118 | + circ.set(facecolor='lightgrey', edgecolor='dimgray', linewidth=3) |
| 119 | + ax.add_collection(circ) |
| 120 | + |
| 121 | + ax.set_xlim([-1.1, 1.1]) |
| 122 | + ax.set_ylim([-1.1, 1.1]) |
| 123 | + ax.axis('off') |
| 124 | + |
| 125 | + if weight_product: |
| 126 | + total_weight = np.product(graph.weights) |
| 127 | + |
| 128 | + wp = '${}$'.format(anal.num_in_str(total_weight)) |
| 129 | + if wp == '$$': |
| 130 | + wp = str(total_weight) |
| 131 | + ax.set_title(wp + str(add_title), fontsize=fontsize) |
| 132 | + |
| 133 | + if add_title != '' and weight_product is False: |
| 134 | + ax.set_title(str(add_title), fontsize=fontsize) |
| 135 | + |
| 136 | + if show: |
| 137 | + plt.show() |
| 138 | + plt.pause(0.01) |
| 139 | + else: |
| 140 | + pass |
| 141 | + if filename: |
| 142 | + fig.savefig(filename + ".pdf") |
| 143 | + |
| 144 | + return fig |
| 145 | + |
| 146 | + |
| 147 | +def leiwandPlot(graph, name='graph'): |
| 148 | + data = [] |
| 149 | + edge_dict = th.edgeBleach(graph.edges) |
| 150 | + for uc_edge in edge_dict.keys(): |
| 151 | + mult = len(edge_dict[uc_edge]) |
| 152 | + loop = (uc_edge[0] == uc_edge[1]) |
| 153 | + for ii, coloring in enumerate(edge_dict[uc_edge]): |
| 154 | + edge = tuple(uc_edge + coloring) |
| 155 | + weight = graph[edge] |
| 156 | + if loop: |
| 157 | + loose = 10 + 5 * ii |
| 158 | + data.append([weight, str(edge[0]), edge[2], str(edge[1]), edge[3], loose]) |
| 159 | + else: |
| 160 | + bend = -22.5 + (ii + 0.5) * 45 / mult |
| 161 | + data.append([weight, str(edge[0]), edge[2], str(edge[1]), edge[3], bend]) |
| 162 | + pytheus.leiwand.leiwand(data, name) |
| 163 | + |
| 164 | + |
| 165 | +def leiwandPlotBulk(graph, cnfg, root, name = 'graph'): |
| 166 | + # if graph is imaginary, just take absolute value as weight for now |
| 167 | + if graph.imaginary: |
| 168 | + graph.absolute() |
| 169 | + data = [] |
| 170 | + edge_dict = th.edgeBleach(graph.edges) |
| 171 | + for uc_edge in edge_dict.keys(): |
| 172 | + mult = len(edge_dict[uc_edge]) |
| 173 | + loop = (uc_edge[0] == uc_edge[1]) |
| 174 | + for ii, coloring in enumerate(edge_dict[uc_edge]): |
| 175 | + edge = tuple(uc_edge + coloring) |
| 176 | + weight = graph[edge] |
| 177 | + if loop: |
| 178 | + loose = 10 + 5 * ii |
| 179 | + data.append([weight, str(edge[0]), edge[2], str(edge[1]), edge[3], loose]) |
| 180 | + else: |
| 181 | + bend = -22.5 + (ii + 0.5) * 45 / mult |
| 182 | + data.append([weight, str(edge[0]), edge[2], str(edge[1]), edge[3], bend]) |
| 183 | + pytheus.leiwand.leiwandBulk(data, cnfg, root=root, name=name) |
| 184 | + |
| 185 | + |
| 186 | +def plotFromFile(filename, number_nodes=True, outfile=""): |
| 187 | + if not os.path.exists(filename) or os.path.isdir(filename): |
| 188 | + raise IOError(f'File does not exist: {filename}') |
| 189 | + with open(filename) as input_file: |
| 190 | + sol_dict = json.load(input_file) |
| 191 | + graph = Graph(sol_dict['graph']) |
| 192 | + graphPlot(graph, scaled_weights=True, number_nodes=number_nodes, filename=outfile) |
0 commit comments