def build(self, *, max_depth: Optional[int] = None) -> 'Search': """ Builds the search tree upto leaves, or an optional maximum depth. This method can be called repeatedly, with higher depth values, to further increase the depth of the tree. :param max_depth: optional maximum depth of search tree :return: the modified Search object. """ if max_depth is None: self.manifold.build(criterion.Layer(-1)) elif max_depth < 1: raise ValueError( f'Expected a positive integer for max_depth. Got {max_depth} instead.' ) elif max_depth > self.depth: self.manifold.build_tree(criterion.MaxDepth(max_depth), criterion.Layer(-1)) return self
def setUpClass(cls) -> None: np.random.seed(42) cls.data, _ = datasets.bullseye(n=1000, num_rings=2) cls.manifold: Manifold = Manifold(cls.data, 'euclidean') cls.manifold.build( criterion.MaxDepth(10), criterion.Layer(5), ) cls.graph: Graph = cls.manifold.graphs[0] return
def test_neighbors(self): for dataset in [ datasets.bullseye, ]: # datasets.spiral_2d, datasets.tori, datasets.skewer, datasets.line]: data, labels = dataset() manifold = Manifold(data, 'euclidean').build( criterion.MaxDepth(12), criterion.Layer(8), ) for cluster in manifold.graphs[0].clusters: potential_neighbors: List[Cluster] = [ c for c in manifold.graphs[0].clusters if c.name != cluster.name ] argcenters: List[int] = [ c.argmedoid for c in potential_neighbors ] distances: List[float] = list( cluster.distance_from(argcenters)) radii: List[float] = [ cluster.radius + c.radius for c in potential_neighbors ] true_neighbors = { c: d for c, d, r in zip(potential_neighbors, distances, radii) if d <= r } neighbors = { edge.neighbor(cluster): edge.distance for edge in manifold.graphs[0].edges_from(cluster) } extras = set(neighbors.keys()) - set(true_neighbors.keys()) self.assertEqual( 0, len(extras), msg= f'got extra neighbors: optimal, true {len(true_neighbors)}, actual {len(neighbors)}\n' + "\n".join([ f"{c.name}, {cluster.radius + c.radius:.6f}" for c in extras ])) missed = set(true_neighbors.keys()) - set(neighbors.keys()) self.assertEqual( 0, len(missed), msg= f'missed some neighbors: optimal, true {len(true_neighbors)}, actual {len(neighbors)}\n' + '\n'.join([ f'{c.name}, {cluster.radius + c.radius:.6f}' for c in missed ])) return
def test_pruned(self): manifold: Manifold = Manifold(self.data, 'euclidean').build(criterion.MaxDepth(10), criterion.Layer(8)) graph = manifold.graphs[0] pruned_graph, subsumed_clusters = graph.pruned_graph self.assertLessEqual(pruned_graph.cardinality, graph.cardinality) self.assertSetEqual(set(pruned_graph.clusters), set(subsumed_clusters.keys())) for cluster, subsumed in subsumed_clusters.items(): self.assertEqual(0, len(subsumed.intersection(set(pruned_graph.clusters))))
def test_dot_file(self): manifold: Manifold = Manifold(self.data, 'euclidean').build(criterion.MinRadius(0.2), criterion.Layer(7)) graph: Graph = manifold.graphs[0] old_clusters: Set[Cluster] = {cluster for cluster in graph.clusters} old_edges: Set[Edge] = {edge for edge in graph.edges} dot_string = manifold.graphs[0].as_dot_string('bullseye_d7') graph = graph.from_dot_string(dot_string) new_clusters: Set[Cluster] = {cluster for cluster in graph.clusters} new_edges: Set[Edge] = {edge for edge in graph.edges} self.assertEqual(old_clusters, new_clusters, f'Found mismatch between old and new clusters.') self.assertEqual(old_edges, new_edges, f'Found mismatch between old and new edges.') return