< Summary

Class:AgglomerativeClusterer
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/Algorithms/Clustering/AgglomerativeClusterer.cs
Covered lines:59
Uncovered lines:0
Coverable lines:59
Total lines:97
Line coverage:100% (59 of 59)
Covered branches:0
Total branches:0
Covered methods:2
Total methods:2
Method coverage:100% (2 of 2)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
AgglomerativeClusterer(...)0%110100%
Cluster(...)0%21210100%

File(s)

/github/workspace/Assets/Scripts/Algorithms/Clustering/AgglomerativeClusterer.cs

#LineLine coverage
 1using System.Collections.Generic;
 2using System.Linq;
 3using UnityEngine;
 4
 5// The agglomerative clusterer performs agglomerative clustering with the stopping condition given
 6// by the size and radius constraints.
 7public class AgglomerativeClusterer : SizeAndRadiusConstrainedClustererBase {
 218  public AgglomerativeClusterer(int maxSize, float maxRadius) : base(maxSize, maxRadius) {}
 9
 10  // Generate the clusters from the list of hierarchical objects.
 711  public override List<Cluster> Cluster(IEnumerable<IHierarchical> hierarchicals) {
 712    var clusters = new List<Cluster>();
 913    if (hierarchicals == null || !hierarchicals.Any()) {
 214      return clusters;
 15    }
 16
 7517    foreach (var hierarchical in hierarchicals) {
 2018      var cluster = new Cluster { Centroid = hierarchical.Position };
 2019      cluster.AddSubHierarchical(hierarchical);
 2020      clusters.Add(cluster);
 2021    }
 22
 23    // Create a set containing all valid cluster indices.
 524    var validClusterIndices = new HashSet<int>();
 7025    for (int i = 0; i < clusters.Count; ++i) {
 2026      validClusterIndices.Add(i);
 2027    }
 28
 29    // Find the pairwise distances between all clusters.
 30    // Only the lower triangular part of the distances matrix is used, i.e., row index > column
 31    // index.
 532    var distances = new float[clusters.Count, clusters.Count];
 7033    for (int i = 0; i < clusters.Count; ++i) {
 13034      for (int j = 0; j < i; ++j) {
 3035        distances[i, j] = Vector3.Distance(clusters[i].Centroid, clusters[j].Centroid);
 3036      }
 2037    }
 38
 3239    while (true) {
 40      // Find the minimum distance between two clusters.
 1641      float minDistance = Mathf.Infinity;
 1642      int clusterIndex1 = -1;
 1643      int clusterIndex2 = -1;
 44      // Invariant: clusterIndex1 > clusterIndex2.
 21645      foreach (int i in validClusterIndices) {
 35546        for (int j = 0; j < i; ++j) {
 10247          if (validClusterIndices.Contains(j) && distances[i, j] < minDistance) {
 2148            minDistance = distances[i, j];
 2149            clusterIndex1 = i;
 2150            clusterIndex2 = j;
 2151          }
 8152        }
 5653      }
 54
 55      // Check whether the minimum distance exceeds the maximum cluster radius, in which case the
 56      // algorithm has converged. This produces a conservative solution because the radius of a
 57      // merged cluster is less than or equal to the sum of the original cluster radii.
 2158      if (minDistance == Mathf.Infinity || minDistance > _maxRadius) {
 559        break;
 60      }
 61
 62      // Check whether merging the two clusters would violate the size constraint.
 1763      if (clusters[clusterIndex1].Size + clusters[clusterIndex2].Size > _maxSize) {
 664        distances[clusterIndex1, clusterIndex2] = Mathf.Infinity;
 665        continue;
 66      }
 67
 68      // Merge the two clusters together and keep the cluster with the smaller index, i.e.,
 69      // clusterIndex2.
 570      clusters[clusterIndex2].Merge(clusters[clusterIndex1]);
 571      clusters[clusterIndex2].Recenter();
 572      validClusterIndices.Remove(clusterIndex1);
 73
 74      // Update the distances matrix using the distance between the cluster centroids.
 75      // TODO(titan): Change the distance metric to use average or maximum linkage.
 5176      foreach (int i in validClusterIndices) {
 1777        if (i == clusterIndex2) {
 578          continue;
 79        }
 780        float distance = Vector3.Distance(clusters[clusterIndex2].Centroid, clusters[i].Centroid);
 1081        if (i < clusterIndex2) {
 382          distances[clusterIndex2, i] = distance;
 1183        } else if (i > clusterIndex2) {
 484          distances[i, clusterIndex2] = distance;
 485        }
 786      }
 587    }
 88
 89    // Select only the valid clusters.
 7090    for (int i = clusters.Count - 1; i >= 0; --i) {
 2591      if (!validClusterIndices.Contains(i)) {
 592        clusters.RemoveAt(i);
 593      }
 2094    }
 595    return clusters;
 796  }
 97}