1 // ****************************************************************************** 2 // 3 // Title: Force Field X. 4 // Description: Force Field X - Software for Molecular Biophysics. 5 // Copyright: Copyright (c) Michael J. Schnieders 2001-2025. 6 // 7 // This file is part of Force Field X. 8 // 9 // Force Field X is free software; you can redistribute it and/or modify it 10 // under the terms of the GNU General Public License version 3 as published by 11 // the Free Software Foundation. 12 // 13 // Force Field X is distributed in the hope that it will be useful, but WITHOUT 14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 // details. 17 // 18 // You should have received a copy of the GNU General Public License along with 19 // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple 20 // Place, Suite 330, Boston, MA 02111-1307 USA 21 // 22 // Linking this library statically or dynamically with other modules is making a 23 // combined work based on this library. Thus, the terms and conditions of the 24 // GNU General Public License cover the whole combination. 25 // 26 // As a special exception, the copyright holders of this library give you 27 // permission to link this library with independent modules to produce an 28 // executable, regardless of the license terms of these independent modules, and 29 // to copy and distribute the resulting executable under terms of your choice, 30 // provided that you also meet, for each linked independent module, the terms 31 // and conditions of the license of that module. An independent module is a 32 // module which is not derived from or based on this library. If you modify this 33 // library, you may extend this exception to your version of the library, but 34 // you are not obligated to do so. If you do not wish to do so, delete this 35 // exception statement from your version. 36 // 37 // ****************************************************************************** 38 package ffx.numerics.clustering; 39 40 /** 41 * Immutable-like holder describing a pair of clusters and the linkage distance 42 * between them; used as entries within the DistanceMap during agglomeration. 43 * 44 * @author Lars Behnke, 2013 45 * @author Michael J. Schnieders 46 * @since 1.0 47 */ 48 public class ClusterPair implements Comparable<ClusterPair> { 49 50 private Cluster lCluster; 51 private Cluster rCluster; 52 private Double linkageDistance; 53 54 /** 55 * Creates an empty ClusterPair. 56 */ 57 public ClusterPair() { 58 } 59 60 /** 61 * Creates a ClusterPair linking two clusters at a given distance. 62 * 63 * @param left the left cluster 64 * @param right the right cluster 65 * @param distance the linkage distance between clusters 66 */ 67 public ClusterPair(Cluster left, Cluster right, Double distance) { 68 lCluster = left; 69 rCluster = right; 70 linkageDistance = distance; 71 } 72 73 /** 74 * Returns the opposite cluster of the provided one in this pair. 75 * 76 * @param c one cluster in this pair 77 * @return the other cluster in the pair 78 */ 79 public Cluster getOtherCluster(Cluster c) { 80 return lCluster == c ? rCluster : lCluster; 81 } 82 83 /** 84 * Gets the left cluster. 85 * 86 * @return the left cluster 87 */ 88 public Cluster getlCluster() { 89 return lCluster; 90 } 91 92 /** 93 * Sets the left cluster. 94 * 95 * @param lCluster the left cluster 96 */ 97 public void setlCluster(Cluster lCluster) { 98 this.lCluster = lCluster; 99 } 100 101 /** 102 * Gets the right cluster. 103 * 104 * @return the right cluster 105 */ 106 public Cluster getrCluster() { 107 return rCluster; 108 } 109 110 /** 111 * Sets the right cluster. 112 * 113 * @param rCluster the right cluster 114 */ 115 public void setrCluster(Cluster rCluster) { 116 this.rCluster = rCluster; 117 } 118 119 /** 120 * Gets the linkage distance. 121 * 122 * @return the linkage distance 123 */ 124 public Double getLinkageDistance() { 125 return linkageDistance; 126 } 127 128 /** 129 * Sets the linkage distance. 130 * 131 * @param distance the linkage distance to set 132 */ 133 public void setLinkageDistance(Double distance) { 134 this.linkageDistance = distance; 135 } 136 137 /** 138 * Creates a new ClusterPair with left and right clusters swapped. 139 * 140 * @return a new ClusterPair with the two left/right inverted 141 */ 142 public ClusterPair reverse() { 143 return new ClusterPair(getrCluster(), getlCluster(), getLinkageDistance()); 144 } 145 146 147 /** 148 * {@inheritDoc} 149 */ 150 @Override 151 public int compareTo(ClusterPair o) { 152 int result; 153 if (o == null || o.getLinkageDistance() == null) { 154 result = -1; 155 } else if (getLinkageDistance() == null) { 156 result = 1; 157 } else { 158 result = getLinkageDistance().compareTo(o.getLinkageDistance()); 159 } 160 161 return result; 162 } 163 164 /** 165 * Agglomerates left and right clusters under an auto-generated name. 166 * 167 * @param clusterIdx index appended to the generated cluster name 168 * @return the new parent Cluster 169 */ 170 public Cluster agglomerate(int clusterIdx) { 171 return agglomerate("clstr#" + clusterIdx); 172 } 173 174 /** 175 * Agglomerates left and right clusters into a new parent with the given name. 176 * 177 * @param name name of the new parent cluster 178 * @return the new parent Cluster 179 */ 180 public Cluster agglomerate(String name) { 181 Cluster cluster = new Cluster(name); 182 cluster.setDistance(new Distance(getLinkageDistance())); 183 //New clusters will track their children's leaf names; i.e. each cluster knows what part of the original data it contains 184 cluster.appendLeafNames(lCluster.getLeafNames()); 185 cluster.appendLeafNames(rCluster.getLeafNames()); 186 cluster.addChild(lCluster); 187 cluster.addChild(rCluster); 188 lCluster.setParent(cluster); 189 rCluster.setParent(cluster); 190 191 Double lWeight = lCluster.getWeightValue(); 192 Double rWeight = rCluster.getWeightValue(); 193 double weight = lWeight + rWeight; 194 cluster.getDistance().setWeight(weight); 195 196 return cluster; 197 } 198 199 /** 200 * {@inheritDoc} 201 */ 202 @Override 203 public String toString() { 204 StringBuilder sb = new StringBuilder(); 205 if (lCluster != null) { 206 sb.append(lCluster.getName()); 207 } 208 if (rCluster != null) { 209 if (!sb.isEmpty()) { 210 sb.append(" + "); 211 } 212 sb.append(rCluster.getName()); 213 } 214 sb.append(" : ").append(linkageDistance); 215 return sb.toString(); 216 } 217 218 }