/* * #%L * gitools-core * %% * Copyright (C) 2013 Universitat Pompeu Fabra - Biomedical Genomics group * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ /******************************************************************************* * Copyright 2013 Lars Behnke * * 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.gitools.analysis.clustering.hierarchical; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Multimap; import org.gitools.analysis.clustering.hierarchical.strategy.LinkageStrategy; import org.gitools.api.analysis.IProgressMonitor; import org.gitools.heatmap.header.HierarchicalCluster; import java.util.*; import java.util.concurrent.CancellationException; public class HierarchyBuilder { private SortedSet<ClusterPair> distances; private Multimap<Integer, ClusterPair> distancesMap; private Set<HierarchicalCluster> clusters; public SortedSet<ClusterPair> getDistances() { return distances; } public Set<HierarchicalCluster> getClusters() { return clusters; } public HierarchyBuilder(Set<HierarchicalCluster> clusters, SortedSet<ClusterPair> distances) { this.clusters = clusters; this.distances = distances; this.distancesMap = LinkedListMultimap.create(); for (ClusterPair distance : distances) { this.distancesMap.put(distance.hashCode(), distance); } } public void agglomerate(LinkageStrategy linkageStrategy, IProgressMonitor monitor, int dimensionSize) { int level = 1; int maxLevel = dimensionSize - 1; while (true) { if (isTreeComplete()) { break; } try { ClusterPair minDistLink = distances.first(); distances.remove(minDistLink); clusters.remove(minDistLink.getrCluster()); clusters.remove(minDistLink.getlCluster()); HierarchicalCluster oldClusterL = minDistLink.getlCluster(); HierarchicalCluster oldClusterR = minDistLink.getrCluster(); HierarchicalCluster newCluster = minDistLink.agglomerate(); monitor.begin("Agglomerating level "+ level + " of " + maxLevel + "", clusters.size()); level++; for (HierarchicalCluster iClust : clusters) { monitor.worked(1); if (monitor.isCancelled()) { throw new CancellationException(); } ClusterPair link1 = findByClusters(iClust, oldClusterL); ClusterPair link2 = findByClusters(iClust, oldClusterR); ClusterPair newLinkage = new ClusterPair(); newLinkage.setlCluster(iClust); newLinkage.setrCluster(newCluster); List<Double> distanceValues = new ArrayList<>(); if (link1 != null) { distanceValues.add(link1.getLinkageDistance()); distances.remove(link1); } if (link2 != null) { distanceValues.add(link2.getLinkageDistance()); distances.remove(link2); } if (link1 == null && link2 == null) { for (ClusterPair pair : distances) { if (pair.getlCluster().equals(iClust) || pair.getrCluster().equals(iClust)) { System.out.println(pair); } } } Double newDistance = linkageStrategy .calculateDistance(distanceValues); newLinkage.setLinkageDistance(newDistance); distances.add(newLinkage); distancesMap.put(newLinkage.hashCode(), newLinkage); } clusters.add(newCluster); } catch (NoSuchElementException e) { break; } } } private ClusterPair findByClusters(HierarchicalCluster c1, HierarchicalCluster c2) { int hash1 = c1.hashCode(); int hash2 = c2.hashCode(); int pair1 = 31 * hash1 + hash2; int pair2 = 31 * hash2 + hash1; for (ClusterPair link : distancesMap.get(pair1)) { if (link.getlCluster().equals(c1) && link.getrCluster().equals(c2)) { distancesMap.remove(pair1, link); return link; } } for (ClusterPair link : distancesMap.get(pair2)) { if (link.getlCluster().equals(c2) && link.getrCluster().equals(c1)) { distancesMap.remove(pair2, link); return link; } } return null; } public boolean isTreeComplete() { return clusters.size() == 1; } public HierarchicalCluster getRootCluster() { if (!isTreeComplete()) { throw new RuntimeException("No root available"); } return clusters.iterator().next(); } }