001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021package org.biojava.nbio.structure.align.multiple; 022 023import java.io.IOException; 024import java.io.Serializable; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.List; 028 029import javax.vecmath.Matrix4d; 030 031import org.biojava.nbio.structure.Atom; 032import org.biojava.nbio.structure.Calc; 033import org.biojava.nbio.structure.StructureException; 034import org.biojava.nbio.structure.StructureIdentifier; 035import org.biojava.nbio.structure.align.client.StructureName; 036import org.biojava.nbio.structure.align.helper.AlignUtils; 037import org.biojava.nbio.structure.align.model.AFPChain; 038import org.biojava.nbio.structure.align.multiple.util.MultipleAlignmentScorer; 039import org.biojava.nbio.structure.align.util.AtomCache; 040import org.biojava.nbio.structure.jama.Matrix; 041 042/** 043 * A general implementation of a {@link MultipleAlignmentEnsemble}. 044 * 045 * @author Aleix Lafita 046 * @since 4.1.0 047 * 048 */ 049public class MultipleAlignmentEnsembleImpl extends AbstractScoresCache 050 implements MultipleAlignmentEnsemble, Serializable, Cloneable { 051 052 private static final long serialVersionUID = -5732485866623431898L; 053 054 // Creation Properties 055 private String algorithmName; 056 private String version; 057 private Long ioTime; 058 private Long calculationTime; 059 060 // Structure Identifiers 061 private List<StructureIdentifier> structureIdentifiers; 062 private List<Atom[]> atomArrays; 063 private List<Matrix> distanceMatrix; 064 065 private List<MultipleAlignment> multipleAlignments; 066 067 /** 068 * Default Constructor. Empty ensemble, no structures assigned. 069 * 070 */ 071 public MultipleAlignmentEnsembleImpl() { 072 073 algorithmName = null; 074 version = null; 075 ioTime = null; 076 calculationTime = null; 077 078 structureIdentifiers = null; 079 atomArrays = null; 080 distanceMatrix = null; 081 multipleAlignments = null; 082 } 083 084 /** 085 * Constructor using structure identifiers. 086 * 087 * @param structureIdentifiers 088 * List of Structure names, that can be parsed by 089 * {@link AtomCache}. 090 */ 091 public MultipleAlignmentEnsembleImpl( 092 List<StructureIdentifier> structureIdentifiers) { 093 this(); 094 setStructureIdentifiers(structureIdentifiers); 095 } 096 097 /** 098 * Copy constructor. This copies recursively all member variables, including 099 * MultipleAlignments, Atom arrays and cached variables. 100 * 101 * @param e 102 * MultipleAlignmentEnsemble to copy. 103 */ 104 public MultipleAlignmentEnsembleImpl(MultipleAlignmentEnsembleImpl e) { 105 106 super(e); // Copy the scores 107 algorithmName = e.algorithmName; 108 version = e.version; 109 ioTime = e.ioTime; 110 calculationTime = e.calculationTime; 111 112 distanceMatrix = null; 113 if (e.distanceMatrix != null) { 114 // Make a deep copy of everything 115 distanceMatrix = new ArrayList<>(); 116 for (Matrix mat : e.distanceMatrix) { 117 distanceMatrix.add((Matrix) mat.clone()); 118 } 119 } 120 121 multipleAlignments = null; 122 if (e.multipleAlignments != null) { 123 // Make a deep copy of everything 124 multipleAlignments = new ArrayList<>(); 125 for (MultipleAlignment msa : e.multipleAlignments) { 126 MultipleAlignment newMSA = msa.clone(); 127 newMSA.setEnsemble(this); 128 multipleAlignments.add(newMSA); 129 } 130 } 131 132 if (e.atomArrays != null) { 133 atomArrays = new ArrayList<>(e.atomArrays); 134 } 135 if (e.structureIdentifiers != null) { 136 structureIdentifiers = new ArrayList<>( 137 e.structureIdentifiers); 138 } 139 } 140 141 /** 142 * Constructor from an AFPChain instance. Creates an equivalent pairwise 143 * alignment, but in the MultipleAlignment format. 144 * 145 * @param afp 146 * pairwise alignment 147 * @param ca1 148 * Atoms of the first strcuture 149 * @param ca2 150 * Atoms of the second structure 151 * @param flexible 152 * true if the alignment is flexible (use BlockSets) 153 */ 154 public MultipleAlignmentEnsembleImpl(AFPChain afp, Atom[] ca1, Atom[] ca2, 155 boolean flexible) { 156 157 this(); 158 // Copy all the creation and algorithm information 159 atomArrays = Arrays.asList(ca1, ca2); 160 if (afp.getName1() != null && !afp.getName1().isEmpty() && 161 afp.getName2() != null && !afp.getName2().isEmpty()) { 162 structureIdentifiers = Arrays.<StructureIdentifier> asList( 163 new StructureName(afp.getName1()), 164 new StructureName(afp.getName2())); 165 } 166 algorithmName = afp.getAlgorithmName(); 167 version = afp.getVersion(); 168 calculationTime = afp.getCalculationTime(); 169 170 MultipleAlignment msa = new MultipleAlignmentImpl(this); 171 setMultipleAlignments(Arrays.asList(msa)); 172 173 // Convert the rotation and translation to a Matrix4D and set it 174 Matrix4d ident = new Matrix4d(); 175 ident.setIdentity(); 176 Matrix[] rot = afp.getBlockRotationMatrix(); 177 Atom[] shift = afp.getBlockShiftVector(); 178 179 // Create a BlockSet for every block in AFPChain if flexible 180 if (flexible) { 181 for (int bs = 0; bs < afp.getBlockNum(); bs++) { 182 BlockSet blockSet = new BlockSetImpl(msa); 183 Matrix4d blockTr = null; 184 try { 185 blockTr = Calc.getTransformation(rot[bs], shift[bs]); 186 } catch (IndexOutOfBoundsException e) { 187 blockTr = ident; 188 } catch (NullPointerException e) { 189 blockTr = ident; 190 } 191 blockSet.setTransformations(Arrays.asList(ident, blockTr)); 192 Block block = new BlockImpl(blockSet); 193 block.setAlignRes(new ArrayList<List<Integer>>()); 194 block.getAlignRes().add(new ArrayList<Integer>()); 195 block.getAlignRes().add(new ArrayList<Integer>()); 196 197 // Set the transformation of the BlockSet 198 Matrix rotB = afp.getBlockRotationMatrix()[bs]; 199 Atom shiftB = afp.getBlockShiftVector()[bs]; 200 Matrix4d transformB = Calc.getTransformation(rotB, shiftB); 201 blockSet.setTransformations(Arrays.asList(ident, transformB)); 202 203 // Convert the optimal alignment to a Block 204 for (int i = 0; i < afp.getOptAln()[bs][0].length; i++) { 205 block.getAlignRes().get(0).add(afp.getOptAln()[bs][0][i]); 206 block.getAlignRes().get(1).add(afp.getOptAln()[bs][1][i]); 207 } 208 } 209 } // Create a Block for every block in AFPChain if not flexible 210 else { 211 BlockSet blockSet = new BlockSetImpl(msa); 212 Matrix4d blockTr = null; 213 try { 214 blockTr = Calc.getTransformation(rot[0], shift[0]); 215 } catch (IndexOutOfBoundsException e) { 216 blockTr = ident; 217 } catch (NullPointerException e) { 218 blockTr = ident; 219 } 220 blockSet.setTransformations(Arrays.asList(ident, blockTr)); 221 for (int bs = 0; bs < afp.getBlockNum(); bs++) { 222 Block block = new BlockImpl(blockSet); 223 block.setAlignRes(new ArrayList<List<Integer>>()); 224 block.getAlignRes().add(new ArrayList<Integer>()); 225 block.getAlignRes().add(new ArrayList<Integer>()); 226 227 // Convert the optimal alignment to a Block 228 for (int i = 0; i < afp.getOptAln()[bs][0].length; i++) { 229 block.getAlignRes().get(0).add(afp.getOptAln()[bs][0][i]); 230 block.getAlignRes().get(1).add(afp.getOptAln()[bs][1][i]); 231 } 232 } 233 } 234 235 // Copy the scores stored in the AFPChain 236 msa.putScore(MultipleAlignmentScorer.PROBABILITY, afp.getProbability()); 237 msa.putScore(MultipleAlignmentScorer.AVGTM_SCORE, afp.getTMScore()); 238 msa.putScore(MultipleAlignmentScorer.CE_SCORE, afp.getAlignScore()); 239 msa.putScore(MultipleAlignmentScorer.RMSD, afp.getTotalRmsdOpt()); 240 } 241 242 @Override 243 public MultipleAlignmentEnsembleImpl clone() { 244 return new MultipleAlignmentEnsembleImpl(this); 245 } 246 247 @Override 248 public String getAlgorithmName() { 249 return algorithmName; 250 } 251 252 @Override 253 public void setAlgorithmName(String algorithmName) { 254 this.algorithmName = algorithmName; 255 } 256 257 @Override 258 public String getVersion() { 259 return version; 260 } 261 262 @Override 263 public void setVersion(String version) { 264 this.version = version; 265 } 266 267 @Override 268 public Long getIoTime() { 269 return ioTime; 270 } 271 272 @Override 273 public void setIoTime(Long millis) { 274 this.ioTime = millis; 275 } 276 277 @Override 278 public Long getCalculationTime() { 279 return calculationTime; 280 } 281 282 @Override 283 public void setCalculationTime(Long millis) { 284 this.calculationTime = millis; 285 } 286 287 @Override 288 public List<StructureIdentifier> getStructureIdentifiers() { 289 return structureIdentifiers; 290 } 291 292 @Override 293 public void setStructureIdentifiers(List<StructureIdentifier> structureNames) { 294 this.structureIdentifiers = structureNames; 295 } 296 297 @Override 298 public List<Atom[]> getAtomArrays() { 299 if (atomArrays == null) { 300 try { 301 updateAtomArrays(); 302 } catch (IOException e) { 303 throw new NullPointerException(e.getMessage()); 304 } catch (StructureException e) { 305 throw new NullPointerException(e.getMessage()); 306 } 307 } 308 return atomArrays; 309 } 310 311 @Override 312 public void setAtomArrays(List<Atom[]> atomArrays) { 313 this.atomArrays = atomArrays; 314 } 315 316 /** 317 * Force the atom arrays to regenerate based on 318 * {@link #getStructureIdentifiers()}. 319 * 320 * @throws IOException 321 * @throws StructureException 322 */ 323 public void updateAtomArrays() throws IOException, StructureException { 324 AtomCache cache = new AtomCache(); 325 atomArrays = new ArrayList<>(); 326 for (StructureIdentifier name : getStructureIdentifiers()) { 327 Atom[] array = cache.getRepresentativeAtoms(name); 328 atomArrays.add(array); 329 } 330 } 331 332 @Override 333 public List<Matrix> getDistanceMatrix() { 334 if (distanceMatrix == null) 335 updateDistanceMatrix(); 336 return distanceMatrix; 337 } 338 339 /** 340 * Force recalculation of the distance matrices. 341 */ 342 public void updateDistanceMatrix() { 343 344 // Reset the distance Matrix variable 345 distanceMatrix = new ArrayList<>(); 346 347 for (int s = 0; s < size(); s++) { 348 Atom[] ca = atomArrays.get(s); 349 Matrix distMat = AlignUtils.getDistanceMatrix(ca, ca); 350 distanceMatrix.add(distMat); 351 } 352 } 353 354 @Override 355 public List<MultipleAlignment> getMultipleAlignments() { 356 357 if (multipleAlignments == null) { 358 multipleAlignments = new ArrayList<>(); 359 } 360 return multipleAlignments; 361 } 362 363 @Override 364 public MultipleAlignment getMultipleAlignment(int index) { 365 return multipleAlignments.get(index); 366 } 367 368 @Override 369 public void setMultipleAlignments(List<MultipleAlignment> alignments) { 370 this.multipleAlignments = alignments; 371 } 372 373 @Override 374 public void addMultipleAlignment(MultipleAlignment alignment) { 375 if (multipleAlignments == null) { 376 multipleAlignments = new ArrayList<>(); 377 } 378 multipleAlignments.add(alignment); 379 alignment.setEnsemble(this); 380 } 381 382 @Override 383 public int size() { 384 if (structureIdentifiers != null) 385 return structureIdentifiers.size(); 386 else if (atomArrays != null) 387 return atomArrays.size(); 388 else { 389 throw new IndexOutOfBoundsException( 390 "Empty ensemble: names == null && atoms == null"); 391 } 392 } 393 394 @Override 395 public void clear() { 396 super.clear(); 397 distanceMatrix = null; 398 for (MultipleAlignment a : getMultipleAlignments()) 399 a.clear(); 400 } 401}