import x10.io.Console;
import x10.util.Random;
/**
* A KMeans object o can compute K means of a given set of
* points of dimension o.myDim.
* <p>
* This class implements a sequential program, that is readily parallelizable.
*
* @author cunningham
* @author vj
*/
public class KMeans(myDim:Int) {
const DIM=2, K=4, POINTS=2000, ITERATIONS=50;
const EPS=0.01F;
static type ValVector(k:Int) = ValRail[Float](k);
static type ValVector = ValVector(DIM);
static type Vector(k:Int) = Rail[Float](k);
static type Vector = Vector(DIM);
static type SumVector(d:Int) = V{self.dim==d};
static type SumVector = SumVector(DIM);
/**
* V represents the sum of 'count' number of vectors of dimension 'dim'.
*
*/
static class V(dim:Int) implements (Int)=>Float {
var vec: Vector(dim);
var count:Int;
def this(dim:Int, init:(Int)=>Float): SumVector(dim) {
property(dim);
vec = Rail.makeVar[Float](this.dim, init);
count = 0;
}
public def apply(i:Int) = vec(i);
def makeZero() {
for ((i) in 0..dim-1)
vec(i) =0.0F;
count=0;
}
def addIn(a:ValVector(dim)) {
for ((i) in 0..dim-1)
vec(i) += a(i);
count++;
}
def div(f:Int) {
for ((i) in 0..dim-1)
vec(i) /= f;
}
def dist(a:ValVector(dim)):Float {
var dist:Float=0.0F;
for ((i) in 0..dim-1) {
val tmp = vec(i)-a(i);
dist += tmp*tmp;
}
return dist;
}
def dist(a:SumVector(dim)):Float {
var dist:Float=0.0F;
for ((i) in 0..dim-1) {
val tmp = vec(i)-a(i);
dist += tmp*tmp;
}
return dist;
}
def print() {
Console.OUT.println();
for ((i) in 0..dim-1) {
Console.OUT.print((i>0? " " : "") + vec(i));
}
}
def normalize() { div(count);}
def count() = count;
}
def this(myDim:Int):KMeans{self.myDim==myDim} {
property(myDim);
}
static type KMeansData(myK:Int, myDim:Int)= Rail[SumVector(myDim)](myK);
/**
* Compute myK means for the given set of points of dimension myDim.
*/
def computeMeans(myK:Int, points: ValRail[ValVector(myDim)]): KMeansData(myK, myDim) {
var redCluster : KMeansData(myK, myDim) =
Rail.makeVar[SumVector(myDim)](myK, (i:Int)=> new V(myDim, (j:Int)=>points(i)(j)));
var blackCluster: KMeansData(myK, myDim) =
Rail.makeVar[SumVector(myDim)](myK, (i:Int)=> new V(myDim, (j:Int)=>0.0F));
for ((i) in 1..ITERATIONS) {
val tmp = redCluster;
redCluster = blackCluster;
blackCluster=tmp;
for ((p) in 0..POINTS-1) {
var closest:Int = -1;
var closestDist:Float = Float.MAX_VALUE;
val point = points(p);
for ((k) in 0..myK-1) { // compute closest mean in cluster.
val dist = blackCluster(k).dist(point);
if (dist < closestDist) {
closestDist = dist;
closest = k;
}
}
redCluster(closest).addIn(point);
}
for ((k) in 0..myK-1)
redCluster(k).normalize();
var b:Boolean = true;
for ((k) in 0..myK-1) {
if (redCluster(k).dist(blackCluster(k)) > EPS) {
b=false;
break;
}
}
if (b)
break;
for ((k) in 0..myK-1)
blackCluster(k).makeZero();
}
return redCluster;
}
public static def main (args : Rail[String]) {
val rnd = new Random(0);
val points = Rail.makeVal[ValVector](POINTS,
(Int)=>Rail.makeVal[Float](DIM, (Int)=>rnd.nextFloat()));
val result = new KMeans(DIM).computeMeans(K, points);
for ((k) in 0..K-1) result(k).print();
}
}
// vim: shiftwidth=4:tabstop=4:expandtab