@Test
/**
 * Tests Bayesian Gaussian Mixture Model on a three well separated clusters.
 */
public void bivariateWellSeparatedNormalTest() throws Exception {

    MacroBaseConf conf = new MacroBaseConf()
            .set(MacroBaseConf.RANDOM_SEED, 44)
            .set(MacroBaseConf.TRANSFORM_TYPE, "MEAN_FIELD_GMM")
            .set(GMMConf.NUM_MIXTURES, 3)
            .set(GMMConf.MIXTURE_CENTERS_FILE, "src/test/resources/data/3gaussians-700.points-centers.json")
            .set(MacroBaseConf.DATA_LOADER_TYPE, "CSV_LOADER")
            .set(MacroBaseConf.CSV_COMPRESSION, CSVIngester.Compression.GZIP)
            .set(MacroBaseConf.CSV_INPUT_FILE, "src/test/resources/data/3gaussians-700points.csv.gz")
            .set(MacroBaseConf.METRICS, "XX, YY")
            .set(MacroBaseConf.ATTRIBUTES, "");
    List<Datum> data = Drainer.drainIngest(conf);
    assertEquals(700, data.size());
    FiniteGMM finiteGMM = new FiniteGMM(conf);
    List<RealVector> calculatedMeans;
    // Make sure we have 3 clusters. Sometimes initialization is not great.
    finiteGMM.train(data);
    calculatedMeans = finiteGMM.getClusterCenters();
    List<RealMatrix> calculatedCovariances = finiteGMM.getClusterCovariances();
    double[][] clusterMeans = {
            {2, 11},
            {1, 1},
            {10, 3},
    };
    List<RealVector> vectorClusterMeans = new ArrayList<>(3);
    for (int k = 0; k < 3; k++) {
        vectorClusterMeans.add(new ArrayRealVector(clusterMeans[k]));
    }
    double[][][] clusterCovariances = {
            {{0.5, 0.4}, {0.4, 0.5}},
            {{0.3, 0}, {0, 0.6}},
            {{0.9, 0.2}, {0.2, 0.3}},
    };
    ExpectMaxGMM gmm = new ExpectMaxGMM(conf);
    gmm.train(data);
    List<RealVector> emMeans = gmm.getClusterCenters();
    List<RealMatrix> emCovariances = gmm.getClusterCovariances();
    for (int i = 0; i < 3; i++) {
        boolean identified = false;
        for (int j = 0; j < 3; j++) {
            if (calculatedMeans.get(i).getDistance(vectorClusterMeans.get(j)) < 0.1) {
                for (int p = 0; p < 2; p++) {
                    for (int q = 0; q < 2; q++) {
                        // Make sure covariance is in the ballpark. Since we only had 700 points.
                        assertEquals(clusterCovariances[j][p][q], calculatedCovariances.get(i).getEntry(p, q), 0.6);
                    }
                }
                identified = true;
                break;
            }
        }
        assertEquals(true, identified);
        for (int z = 0; z < 3; z++) {
            if (emMeans.get(z).getDistance(calculatedMeans.get(i)) < 0.1) {
                for (int p = 0; p < 2; p++) {
                    for (int q = 0; q < 2; q++) {
                        // Make sure we have a closer estimate to EM algorithm means
                        assertEquals(emCovariances.get(z).getEntry(p, q), calculatedCovariances.get(i).getEntry(p, q), 0.1);
                    }
                }
                break;
            }
        }
    }
    // Make sure centers belong to only one cluster.
    double[] maxProbas = {0, 0, 0};
    for (int i = 0; i < 3; i++) {
        double[] probas = finiteGMM.getClusterProbabilities(new Datum(new ArrayList<Integer>(), vectorClusterMeans.get(i)));
        for (int j=0; j< 3; j++) {
            maxProbas[j] = Math.max(probas[j], maxProbas[j]);
        }
    }
    for (int j=0; j< 3; j++) {
        assertEquals(maxProbas[j], 1, 0.01);
    }
}