1 /**************************************************************************
2 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
4 * Author: The ALICE Off-line Project. *
5 * Contributors are mentioned in the code where appropriate. *
7 * Permission to use, copy, modify and distribute this software and its *
8 * documentation strictly for non-commercial purposes is hereby granted *
9 * without fee, provided that the above copyright notice appears in all *
10 * copies and that both the copyright notice and this permission notice *
11 * appear in the supporting documentation. The authors make no claims *
12 * about the suitability of this software for any purpose. It is *
13 * provided "as is" without express or implied warranty. *
14 **************************************************************************/
16 // Implemenatation of the K-Means Clustering Algorithm
17 // See http://en.wikipedia.org/wiki/K-means_clustering and references therein.
19 // This particular implementation is the so called Soft K-means algorithm.
20 // It has been modified to work on the cylindrical topology in eta-phi space.
22 // Author: Andreas Morsch (CERN)
23 // andreas.morsch@cern.ch
25 #include "AliKMeansClustering.h"
30 ClassImp(AliKMeansClustering)
32 Double_t AliKMeansClustering::fBeta = 10.;
35 Int_t AliKMeansClustering::SoftKMeans(Int_t k, Int_t n, Double_t* x, Double_t* y, Double_t* mx, Double_t* my , Double_t* rk )
38 // The soft K-means algorithm
42 // (1) Initialisation of the k means
44 for (i = 0; i < k; i++) {
45 mx[i] = 2. * TMath::Pi() * gRandom->Rndm();
46 my[i] = -1. + 2. * gRandom->Rndm();
50 // (2a) The responsibilities
51 Double_t** r = new Double_t*[n]; // responsibilities
52 for (j = 0; j < n; j++) {r[j] = new Double_t[k];}
55 Double_t* nr = new Double_t[n];
64 for (j = 0; j < n; j++) {
66 for (i = 0; i < k; i++) {
67 r[j][i] = TMath::Exp(- fBeta * d(mx[i], my[i], x[j], y[j]));
72 for (j = 0; j < n; j++) {
73 for (i = 0; i < k; i++) {
82 for (i = 0; i < k; i++) {
83 Double_t oldx = mx[i];
84 Double_t oldy = my[i];
90 for (j = 1; j < n; j++) {
93 // Here we have to take into acount the cylinder topology where phi is defined mod 2xpi
94 // If two coordinates are separated by more than pi in phi one has to be shifted by +/- 2 pi
96 Double_t dx = mx[i] - x[j];
97 if (dx > TMath::Pi()) xx += 2. * TMath::Pi();
98 if (dx < -TMath::Pi()) xx -= 2. * TMath::Pi();
99 mx[i] = mx[i] * rk[i] + r[j][i] * xx;
100 my[i] = my[i] * rk[i] + r[j][i] * y[j];
104 if (mx[i] > 2. * TMath::Pi()) mx[i] -= 2. * TMath::Pi();
105 if (mx[i] < 0. ) mx[i] += 2. * TMath::Pi();
107 di += d(mx[i], my[i], oldx, oldy);
111 if (di < 1.e-8 || nit > 1000) break;
116 for (j = 0; j < n; j++) delete[] r[j];
123 Int_t AliKMeansClustering::SoftKMeans2(Int_t k, Int_t n, Double_t* x, Double_t* y, Double_t* mx, Double_t* my , Double_t* sigma2, Double_t* rk )
126 // The soft K-means algorithm
130 // (1) Initialisation of the k means using k-means++ recipe
132 OptimalInit(k, n, x, y, mx, my);
134 // (2a) The responsibilities
135 Double_t** r = new Double_t*[n]; // responsibilities
136 for (j = 0; j < n; j++) {r[j] = new Double_t[k];}
138 // (2b) Normalisation
139 Double_t* nr = new Double_t[n];
142 Double_t* pi = new Double_t[k];
145 // (2d) Initialise the responsibilties and weights
146 for (j = 0; j < n; j++) {
148 for (i = 0; i < k; i++) {
149 r[j][i] = TMath::Exp(- fBeta * d(mx[i], my[i], x[j], y[j]));
154 for (i = 0; i < k; i++) {
156 sigma2[i] = 1./fBeta;
158 for (j = 0; j < n; j++) {
162 pi[i] = rk[i] / Double_t(n);
172 for (j = 0; j < n; j++) {
174 for (i = 0; i < k; i++) {
175 r[j][i] = pi[i] * TMath::Exp(- d(mx[i], my[i], x[j], y[j]) / sigma2[i] )
176 / (2. * sigma2[i] * TMath::Pi() * TMath::Pi());
181 for (i = 0; i < k; i++) {
182 for (j = 0; j < n; j++) {
191 for (i = 0; i < k; i++) {
192 Double_t oldx = mx[i];
193 Double_t oldy = my[i];
198 for (j = 1; j < n; j++) {
201 // Here we have to take into acount the cylinder topology where phi is defined mod 2xpi
202 // If two coordinates are separated by more than pi in phi one has to be shifted by +/- 2 pi
204 Double_t dx = mx[i] - x[j];
205 if (dx > TMath::Pi()) xx += 2. * TMath::Pi();
206 if (dx < -TMath::Pi()) xx -= 2. * TMath::Pi();
207 if (r[j][i] > 1.e-15) {
208 mx[i] = mx[i] * rk[i] + r[j][i] * xx;
209 my[i] = my[i] * rk[i] + r[j][i] * y[j];
214 if (mx[i] > 2. * TMath::Pi()) mx[i] -= 2. * TMath::Pi();
215 if (mx[i] < 0. ) mx[i] += 2. * TMath::Pi();
217 di += d(mx[i], my[i], oldx, oldy);
222 for (i = 0; i < k; i++) {
224 for (j = 0; j < n; j++) {
225 sigma2[i] += r[j][i] * d(mx[i], my[i], x[j], y[j]);
228 if (sigma2[i] < 0.0025) sigma2[i] = 0.0025;
232 for (i = 0; i < k; i++) pi[i] = rk[i] / Double_t(n);
235 if (di < 1.e-8 || nit > 1000) break;
241 for (j = 0; j < n; j++) delete[] r[j];
247 Int_t AliKMeansClustering::SoftKMeans3(Int_t k, Int_t n, Double_t* x, Double_t* y, Double_t* mx, Double_t* my ,
248 Double_t* sigmax2, Double_t* sigmay2, Double_t* rk )
251 // The soft K-means algorithm
255 // (1) Initialisation of the k means using k-means++ recipe
257 OptimalInit(k, n, x, y, mx, my);
259 // (2a) The responsibilities
260 Double_t** r = new Double_t*[n]; // responsibilities
261 for (j = 0; j < n; j++) {r[j] = new Double_t[k];}
263 // (2b) Normalisation
264 Double_t* nr = new Double_t[n];
267 Double_t* pi = new Double_t[k];
270 // (2d) Initialise the responsibilties and weights
271 for (j = 0; j < n; j++) {
273 for (i = 0; i < k; i++) {
275 r[j][i] = TMath::Exp(- fBeta * d(mx[i], my[i], x[j], y[j]));
280 for (i = 0; i < k; i++) {
282 sigmax2[i] = 1./fBeta;
283 sigmay2[i] = 1./fBeta;
285 for (j = 0; j < n; j++) {
289 pi[i] = rk[i] / Double_t(n);
299 for (j = 0; j < n; j++) {
301 for (i = 0; i < k; i++) {
303 Double_t dx = TMath::Abs(mx[i]-x[j]);
304 if (dx > TMath::Pi()) dx = 2. * TMath::Pi() - dx;
305 Double_t dy = TMath::Abs(my[i]-y[j]);
306 r[j][i] = pi[i] * TMath::Exp(-0.5 * (dx * dx / sigmax2[i] + dy * dy / sigmay2[i]))
307 / (2. * TMath::Sqrt(sigmax2[i] * sigmay2[i]) * TMath::Pi() * TMath::Pi());
312 for (i = 0; i < k; i++) {
313 for (j = 0; j < n; j++) {
322 for (i = 0; i < k; i++) {
323 Double_t oldx = mx[i];
324 Double_t oldy = my[i];
329 for (j = 1; j < n; j++) {
332 // Here we have to take into acount the cylinder topology where phi is defined mod 2xpi
333 // If two coordinates are separated by more than pi in phi one has to be shifted by +/- 2 pi
335 Double_t dx = mx[i] - x[j];
336 if (dx > TMath::Pi()) xx += 2. * TMath::Pi();
337 if (dx < -TMath::Pi()) xx -= 2. * TMath::Pi();
338 if (r[j][i] > 1.e-15) {
339 mx[i] = mx[i] * rk[i] + r[j][i] * xx;
340 my[i] = my[i] * rk[i] + r[j][i] * y[j];
345 if (mx[i] > 2. * TMath::Pi()) mx[i] -= 2. * TMath::Pi();
346 if (mx[i] < 0. ) mx[i] += 2. * TMath::Pi();
348 di += d(mx[i], my[i], oldx, oldy);
353 for (i = 0; i < k; i++) {
357 for (j = 0; j < n; j++) {
358 Double_t dx = TMath::Abs(mx[i]-x[j]);
359 if (dx > TMath::Pi()) dx = 2. * TMath::Pi() - dx;
360 Double_t dy = TMath::Abs(my[i]-y[j]);
361 sigmax2[i] += r[j][i] * dx * dx;
362 sigmay2[i] += r[j][i] * dy * dy;
366 if (sigmax2[i] < 0.0025) sigmax2[i] = 0.0025;
367 if (sigmay2[i] < 0.0025) sigmay2[i] = 0.0025;
371 for (i = 0; i < k; i++) pi[i] = rk[i] / Double_t(n);
374 if (di < 1.e-8 || nit > 1000) break;
380 for (j = 0; j < n; j++) delete[] r[j];
386 Double_t AliKMeansClustering::d(Double_t mx, Double_t my, Double_t x, Double_t y)
389 // Distance definition
390 // Quasi - Euclidian on the eta-phi cylinder
392 Double_t dx = TMath::Abs(mx-x);
393 if (dx > TMath::Pi()) dx = 2. * TMath::Pi() - dx;
395 return (0.5*(dx * dx + (my - y) * (my - y)));
400 void AliKMeansClustering::OptimalInit(Int_t k, Int_t n, Double_t* x, Double_t* y, Double_t* mx, Double_t* my)
403 // Optimal initialisation using the k-means++ algorithm
404 // http://en.wikipedia.org/wiki/K-means%2B%2B
406 // k-means++ is an algorithm for choosing the initial values for k-means clustering in statistics and machine learning.
407 // It was proposed in 2007 by David Arthur and Sergei Vassilvitskii as an approximation algorithm for the NP-hard k-means problem---
408 // a way of avoiding the sometimes poor clusterings found by the standard k-means algorithm.
411 TH1F d2("d2", "", n, -0.5, Float_t(n)-0.5);
414 // (1) Chose first center as a random point among the input data.
415 Int_t ir = Int_t(Float_t(n) * gRandom->Rndm());
423 // find min distance to existing clusters
424 for (Int_t j = 0; j < n; j++) {
425 Double_t dmin = 1.e10;
426 for (Int_t i = 0; i < icl; i++) {
427 Double_t dij = d(mx[i], my[i], x[j], y[j]);
428 if (dij < dmin) dmin = dij;
430 d2.Fill(Float_t(j), dmin);
432 // select a new cluster from data points with probability ~d2
433 ir = Int_t(d2.GetRandom() + 0.5);
441 ClassImp(AliKMeansResult)
445 AliKMeansResult::AliKMeansResult(Int_t k):
448 fMx (new Double_t[k]),
449 fMy (new Double_t[k]),
450 fSigma2(new Double_t[k]),
451 fRk (new Double_t[k]),
452 fTarget(new Double_t[k]),
458 AliKMeansResult::AliKMeansResult(const AliKMeansResult &res):
469 for (Int_t i = 0; i <fK; i++) {
470 fMx[i] = (res.GetMx()) [i];
471 fMy[i] = (res.GetMy()) [i];
472 fSigma2[i] = (res.GetSigma2())[i];
473 fRk[i] = (res.GetRk()) [i];
474 fTarget[i] = (res.GetTarget())[i];
475 fInd[i] = (res.GetInd()) [i];
479 AliKMeansResult& AliKMeansResult::operator=(const AliKMeansResult& res)
482 // Assignment operator
485 for (Int_t i = 0; i <fK; i++) {
486 fMx[i] = (res.GetMx()) [i];
487 fMy[i] = (res.GetMy()) [i];
488 fSigma2[i] = (res.GetSigma2())[i];
489 fRk[i] = (res.GetRk()) [i];
490 fTarget[i] = (res.GetTarget())[i];
491 fInd[i] = (res.GetInd()) [i];
498 AliKMeansResult::~AliKMeansResult()
509 void AliKMeansResult::Sort()
511 // Build target array and sort
513 for (Int_t i = 0; i < fK; i++) {
515 fTarget[i] = fRk[i] / fSigma2[i];
517 else fTarget[i] = 0.;
520 TMath::Sort(fK, fTarget, fInd);
523 void AliKMeansResult::Sort(Int_t n, Double_t* x, Double_t* y)
525 // Build target array and sort
526 for (Int_t i = 0; i < fK; i++)
529 for (Int_t j = 0; j < n; j++)
531 if (2. * AliKMeansClustering::d(fMx[i], fMy[i], x[j], y[j]) < 2.28 * fSigma2[i]) nc++;
535 fTarget[i] = Double_t(nc) / (2.28 * fSigma2[i]);
541 TMath::Sort(fK, fTarget, fInd);
544 void AliKMeansResult::CopyResults(AliKMeansResult* res)
547 for (Int_t i = 0; i <fK; i++) {
548 fMx[i] = (res->GetMx()) [i];
549 fMy[i] = (res->GetMy()) [i];
550 fSigma2[i] = (res->GetSigma2())[i];
551 fRk[i] = (res->GetRk()) [i];
552 fTarget[i] = (res->GetTarget())[i];
553 fInd[i] = (res->GetInd()) [i];