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 }