1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  package ffx.algorithms.optimize.manybody;
39  
40  import edu.rit.pj.ParallelTeam;
41  import ffx.algorithms.AlgorithmListener;
42  import ffx.algorithms.optimize.RotamerOptimization;
43  import ffx.crystal.Crystal;
44  import ffx.crystal.SymOp;
45  import ffx.potential.MolecularAssembly;
46  import ffx.potential.bonded.Atom;
47  import ffx.potential.bonded.MultiResidue;
48  import ffx.potential.bonded.Residue;
49  import ffx.potential.bonded.ResidueState;
50  import ffx.potential.bonded.Rotamer;
51  import ffx.potential.bonded.RotamerLibrary;
52  import ffx.potential.nonbonded.NeighborList;
53  import org.apache.commons.math3.util.CombinatoricsUtils;
54  import org.apache.commons.math3.util.FastMath;
55  
56  import java.util.Arrays;
57  import java.util.HashMap;
58  import java.util.List;
59  import java.util.Map;
60  import java.util.logging.Level;
61  import java.util.logging.Logger;
62  
63  import static java.lang.String.format;
64  import static org.apache.commons.math3.util.FastMath.min;
65  import static org.apache.commons.math3.util.FastMath.sqrt;
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  public class DistanceMatrix {
83  
84    private static final Logger logger = Logger.getLogger(DistanceMatrix.class.getName());
85  
86    
87  
88  
89    private final MolecularAssembly molecularAssembly;
90    
91  
92  
93    private final AlgorithmListener algorithmListener;
94    
95  
96  
97  
98    private final Residue[] allResiduesArray;
99    
100 
101 
102 
103   private final List<Residue> allResiduesList;
104   
105 
106 
107   private final int numResidues;
108   
109 
110 
111   private final RotamerOptimization.DistanceMethod distanceMethod;
112   
113 
114 
115   private final double distance;
116   
117 
118 
119   private final double twoBodyCutoffDist;
120   
121 
122 
123   private final double threeBodyCutoffDist;
124   
125 
126 
127 
128   private NeighborDistances[][] distanceMatrix;
129 
130   public DistanceMatrix(MolecularAssembly molecularAssembly, AlgorithmListener algorithmListener,
131                         Residue[] allResiduesArray, List<Residue> allResiduesList,
132                         RotamerOptimization.DistanceMethod distanceMethod, double distance, double twoBodyCutoffDist,
133                         double threeBodyCutoffDist) {
134     this.molecularAssembly = molecularAssembly;
135     this.algorithmListener = algorithmListener;
136     this.allResiduesArray = allResiduesArray;
137     this.allResiduesList = allResiduesList;
138     this.numResidues = allResiduesArray.length;
139     this.distanceMethod = distanceMethod;
140     this.distance = distance;
141     this.twoBodyCutoffDist = twoBodyCutoffDist;
142     this.threeBodyCutoffDist = threeBodyCutoffDist;
143     distanceMatrix();
144   }
145 
146   
147 
148 
149 
150 
151 
152   private static boolean areFiniteAndNotMax(double... values) {
153     return Arrays.stream(values)
154         .allMatch((double val) -> Double.isFinite(val) && val < Double.MAX_VALUE);
155   }
156 
157   
158 
159 
160 
161 
162 
163 
164 
165 
166 
167   public double checkDistMatrix(int i, int ri, int j, int rj) {
168     if (i > j) {
169       int temp = i;
170       i = j;
171       j = temp;
172       temp = ri;
173       ri = rj;
174       rj = temp;
175     }
176     return distanceMatrix[i][ri].getDistance(j, rj);
177   }
178 
179   
180 
181 
182 
183 
184 
185 
186 
187 
188   public boolean checkPairDistThreshold(int i, int ri, int j, int rj) {
189     if (twoBodyCutoffDist <= 0 || !Double.isFinite(twoBodyCutoffDist)) {
190       return false;
191     }
192     return (get2BodyDistance(i, ri, j, rj) > twoBodyCutoffDist);
193   }
194 
195   
196 
197 
198 
199 
200 
201 
202 
203 
204 
205 
206 
207 
208 
209   public boolean checkQuadDistThreshold(int i, int ri, int j, int rj, int k, int rk, int l, int rl) {
210     if (checkTriDistThreshold(i, ri, j, rj, k, rk) || checkTriDistThreshold(i, ri, j, rj, l, rl)
211         || checkTriDistThreshold(i, ri, k, rk, l, rl) || checkTriDistThreshold(j, rj, k, rk, l,
212         rl)) {
213       return true;
214     }
215     
216     if (threeBodyCutoffDist <= 0 || !Double.isFinite(threeBodyCutoffDist)) {
217       return false;
218     }
219     return get4BodyDistance(i, ri, j, rj, k, rk, l, rl) > threeBodyCutoffDist;
220   }
221 
222   
223 
224 
225 
226 
227 
228 
229 
230 
231 
232 
233 
234   public boolean checkTriDistThreshold(int i, int ri, int j, int rj, int k, int rk) {
235     if (checkPairDistThreshold(i, ri, j, rj) || checkPairDistThreshold(i, ri, k, rk)
236         || checkPairDistThreshold(j, rj, k, rk)) {
237       return true;
238     }
239     if (threeBodyCutoffDist <= 0 || !Double.isFinite(threeBodyCutoffDist)) {
240       return false;
241     }
242     return (get3BodyDistance(i, ri, j, rj, k, rk) > threeBodyCutoffDist);
243   }
244 
245   
246 
247 
248 
249 
250 
251 
252 
253 
254 
255 
256 
257   public double get2BodyDistance(int i, int ri, int j, int rj) {
258     if (i > j) {
259       int temp = i;
260       i = j;
261       j = temp;
262       temp = ri;
263       ri = rj;
264       rj = temp;
265     }
266 
267     if (distanceMethod == RotamerOptimization.DistanceMethod.ROTAMER) {
268       return checkDistMatrix(i, ri, j, rj);
269     } else {
270       return getResidueDistance(i, ri, j, rj);
271     }
272   }
273 
274   
275 
276 
277 
278 
279 
280 
281 
282 
283 
284 
285 
286   public double get3BodyResidueDistance(int i, int ri, int j, int rj, int k, int rk) {
287     double ij = getResidueDistance(i, ri, j, rj);
288     double ik = getResidueDistance(i, ri, k, rk);
289     double jk = getResidueDistance(j, rj, k, rk);
290     if (areFiniteAndNotMax(ij, ik, jk)) {
291       return sqrt((ij * ij + ik * ik + jk * jk) / 3.0);
292     } else {
293       return Double.MAX_VALUE;
294     }
295   }
296 
297   
298 
299 
300 
301 
302 
303 
304 
305 
306 
307 
308 
309 
310 
311   public double get4BodyResidueDistance(int i, int ri, int j, int rj, int k, int rk, int l, int rl) {
312     double ij = getResidueDistance(i, ri, j, rj);
313     double ik = getResidueDistance(i, ri, k, rk);
314     double il = getResidueDistance(i, ri, l, rl);
315     double jk = getResidueDistance(j, rj, k, rk);
316     double jl = getResidueDistance(j, rj, l, rl);
317     double kl = getResidueDistance(k, rk, l, rl);
318     if (areFiniteAndNotMax(ij, ik, il, jk, jl, kl)) {
319       return sqrt((ij * ij + ik * ik + il * il + jk * jk + jl * jl + kl * kl) / 6.0);
320     } else {
321       return Double.MAX_VALUE;
322     }
323   }
324 
325   
326 
327 
328 
329 
330 
331   public double getRawNBodyDistance(int... resRot) {
332     int nRes = resRot.length;
333     if (nRes % 2 != 0) {
334       throw new IllegalArgumentException(" Must have an even number of arguments; res-rot pairs!");
335     }
336     nRes /= 2;
337     if (nRes < 2) {
338       throw new IllegalArgumentException(" Must have >= 4 arguments; at least 2 res-rot pairs!");
339     }
340     
341     int numDists = (int) CombinatoricsUtils.binomialCoefficient(nRes, 2);
342     double mult = 1.0 / numDists;
343     double totDist2 = 0.0;
344 
345     for (int i = 0; i < nRes - 1; i++) {
346       int i2 = 2 * i;
347       for (int j = i + 1; j < nRes; j++) {
348         int j2 = 2 * j;
349         double rawDist = checkDistMatrix(resRot[i2], resRot[i2 + 1], resRot[j2], resRot[j2 + 1]);
350         if (!Double.isFinite(rawDist) || rawDist == Double.MAX_VALUE) {
351           return Double.MAX_VALUE;
352         }
353         totDist2 += (rawDist * mult);
354       }
355     }
356     return FastMath.sqrt(totDist2);
357   }
358 
359   
360 
361 
362 
363 
364 
365 
366 
367 
368 
369   public double getResidueDistance(int i, int ri, int j, int rj) {
370     if (i > j) {
371       int temp = i;
372       i = j;
373       j = temp;
374       temp = ri;
375       ri = rj;
376       rj = temp;
377     }
378 
379     double minDist = Double.MAX_VALUE;
380     final int lenri = distanceMatrix[i].length;
381     final int lenrj = allResiduesArray[j].getRotamers().length;
382     for (int roti = 0; roti < lenri; roti++) {
383       for (int rotj = 0; rotj < lenrj; rotj++) {
384         minDist = Math.min(minDist, checkDistMatrix(i, roti, j, rotj));
385       }
386     }
387     return minDist;
388   }
389 
390   
391 
392 
393 
394 
395 
396 
397 
398   public double interResidueDistance(double[][] resi, double[][] resj, SymOp symOp) {
399     double dist = Double.MAX_VALUE;
400     Crystal crystal = molecularAssembly.getCrystal();
401     for (double[] xi : resi) {
402       for (double[] xj : resj) {
403         if (symOp != null) {
404           crystal.applySymOp(xj, xj, symOp);
405         }
406         double r = crystal.image(xi[0] - xj[0], xi[1] - xj[1], xi[2] - xj[2]);
407         if (r < dist) {
408           dist = r;
409         }
410       }
411     }
412     return sqrt(dist);
413   }
414 
415   private void distanceMatrix() {
416     distanceMatrix = new NeighborDistances[numResidues - 1][];
417     for (int i = 0; i < (numResidues - 1); i++) {
418       Residue residuei = allResiduesArray[i];
419       int lengthRi;
420       try {
421         lengthRi = residuei.getRotamers().length;
422       } catch (IndexOutOfBoundsException ex) {
423         logger.warning(format(" Residue i %s has null rotamers.", residuei.toFormattedString(false, true)));
424         continue;
425       }
426       distanceMatrix[i] = new NeighborDistances[lengthRi];
427       for (int ri = 0; ri < lengthRi; ri++) {
428         distanceMatrix[i][ri] = new NeighborDistances(i, ri);
429       }
430     }
431 
432     ResidueState[] orig = ResidueState.storeAllCoordinates(allResiduesList);
433     int nMultiRes = 0;
434 
435     
436     
437     Atom[] atoms = new Atom[numResidues];
438     for (int i = 0; i < numResidues; i++) {
439       Residue residuei = allResiduesArray[i];
440       atoms[i] = residuei.getReferenceAtom();
441       if (residuei instanceof MultiResidue) {
442         ++nMultiRes;
443       }
444     }
445 
446       
447 
448 
449 
450 
451 
452     ParallelTeam parallelTeam = getParallelTeam(nMultiRes);
453     Crystal crystal = molecularAssembly.getCrystal();
454     int nSymm = crystal.spaceGroup.getNumberOfSymOps();
455     logger.info("\n Computing Residue Distance Matrix");
456 
457     double nlistCutoff = Math.max(Math.max(distance, twoBodyCutoffDist), threeBodyCutoffDist);
458 
459     
460     
461     double conservativeBuffer = 25.0;
462     nlistCutoff += conservativeBuffer;
463 
464       
465 
466 
467 
468 
469     if (!crystal.aperiodic()) {
470       double sphere = min(min(crystal.interfacialRadiusA, crystal.interfacialRadiusB), crystal.interfacialRadiusC);
471       if (nlistCutoff > sphere) {
472         nlistCutoff = sphere;
473       }
474     }
475 
476     NeighborList neighborList = new NeighborList(crystal, atoms, nlistCutoff, 0.0, parallelTeam);
477 
478     
479     double[][] xyz = new double[nSymm][3 * numResidues];
480     double[] in = new double[3];
481     double[] out = new double[3];
482     for (int iSymOp = 0; iSymOp < nSymm; iSymOp++) {
483       SymOp symOp = crystal.spaceGroup.getSymOp(iSymOp);
484       for (int i = 0; i < numResidues; i++) {
485         Atom atom = atoms[i];
486         in[0] = atom.getX();
487         in[1] = atom.getY();
488         in[2] = atom.getZ();
489         crystal.applySymOp(in, out, symOp);
490         int iX = i * 3;
491         int iY = iX + 1;
492         int iZ = iX + 2;
493         xyz[iSymOp][iX] = out[0];
494         xyz[iSymOp][iY] = out[1];
495         xyz[iSymOp][iZ] = out[2];
496       }
497     }
498 
499     
500     int[][][] lists = new int[nSymm][numResidues][];
501     boolean forceRebuild = true;
502     boolean printLists = false;
503     long neighborTime = -System.nanoTime();
504     neighborList.buildList(xyz, lists, null, forceRebuild, printLists);
505 
506     neighborTime += System.nanoTime();
507     logger.info(format(" Built residue neighbor list:           %8.3f sec", neighborTime * 1.0e-9));
508 
509     DistanceRegion distanceRegion = new DistanceRegion(parallelTeam.getThreadCount(), numResidues,
510         crystal, lists, neighborList.getPairwiseSchedule());
511 
512     long parallelTime = -System.nanoTime();
513     try {
514       distanceRegion.init(this, molecularAssembly, allResiduesArray, algorithmListener, distanceMatrix);
515       parallelTeam.execute(distanceRegion);
516     } catch (Exception e) {
517       String message = " Exception computing residue distance matrix.";
518       logger.log(Level.SEVERE, message, e);
519     }
520     parallelTime += System.nanoTime();
521     logger.info(format(" Pairwise distance matrix:              %8.3f sec", parallelTime * 1.0e-9));
522 
523     ResidueState.revertAllCoordinates(allResiduesList, orig);
524     try {
525       parallelTeam.shutdown();
526     } catch (Exception ex) {
527       logger.warning(format(" Exception shutting down parallel team for the distance matrix: %s", ex));
528     }
529   }
530 
531   private ParallelTeam getParallelTeam(int nMultiRes) {
532     int nThreads;
533     if (molecularAssembly.getPotentialEnergy().getParallelTeam() != null) {
534       nThreads = (nMultiRes > 1) ? 1
535           : molecularAssembly.getPotentialEnergy().getParallelTeam().getThreadCount();
536     } else {
537       
538       nThreads = 16;
539     }
540     ParallelTeam parallelTeam = new ParallelTeam(nThreads);
541     return parallelTeam;
542   }
543 
544   
545 
546 
547 
548 
549 
550 
551 
552 
553 
554 
555 
556   private double get3BodyDistance(int i, int ri, int j, int rj, int k, int rk) {
557     double ij = get2BodyDistance(i, ri, j, rj);
558     double ik = get2BodyDistance(i, ri, k, rk);
559     double jk = get2BodyDistance(j, rj, k, rk);
560     if (areFiniteAndNotMax(ij, ik, jk)) {
561       return sqrt((ij * ij + ik * ik + jk * jk) / 3.0);
562     } else {
563       return Double.MAX_VALUE;
564     }
565   }
566 
567   
568 
569 
570 
571 
572 
573 
574 
575 
576 
577 
578 
579 
580 
581   private double get4BodyDistance(int i, int ri, int j, int rj, int k, int rk, int l, int rl) {
582     double ij = get2BodyDistance(i, ri, j, rj);
583     double ik = get2BodyDistance(i, ri, k, rk);
584     double il = get2BodyDistance(i, ri, l, rl);
585     double jk = get2BodyDistance(j, rj, k, rk);
586     double jl = get2BodyDistance(j, rj, l, rl);
587     double kl = get2BodyDistance(k, rk, l, rl);
588     if (areFiniteAndNotMax(ij, ik, il, jk, jl, kl)) {
589       return sqrt((ij * ij + ik * ik + il * il + jk * jk + jl * jl + kl * kl) / 6.0);
590     } else {
591       return Double.MAX_VALUE;
592     }
593   }
594 
595   
596 
597 
598 
599 
600 
601 
602 
603 
604 
605   private double evaluateDistance(int i, int ri, int j, int rj) {
606     Residue resi = allResiduesArray[i];
607     Rotamer[] rotamersI = resi.getRotamers();
608     Rotamer roti = rotamersI[ri];
609     double[][] xi;
610     if (roti.equals(resi.getRotamer())) {
611       xi = resi.storeCoordinateArray();
612     } else {
613       ResidueState origI = resi.storeState();
614       RotamerLibrary.applyRotamer(resi, roti);
615       xi = resi.storeCoordinateArray();
616       resi.revertState(origI);
617     }
618 
619     Residue resj = allResiduesArray[j];
620     Rotamer[] rotamersJ = resj.getRotamers();
621     Rotamer rotj = rotamersJ[rj];
622     double[][] xj;
623     if (rotj.equals(resj.getRotamer())) {
624       xj = resj.storeCoordinateArray();
625     } else {
626       ResidueState origJ = resj.storeState();
627       RotamerLibrary.applyRotamer(resj, rotj);
628       xj = resj.storeCoordinateArray();
629       resj.revertState(origJ);
630     }
631 
632     Crystal crystal = molecularAssembly.getCrystal();
633     int nSymm = crystal.spaceGroup.getNumberOfSymOps();
634     double minDist = Double.MAX_VALUE;
635     for (int iSymOp = 0; iSymOp < nSymm; iSymOp++) {
636       SymOp symOp = crystal.spaceGroup.getSymOp(iSymOp);
637       double dist = interResidueDistance(xi, xj, symOp);
638       minDist = Math.min(dist, minDist);
639     }
640     return minDist;
641   }
642 
643   
644 
645 
646   public class NeighborDistances {
647 
648     
649 
650 
651     private final int i;
652     
653 
654 
655     private final int ri;
656 
657     
658 
659 
660 
661 
662 
663     private final Map<Integer, double[]> distances = new HashMap<>();
664 
665     
666 
667 
668 
669 
670 
671     public NeighborDistances(int i, int ri) {
672       this.i = i;
673       this.ri = ri;
674     }
675 
676     
677 
678 
679 
680 
681 
682 
683     public void storeDistance(int j, int rj, double distance) {
684       
685       synchronized (distances) {
686         if (distances.containsKey(j)) {
687           distances.get(j)[rj] = distance;
688         } else {
689           double[] dists = new double[allResiduesArray[j].getRotamers().length];
690           Arrays.fill(dists, -1);
691           dists[rj] = distance;
692           distances.put(j, dists);
693         }
694       }
695     }
696 
697     
698 
699 
700 
701 
702 
703 
704     public double getDistance(int j, int rj) {
705       if (distances.containsKey(j) && distances.get(j)[rj] >= 0) {
706         return distances.get(j)[rj];
707       } else {
708         double distance = Double.POSITIVE_INFINITY;
709         storeDistance(j, rj, distance);
710         return distance;
711       }
712     }
713   }
714 
715 }