/*
* Copyright 2004-2010 Information & Software Engineering Group (188/1)
* Institute of Software Technology and Interactive Systems
* Vienna University of Technology, Austria
*
* 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.ifs.tuwien.ac.at/dm/somtoolbox/license.html
*
* 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 at.tuwien.ifs.somtoolbox.visualization.clustering;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.logging.Logger;
import at.tuwien.ifs.somtoolbox.apps.viewer.GeneralUnitPNode;
import at.tuwien.ifs.somtoolbox.layers.metrics.L2Metric;
import at.tuwien.ifs.somtoolbox.layers.metrics.MetricException;
/**
* Single Linkage Clustering Algorithm. This is an adapted version only calculating the distances to the direct
* neighbours. This class is not compatible with mnemonic SOMs (and probably also not compatible with hierarchical SOMs)
*
* @author Angela Roiger
* @version $Id: SingleLinkageTreeBuilder.java 3938 2010-11-17 15:15:25Z mayer $
*/
public class SingleLinkageTreeBuilder extends TreeBuilder {
/**
* Calculation of the Clustering. This code is only compatible with rectangular, non hierarchical SOMs!
*
* @param units the GeneralUnitPNode Array containing all the units of the SOM
* @return the ClusteringTree (i.e. the top node of the tree)
*/
@Override
public ClusteringTree createTree(GeneralUnitPNode[][] units) throws ClusteringAbortedException {
Logger.getLogger("at.tuwien.ifs.somtoolbox").info("Start Clustering ");
this.level = units.length * units[0].length;
// initialize monitor
resetMonitor(2 * level);
TreeSet<NodeDistance> dists = calculateNearestDistances(units);
NodeDistance toMerge;
ClusterNode newNode = null;
while (dists.size() > 0) {
toMerge = dists.first();
// Logger.getLogger("at.tuwien.ifs.somtoolbox").info("Shortest: "+tmpDist.dist);
dists.remove(toMerge);
level--;
incrementMonitor();
allowAborting();
newNode = new ClusterNode(toMerge.n1, toMerge.n2, level, toMerge.dist);
// HashMap duplicateEliminator = new HashMap();
// remove not needed connections and change distances from n1,n2 to newNode
for (Iterator<NodeDistance> i = dists.iterator(); i.hasNext();) {
NodeDistance x = i.next();
if (x.n1 == toMerge.n1 || x.n1 == toMerge.n2) {
x.n1 = newNode;
}
if (x.n2 == toMerge.n1 || x.n2 == toMerge.n2) {
x.n2 = newNode;
}
if (x.n1 == x.n2) {
i.remove();
}
//
// // & keep only the shortest distance for each connection
// if ((x.n1 == newNode) || (x.n2 == newNode)) {
//
// if (x.n1 == newNode) { // make pair where new node is first
// pair = Arrays.asList(new Object[] { x.n1, x.n2 });
// } else {
// pair = Arrays.asList(new Object[] { x.n2, x.n1 });
// }
// // keep only shorter distance
// if (!duplicateEliminator.containsKey(pair)) {
//
// duplicateEliminator.put(pair, x);
// } else {
//
// }
// i.remove();
//
// }
}
// dists.addAll(duplicateEliminator.values());
}
finishMonitor();
Logger.getLogger("at.tuwien.ifs.somtoolbox").info("Finished Clustering - Single Linkage");
return new ClusteringTree(newNode, units.length);
}
/**
* Calculates the distances from each unit to its neighbours to the right, bottom and bottom-right.
*
* @param units A GeneralUnitPNode[][] containing the Units of the som
* @return a TreeSet of NodeDistances containing the distances between the units starting with the smallest.
*/
private TreeSet<NodeDistance> calculateNearestDistances(GeneralUnitPNode[][] units)
throws ClusteringAbortedException {
int xdim = units.length;
int ydim = units[0].length;
// Angela: Pfusch :)
L2Metric l1 = new L2Metric();
ClusterNode[][] tmp = new ClusterNode[xdim][ydim];
TreeSet<NodeDistance> dists = new TreeSet<NodeDistance>();
// create all basic Nodes
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
tmp[i][j] = new ClusterNode(units[i][j], level);
}
}
// calculate initial distances:
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
incrementMonitor();
allowAborting();
try {
if (i < xdim - 1) {
dists.add(new NodeDistance(tmp[i][j], tmp[i + 1][j], l1.distance(
units[i][j].getUnit().getWeightVector(), units[i + 1][j].getUnit().getWeightVector())));
// dists.add(new NodeDistance(tmp[i][j],tmp[i+1][j], l1.distance(
// Normalization.normalizeVectorToUnitLength(units[i][j].getWeightVector()),
// Normalization.normalizeVectorToUnitLength(units[i+1][j].getWeightVector()))) );
// Logger.getLogger("at.tuwien.ifs.somtoolbox").info("Distance: "+ i +","+ j +","+ (i+1) +","+ j
// +":"+l1.distance(units[i][j].getWeightVector(),units[i+1][j].getWeightVector()));
}
if (j < ydim - 1) {
dists.add(new NodeDistance(tmp[i][j], tmp[i][j + 1], l1.distance(
units[i][j].getUnit().getWeightVector(), units[i][j + 1].getUnit().getWeightVector())));
// dists.add(new
// NodeDistance(tmp[i][j],tmp[i][j+1],l1.distance(Normalization.normalizeVectorToUnitLength(units[i][j].getWeightVector()),Normalization.normalizeVectorToUnitLength(units[i][j+1].getWeightVector())))
// );
// Logger.getLogger("at.tuwien.ifs.somtoolbox").info("Distance: "+ i +","+ j +","+ i +","+ (j+1)
// +":"+l1.distance(units[i][j].getWeightVector(),units[i][j+1].getWeightVector()));
if (i < xdim - 1) {
dists.add(new NodeDistance(tmp[i][j], tmp[i + 1][j + 1], l1.distance(
units[i][j].getUnit().getWeightVector(),
units[i + 1][j + 1].getUnit().getWeightVector())));
}
}
if (j > 1 && i < xdim - 1) {
dists.add(new NodeDistance(tmp[i][j], tmp[i + 1][j - 1], l1.distance(
units[i][j].getUnit().getWeightVector(),
units[i + 1][j - 1].getUnit().getWeightVector())));
}
} catch (MetricException e) {
Logger.getLogger("at.tuwien.ifs.somtoolbox").severe("Cannot create clustering: " + e.getMessage());
}
}
}
// // calculate initial distances v2:
// for (int i = 0; i < xdim; i++) {
// for (int j = 0; j < ydim; j++) {
// incrementMonitor();
// allowAborting();
// try {
// for (int k = i; k < xdim; k++) {
// int start = 0; // start at the beginning of the row...
// if (k == i) { // unless it's my own row ...
// start = j + 1; // then start with the next item.
// }
// for (int l = start; l < ydim; l++) {
// dists.add(new NodeDistance(tmp[i][j], tmp[k][l], l1.distance(
// units[i][j].getUnit().getWeightVector(), units[k][l].getUnit().getWeightVector())));
// }
// }
//
// } catch (Exception e) {
// Logger.getLogger("at.tuwien.ifs.somtoolbox").severe("Cannot create clustering: " + e.getMessage());
// e.printStackTrace();
// }
// }
//
// }
return dists;
}
@Override
public String getClusteringAlgName() {
return "SingleLinkage";
}
}