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