/*
* Copyright 2007 Tom Gibara
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.jgrasstools.gears.utils.clustering;
/**
* A cluster of points.
*
* @author Tom Gibara
*
* @param <K> the key type
*/
public class GvmCluster<S extends GvmSpace, K> {
/**
* The set of clusters to which this cluster belongs
*/
final GvmClusters<S, K> clusters;
/**
* The pairings of this cluster with all other clusters.
*/
final GvmClusterPair<S,K>[] pairs;
/**
* Whether this cluster is in the process of being removed.
*/
boolean removed;
/**
* The number of points in this cluster.
*/
int count;
/**
* The total mass of this cluster.
*/
double m0;
/**
* The mass-weighted coordinate sum.
*/
final Object m1;
/**
* The mass-weighted coordinate-square sum.
*/
final Object m2;
/**
* The computed variance of this cluster
*/
double var;
/**
* The key associated with this cluster.
*/
K key;
// constructors
@SuppressWarnings("unchecked")
GvmCluster(GvmClusters<S,K> clusters) {
this.clusters = clusters;
removed = false;
count = 0;
m0 = 0.0;
m1 = clusters.space.newOrigin();
m2 = clusters.space.newOrigin();
pairs = new GvmClusterPair[clusters.capacity];
update();
}
// public accessors
/**
* The total mass of the cluster.
*/
public double getMass() {
return m0;
}
/**
* The number of points in the cluster.
*/
public int getCount() {
return count;
}
/**
* The computed variance of the cluster
*/
public double getVariance() {
return var;
}
/**
* The key associated with the cluster, may be null.
*/
public K getKey() {
return key;
}
// package methods
/**
* Completely clears this cluster. All points and their associated mass is
* removed along with any key that was assigned to the cluster,
*/
void clear() {
count = 0;
m0 = 0.0;
clusters.space.setToOrigin(m1);
clusters.space.setToOrigin(m2);
var = 0.0;
key = null;
}
/**
* Sets this cluster equal to a single point.
*
* @param m
* the mass of the point
* @param pt
* the coordinates of the point
*/
void set(final double m, final Object pt) {
if (m == 0.0) {
if (count != 0) {
clusters.space.setToOrigin(m1);
clusters.space.setToOrigin(m2);
}
} else {
clusters.space.setToScaled(m1, m, pt);
clusters.space.setToScaledSqr(m2, m, pt);
}
count = 1;
m0 = m;
var = 0.0;
}
/**
* Adds a point to the cluster.
*
* @param m
* the mass of the point
* @param pt
* the coordinates of the point
*/
void add(final double m, final Object pt) {
if (count == 0) {
set(m, pt);
} else {
count += 1;
if (m != 0.0) {
m0 += m;
clusters.space.addScaled(m1, m, pt);
clusters.space.addScaledSqr(m2, m, pt);
update();
}
}
}
/**
* Sets this cluster equal to the specified cluster
*
* @param cluster a cluster, not this or null
*/
void set(GvmCluster<S,K> cluster) {
if (cluster == this) throw new IllegalArgumentException("cannot set cluster to itself");
m0 = cluster.m0;
clusters.space.setTo(m1, cluster.m1);
clusters.space.setTo(m2, cluster.m2);
var = cluster.var;
}
/**
* Adds the specified cluster to this cluster.
*
* @param cluster the cluster to be added
*/
void add(GvmCluster<S,K> cluster) {
if (cluster == this) throw new IllegalArgumentException();
if (cluster.count == 0) return; //nothing to do
if (count == 0) {
set(cluster);
} else {
count += cluster.count;
//TODO accelerate add
m0 += cluster.m0;
clusters.space.add(m1, cluster.m1);
clusters.space.add(m2, cluster.m2);
update();
}
}
/**
* Computes this clusters variance if it were to have a new point added to it.
*
* @param m the mass of the point
* @param pt the coordinates of the point
* @return the variance of this cluster inclusive of the point
*/
double test(double m, Object pt) {
return m0 == 0.0 && m == 0.0 ? 0.0 : clusters.space.variance(m0, m1, m2, m, pt) - var;
}
/**
* Computes the variance of a cluster that aggregated this cluster with the
* supplied cluster.
*
* @param cluster
* another cluster
* @return the combined variance
*/
//TODO: change for consistency with other test method : return increase in variance
double test(GvmCluster<S,K> cluster) {
return m0 == 0.0 && cluster.m0 == 0.0 ? 0.0 : clusters.space.variance(m0, m1, m2, cluster.m0, cluster.m1, cluster.m2);
}
// private utility methods
/**
* Recompute this cluster's variance.
*/
private void update() {
var = m0 == 0.0 ? 0.0 : clusters.space.variance(m0, m1, m2);
}
}