# Path: treecat/structure.py
# @profile
# def estimate_tree(grid, edge_logits):
#     """Compute a maximum likelihood spanning tree of a dense weighted graph.
# 
#     Args:
#         grid: A 3 x K array as returned by make_complete_graph().
#         edge_logits: A length-K array of nonnormalized log probabilities.
# 
#     Returns:
#         A list of (vertex, vertex) pairs.
#     """
#     K = len(edge_logits)
#     assert grid.shape == (3, K)
#     weights = triangular_to_square(grid, edge_logits)
#     weights *= -1
#     weights -= weights.min()
#     weights += 1.0
#     csr = minimum_spanning_tree(weights, overwrite=True)
#     coo = csr.tocoo()
#     edges = zip(coo.row, coo.col)
#     edges = sorted(tuple(sorted(pair)) for pair in edges)
#     assert len(edges) == weights.shape[0] - 1
#     return edges
# 
# Path: treecat/structure.py
# def make_complete_graph(num_vertices):
#     """Constructs a complete graph.
# 
#     The pairing function is: k = v1 + v2 * (v2 - 1) // 2
# 
#     Args:
#         num_vertices: Number of vertices.
# 
#     Returns: A tuple with elements:
#         V: Number of vertices.
#         K: Number of edges.
#         grid: a 3 x K grid of (edge, vertex, vertex) triples.
#     """
#     V = num_vertices
#     K = V * (V - 1) // 2
#     grid = np.zeros([3, K], np.int32)
#     k = 0
#     for v2 in range(V):
#         for v1 in range(v2):
#             grid[:, k] = [k, v1, v2]
#             k += 1
#     return grid
# 
# Path: treecat/structure.py
# def order_vertices(edges):
#     """Sort vertices using greedy bin packing.
# 
#     This is mainly useful for plotting and printing.
# 
#     Args:
#         edges: A list of (vertex, vertex) pairs.
# 
#     Returns:
#         An (permutation, inverse) pair, each a list of vertices.
#     """
#     grid = make_tree(edges)
#     root = find_center_of_tree(grid)
# 
#     E = len(edges)
#     V = E + 1
#     neighbors = [set() for _ in range(V)]
#     for v1, v2 in edges:
#         neighbors[v1].add(v2)
#         neighbors[v2].add(v1)
#     orders = [None for v in range(V)]
#     seen = [False] * V
#     stack = [root]
#     seen[root] = True
#     while stack:
#         v = stack[-1]
#         done = True
#         for v2 in neighbors[v]:
#             if not seen[v2]:
#                 stack.append(v2)
#                 seen[v2] = True
#                 done = False
#                 break
#         if not done:
#             continue
#         lhs = []
#         rhs = []
#         parts = [(v2, orders[v2]) for v2 in neighbors[v]
#                  if orders[v2] is not None]
#         parts.sort(key=lambda x: len(x[1]))
#         for v2, part in parts:
#             midpoint = (len(part) - 1) / 2
#             if len(lhs) < len(rhs):
#                 if part.index(v2) < midpoint:
#                     part.reverse()
#                 lhs = part + lhs
#             else:
#                 if part.index(v2) > midpoint:
#                     part.reverse()
#                 rhs = rhs + part
#         orders[v] = lhs + [v] + rhs
#         stack.pop()
# 
#     order_inv = orders[root]
#     order = [None] * V
#     for v1, v2 in enumerate(order_inv):
#         order[v2] = v1
#     return order, order_inv


import io
import os
import re
import numpy as np
import scipy.cluster
import scipy.linalg
import scipy.spatial
    import networkx as nx
from collections import Counter
from matplotlib import pyplot
from matplotlib.patches import PathPatch
from matplotlib.path import Path
from treecat.structure import estimate_tree
from treecat.structure import make_complete_graph
from treecat.structure import order_vertices
    # Adapted from https://matplotlib.org/users/path_tutorial.html
    codes = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4]
    xy = np.array([begin, begin, end, end])
    dist = ((begin - end)**2).sum()**0.5
    xy[[1, 2], :] *= 1 - 2 / 3 * dist + 1 / 6 * dist**2 - spacing
    path = Path(xy, codes)
    patch = PathPatch(
        path, facecolor='none', edgecolor=color, lw=1, alpha=alpha)
    pyplot.gca().add_patch(patch)


def plot_circular(server,
                  tree_samples=0,
                  fontsize=8,
                  color='#4488aa',
                  contract=0.08):
    """Plot a tree stucture with features arranged around a circle.

    Args:
        server: A DataServer instance.
        tree_samples: Number of trees to sample, in addition to the mode.
        fontsize: The font size for labels, in points.
        color: A matplotlib color spec for edge colors.
        contract: Contract vertex positions by this amount to visually hint
            their grouping.

    Requires:
        matplotlib.
    """
    # Extract ordered parameters to draw.

# Path: treecat/structure.py
# @profile
# def estimate_tree(grid, edge_logits):
#     """Compute a maximum likelihood spanning tree of a dense weighted graph.
# 
#     Args:
#         grid: A 3 x K array as returned by make_complete_graph().
#         edge_logits: A length-K array of nonnormalized log probabilities.
# 
#     Returns:
#         A list of (vertex, vertex) pairs.
#     """
#     K = len(edge_logits)
#     assert grid.shape == (3, K)
#     weights = triangular_to_square(grid, edge_logits)
#     weights *= -1
#     weights -= weights.min()
#     weights += 1.0
#     csr = minimum_spanning_tree(weights, overwrite=True)
#     coo = csr.tocoo()
#     edges = zip(coo.row, coo.col)
#     edges = sorted(tuple(sorted(pair)) for pair in edges)
#     assert len(edges) == weights.shape[0] - 1
#     return edges
# 
# Path: treecat/structure.py
# def make_complete_graph(num_vertices):
#     """Constructs a complete graph.
# 
#     The pairing function is: k = v1 + v2 * (v2 - 1) // 2
# 
#     Args:
#         num_vertices: Number of vertices.
# 
#     Returns: A tuple with elements:
#         V: Number of vertices.
#         K: Number of edges.
#         grid: a 3 x K grid of (edge, vertex, vertex) triples.
#     """
#     V = num_vertices
#     K = V * (V - 1) // 2
#     grid = np.zeros([3, K], np.int32)
#     k = 0
#     for v2 in range(V):
#         for v1 in range(v2):
#             grid[:, k] = [k, v1, v2]
#             k += 1
#     return grid
# 
# Path: treecat/structure.py
# def order_vertices(edges):
#     """Sort vertices using greedy bin packing.
# 
#     This is mainly useful for plotting and printing.
# 
#     Args:
#         edges: A list of (vertex, vertex) pairs.
# 
#     Returns:
#         An (permutation, inverse) pair, each a list of vertices.
#     """
#     grid = make_tree(edges)
#     root = find_center_of_tree(grid)
# 
#     E = len(edges)
#     V = E + 1
#     neighbors = [set() for _ in range(V)]
#     for v1, v2 in edges:
#         neighbors[v1].add(v2)
#         neighbors[v2].add(v1)
#     orders = [None for v in range(V)]
#     seen = [False] * V
#     stack = [root]
#     seen[root] = True
#     while stack:
#         v = stack[-1]
#         done = True
#         for v2 in neighbors[v]:
#             if not seen[v2]:
#                 stack.append(v2)
#                 seen[v2] = True
#                 done = False
#                 break
#         if not done:
#             continue
#         lhs = []
#         rhs = []
#         parts = [(v2, orders[v2]) for v2 in neighbors[v]
#                  if orders[v2] is not None]
#         parts.sort(key=lambda x: len(x[1]))
#         for v2, part in parts:
#             midpoint = (len(part) - 1) / 2
#             if len(lhs) < len(rhs):
#                 if part.index(v2) < midpoint:
#                     part.reverse()
#                 lhs = part + lhs
#             else:
#                 if part.index(v2) > midpoint:
#                     part.reverse()
#                 rhs = rhs + part
#         orders[v] = lhs + [v] + rhs
#         stack.pop()
# 
#     order_inv = orders[root]
#     order = [None] * V
#     for v1, v2 in enumerate(order_inv):
#         order[v2] = v1
#     return order, order_inv


# Path: treecat/plotting.py
import io
import os
import re
import numpy as np
import scipy.cluster
import scipy.linalg
import scipy.spatial
    import networkx as nx
from collections import Counter
from matplotlib import pyplot
from matplotlib.patches import PathPatch
from matplotlib.path import Path
from treecat.structure import estimate_tree
from treecat.structure import make_complete_graph
from treecat.structure import order_vertices

    # Adapted from https://matplotlib.org/users/path_tutorial.html
    codes = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4]
    xy = np.array([begin, begin, end, end])
    dist = ((begin - end)**2).sum()**0.5
    xy[[1, 2], :] *= 1 - 2 / 3 * dist + 1 / 6 * dist**2 - spacing
    path = Path(xy, codes)
    patch = PathPatch(
        path, facecolor='none', edgecolor=color, lw=1, alpha=alpha)
    pyplot.gca().add_patch(patch)


def plot_circular(server,
                  tree_samples=0,
                  fontsize=8,
                  color='#4488aa',
                  contract=0.08):
    """Plot a tree stucture with features arranged around a circle.

    Args:
        server: A DataServer instance.
        tree_samples: Number of trees to sample, in addition to the mode.
        fontsize: The font size for labels, in points.
        color: A matplotlib color spec for edge colors.
        contract: Contract vertex positions by this amount to visually hint
            their grouping.

    Requires:
        matplotlib.
    """
    # Extract ordered parameters to draw.

    edges = server.estimate_tree()