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 ffx.algorithms.optimize.RotamerOptimization;
41 import ffx.potential.bonded.MultiResidue;
42 import ffx.potential.bonded.Residue;
43 import ffx.potential.bonded.Rotamer;
44
45 import java.util.List;
46 import java.util.logging.Level;
47 import java.util.logging.Logger;
48 import java.util.stream.IntStream;
49
50 import static ffx.potential.bonded.Residue.ResidueType.NA;
51 import static java.lang.String.format;
52
53 public class EliminatedRotamers {
54
55 private static final Logger logger = Logger.getLogger(EliminatedRotamers.class.getName());
56
57 private final RotamerOptimization rO;
58 private final DistanceMatrix dM;
59
60
61
62
63 private final List<Residue> allResiduesList;
64
65
66
67 private final int maxRotCheckDepth;
68
69
70
71 private final double clashThreshold;
72
73
74
75 private final double pairClashThreshold;
76
77
78
79
80 private final double multiResClashThreshold;
81
82
83
84
85 private final double nucleicPruningFactor;
86
87 private final double nucleicPairsPruningFactor;
88
89
90
91 private final double multiResPairClashAddn;
92
93
94
95 private final boolean pruneClashes;
96
97
98
99 private final boolean prunePairClashes;
100
101
102
103 private final boolean print;
104
105
106
107 public boolean[][] eliminatedSingles;
108
109
110
111 public boolean[][][][] eliminatedPairs;
112
113
114
115 public boolean[][] onlyPrunedSingles;
116
117
118
119 public boolean[][][][] onlyPrunedPairs;
120
121 private EnergyExpansion eE;
122
123 public EliminatedRotamers(RotamerOptimization rO, DistanceMatrix dM, List<Residue> allResiduesList,
124 int maxRotCheckDepth, double clashThreshold, double pairClashThreshold,
125 double multiResClashThreshold, double nucleicPruningFactor, double nucleicPairsPruningFactor,
126 double multiResPairClashAddn, boolean pruneClashes, boolean prunePairClashes, boolean print,
127 Residue[] residues) {
128 this.rO = rO;
129 this.dM = dM;
130 this.allResiduesList = allResiduesList;
131 this.maxRotCheckDepth = maxRotCheckDepth;
132 this.clashThreshold = clashThreshold;
133 this.pairClashThreshold = pairClashThreshold;
134 this.multiResClashThreshold = multiResClashThreshold;
135 this.nucleicPruningFactor = nucleicPruningFactor;
136 this.nucleicPairsPruningFactor = nucleicPairsPruningFactor;
137 this.multiResPairClashAddn = multiResPairClashAddn;
138 this.pruneClashes = pruneClashes;
139 this.prunePairClashes = prunePairClashes;
140 this.print = print;
141 allocateEliminationMemory(residues);
142 }
143
144
145
146
147
148
149
150
151 public boolean check(int i, int ri) {
152 if (eliminatedSingles == null) {
153 return false;
154 }
155 return eliminatedSingles[i][ri];
156 }
157
158
159
160
161
162
163
164
165
166
167 public boolean check(int i, int ri, int j, int rj) {
168 if (eliminatedPairs == null) {
169 return false;
170 }
171
172
173 if (j < i) {
174 int ii = i;
175 int iri = ri;
176 i = j;
177 ri = rj;
178 j = ii;
179 rj = iri;
180 }
181 return eliminatedPairs[i][ri][j][rj];
182 }
183
184
185
186
187
188
189
190
191
192
193 public boolean checkPrunedPairs(int i, int ri, int j, int rj) {
194 if (onlyPrunedPairs == null) {
195 return false;
196 }
197
198 if (j < i) {
199 int ii = i;
200 int iri = ri;
201 i = j;
202 ri = rj;
203 j = ii;
204 rj = iri;
205 }
206 return onlyPrunedPairs[i][ri][j][rj];
207 }
208
209
210
211
212
213
214
215
216 public boolean checkPrunedSingles(int i, int ri) {
217 if (onlyPrunedSingles == null) {
218 return false;
219 }
220 return onlyPrunedSingles[i][ri];
221 }
222
223
224
225
226
227
228
229
230
231
232
233
234 public boolean checkToJ(int i, int ri, int j, int rj) {
235 return (check(j, rj) || check(i, ri, j, rj));
236 }
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252 public boolean checkToK(int i, int ri, int j, int rj, int k, int rk) {
253 return (check(k, rk) || check(i, ri, k, rk) || check(j, rj, k, rk));
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272 public boolean checkToL(int i, int ri, int j, int rj, int k, int rk, int l, int rl) {
273 return (check(l, rl) || check(i, ri, l, rl) || check(j, rj, l, rl) || check(k, rk, l, rl));
274 }
275
276
277
278
279
280
281
282
283
284
285
286 public boolean eliminateRotamer(Residue[] residues, int i, int ri, boolean verbose) {
287
288 if (check(i, ri)) {
289 return false;
290 }
291
292
293 int[] validRots = rotamerCount(residues, i);
294 int rotCount = 0;
295 for (int rii = 0; rii < validRots.length; rii++) {
296 if (rii != ri) {
297 ++rotCount;
298 }
299 }
300
301 if (rotCount == 0) {
302
303 return false;
304 }
305
306 eliminatedSingles[i][ri] = true;
307
308 if (verbose) {
309 Rotamer[] rotamers = residues[i].getRotamers();
310 rO.logIfRank0(
311 format(" Rotamer (%8s,%2d) eliminated (%2d left).", residues[i].toString(rotamers[ri]), ri,
312 rotCount));
313 }
314 int eliminatedPairs = eliminateRotamerPairs(residues, i, ri, verbose);
315 if (eliminatedPairs > 0 && verbose) {
316 rO.logIfRank0(format(" Eliminated %2d rotamer pairs.", eliminatedPairs));
317 }
318 return true;
319 }
320
321 public boolean eliminateRotamerPair(Residue[] residues, int i, int ri, int j, int rj,
322 boolean verbose) {
323 if (i > j) {
324 int ii = i;
325 int iri = ri;
326 i = j;
327 ri = rj;
328 j = ii;
329 rj = iri;
330 }
331
332 if (!check(i, ri, j, rj)) {
333 eliminatedPairs[i][ri][j][rj] = true;
334 if (verbose) {
335 Rotamer[] rotI = residues[i].getRotamers();
336 Rotamer[] rotJ = residues[j].getRotamers();
337 rO.logIfRank0(format(" Rotamer pair eliminated: [(%8s,%2d) (%8s,%2d)]",
338 residues[i].toString(rotI[ri]), ri, residues[j].toString(rotJ[rj]), rj));
339 }
340 return true;
341 } else {
342 return false;
343 }
344 }
345
346 public int eliminateRotamerPairs(Residue[] residues, int i, int ri, boolean verbose) {
347 int eliminatedPairs = 0;
348 for (int j = 0; j < residues.length; j++) {
349 if (j == i) {
350 continue;
351 }
352 Residue resJ = residues[j];
353 int lenRj = resJ.getRotamers().length;
354 for (int rj = 0; rj < lenRj; rj++) {
355 if (eliminateRotamerPair(residues, i, ri, j, rj, verbose)) {
356 ++eliminatedPairs;
357 }
358 }
359 }
360 return eliminatedPairs;
361 }
362
363
364
365
366
367
368
369
370
371
372 public boolean pairsToSingleElimination(Residue[] residues, int i, int j) {
373 assert i != j;
374 assert i < residues.length;
375 assert j < residues.length;
376
377 Residue residueI = residues[i];
378 Residue residueJ = residues[j];
379 Rotamer[] rotI = residueI.getRotamers();
380 Rotamer[] rotJ = residueJ.getRotamers();
381 int lenRi = rotI.length;
382 int lenRj = rotJ.length;
383 boolean eliminated = false;
384
385
386 for (int ri = 0; ri < lenRi; ri++) {
387 if (check(i, ri)) {
388 continue;
389 }
390 boolean pairRemaining = false;
391 for (int rj = 0; rj < lenRj; rj++) {
392 if (!check(j, rj) && !check(i, ri, j, rj)) {
393 pairRemaining = true;
394 break;
395 }
396 }
397 if (!pairRemaining) {
398 if (eliminateRotamer(residues, i, ri, print)) {
399 eliminated = true;
400 rO.logIfRank0(format(" Eliminating rotamer %s-%d with no remaining pairs to residue %s.",
401 residueI.toString(rotI[ri]), ri, residueJ));
402 } else {
403 rO.logIfRank0(
404 format(" Already eliminated rotamer %s-%d with no remaining pairs to residue %s.",
405 residueI.toString(rotI[ri]), ri, residueJ), Level.WARNING);
406 }
407 }
408 }
409
410
411 for (int rj = 0; rj < lenRj; rj++) {
412 if (check(j, rj)) {
413 continue;
414 }
415 boolean pairRemaining = false;
416 for (int ri = 0; ri < lenRi; ri++) {
417 if (!check(i, ri) && !check(i, ri, j, rj)) {
418 pairRemaining = true;
419 break;
420 }
421 }
422 if (!pairRemaining) {
423 if (eliminateRotamer(residues, j, rj, print)) {
424 eliminated = true;
425 rO.logIfRank0(format(" Eliminating rotamer %s-%d with no remaining pairs to residue %s.",
426 residueJ.toString(rotJ[rj]), rj, residueI));
427 } else {
428 rO.logIfRank0(
429 format(" Already eliminated rotamer J %s-%d with no remaining pairs to residue %s.",
430 residueJ.toString(rotJ[rj]), rj, residueI), Level.WARNING);
431 }
432 }
433 }
434
435 return eliminated;
436 }
437
438
439
440
441
442
443
444 public void prePrunePairs(Residue[] residues) {
445
446
447 int nResidues = residues.length;
448 for (int i = 0; i < nResidues - 1; i++) {
449 Residue resi = residues[i];
450 Rotamer[] rotI = resi.getRotamers();
451 int ni = rotI.length;
452
453 for (int j = i + 1; j < nResidues; j++) {
454 Residue resJ = residues[j];
455 Rotamer[] rotJ = resJ.getRotamers();
456 int nj = rotJ.length;
457
458 for (int ri = 0; ri < ni; ri++) {
459 if (!validRotamer(residues, i, ri)) {
460 continue;
461 }
462
463 for (int rj = 0; rj < nj; rj++) {
464 if (!validRotamer(residues, j, rj) || check(i, ri, j, rj)) {
465 continue;
466 }
467 if (!check(i, ri, j, rj) && Double.isNaN(eE.get2Body(i, ri, j, rj))) {
468 rO.logIfRank0(format(
469 " Rotamer Pair (%7s,%2d) (%7s,%2d) 2-body energy %12.4f pre-pruned since energy is NaN.",
470 i, ri, j, rj, eE.get2Body(i, ri, j, rj)));
471 eliminateRotamerPair(residues, i, ri, j, rj, print);
472 }
473 }
474 }
475 }
476 }
477 }
478
479
480
481
482
483
484
485 public void prePruneSelves(Residue[] residues) {
486
487 for (int i = 0; i < residues.length; i++) {
488 Residue residue = residues[i];
489 Rotamer[] rotamers = residue.getRotamers();
490 int nRot = rotamers.length;
491 for (int ri = 0; ri < nRot; ri++) {
492 if (!check(i, ri) && Double.isNaN(eE.getSelf(i, ri))) {
493 rO.logIfRank0(
494 format(" Rotamer (%7s,%2d) self-energy %12.4f pre-pruned since energy is NaN.",
495 residue, ri, eE.getSelf(i, ri)));
496 eliminateRotamer(residues, i, ri, false);
497 }
498 }
499 }
500 }
501
502
503
504
505
506
507
508
509 public void prunePairClashes(Residue[] residues) {
510 if (!prunePairClashes) {
511 return;
512 }
513 int nResidues = residues.length;
514
515 for (int i = 0; i < nResidues - 1; i++) {
516 Residue residueI = residues[i];
517 Rotamer[] rotI = residueI.getRotamers();
518 int lenRi = rotI.length;
519 int indI = allResiduesList.indexOf(residueI);
520 for (int j = i + 1; j < nResidues; j++) {
521 Residue residueJ = residues[j];
522 Rotamer[] rotJ = residueJ.getRotamers();
523 int lenRj = rotJ.length;
524 int indJ = allResiduesList.indexOf(residueJ);
525
526 double minPair = Double.MAX_VALUE;
527 int minRI = -1;
528 int minRJ = -1;
529
530 boolean cutoffPair = true;
531 for (int ri = 0; ri < lenRi; ri++) {
532 if (check(i, ri)) {
533 continue;
534 }
535 for (int rj = 0; rj < lenRj; rj++) {
536 if (check(j, rj) || check(i, ri, j, rj) || dM.checkPairDistThreshold(indI, ri, indJ,
537 rj)) {
538 continue;
539 }
540 cutoffPair = false;
541 double pairEnergy = eE.get2Body(i, ri, j, rj) + eE.getSelf(i, ri) + eE.getSelf(j, rj);
542 assert Double.isFinite(pairEnergy);
543 if (pairEnergy < minPair) {
544 minPair = pairEnergy;
545 minRI = ri;
546 minRJ = rj;
547 }
548 }
549 }
550 if (cutoffPair) {
551
552 continue;
553 }
554 assert (minRI >= 0 && minRJ >= 0);
555
556
557 double threshold = pairClashThreshold;
558 if (residueI instanceof MultiResidue) {
559 threshold += multiResPairClashAddn;
560 }
561 if (residueJ instanceof MultiResidue) {
562 threshold += multiResPairClashAddn;
563 }
564 int numNARes =
565 (residueI.getResidueType() == NA ? 1 : 0) + (residueJ.getResidueType() == NA ? 1 : 0);
566 switch (numNARes) {
567 case 0:
568 break;
569 case 1:
570 threshold *= nucleicPairsPruningFactor;
571 break;
572 case 2:
573 threshold *= nucleicPruningFactor;
574 break;
575 default:
576 throw new ArithmeticException(" RotamerOptimization.prunePairClashes() has somehow "
577 + "found less than zero or more than two nucleic acid residues in a pair of"
578 + " residues. This result should be impossible.");
579 }
580 double toEliminate = threshold + minPair;
581
582 for (int ri = 0; ri < lenRi; ri++) {
583 if (check(i, ri)) {
584 continue;
585 }
586 for (int rj = 0; rj < lenRj; rj++) {
587 if (check(j, rj) || check(i, ri, j, rj)) {
588 continue;
589 }
590 double pairEnergy = eE.get2Body(i, ri, j, rj) + eE.getSelf(i, ri) + eE.getSelf(j, rj);
591 assert Double.isFinite(pairEnergy);
592 if (pairEnergy > toEliminate) {
593 rO.logIfRank0(
594 format(" Pruning pair %s-%d %s-%d by %s-%d %s-%d; energy %s > " + "%s + %s",
595 residueI.toString(rotI[ri]), ri, residueJ.toString(rotJ[rj]), rj,
596 residueI.toString(rotI[minRI]), minRI, residueJ.toString(rotJ[minRJ]), minRJ,
597 rO.formatEnergy(pairEnergy), rO.formatEnergy(threshold),
598 rO.formatEnergy(minPair)));
599 }
600 }
601 }
602
603 pairsToSingleElimination(residues, i, j);
604 }
605 }
606 }
607
608
609
610
611
612
613
614
615 public void pruneSingleClashes(Residue[] residues) {
616 if (!pruneClashes) {
617 return;
618 }
619 for (int i = 0; i < residues.length; i++) {
620 Residue residue = residues[i];
621 Rotamer[] rotamers = residue.getRotamers();
622 int nRot = rotamers.length;
623 double minEnergy = Double.MAX_VALUE;
624 int minRot = -1;
625 for (int ri = 0; ri < nRot; ri++) {
626 if (!check(i, ri) && eE.getSelf(i, ri) < minEnergy) {
627 minEnergy = eE.getSelf(i, ri);
628 minRot = ri;
629 }
630 }
631
632 double energyToPrune =
633 (residue instanceof MultiResidue) ? multiResClashThreshold : clashThreshold;
634 energyToPrune =
635 (residue.getResidueType() == NA) ? energyToPrune * nucleicPruningFactor : energyToPrune;
636 energyToPrune += minEnergy;
637
638 for (int ri = 0; ri < nRot; ri++) {
639 if (!check(i, ri) && (eE.getSelf(i, ri) > energyToPrune)) {
640 if (eliminateRotamer(residues, i, ri, print)) {
641 rO.logIfRank0(format(" Rotamer (%7s,%2d) self-energy %s pruned by (%7s,%2d) %s.",
642 residue.toString(rotamers[ri]), ri, rO.formatEnergy(eE.getSelf(i, ri)),
643 residue.toString(rotamers[minRot]), minRot, rO.formatEnergy(minEnergy)));
644 }
645 }
646 }
647 }
648 }
649
650 public void setEnergyExpansion(EnergyExpansion eE) {
651 this.eE = eE;
652 }
653
654 @Override
655 public String toString() {
656 int rotamerCount = 0;
657 int pairCount = 0;
658 int singles = 0;
659 int pairs = 0;
660 int nRes = eliminatedSingles.length;
661 for (int i = 0; i < nRes; i++) {
662 int nRotI = eliminatedSingles[i].length;
663 rotamerCount += nRotI;
664 for (int ri = 0; ri < nRotI; ri++) {
665 if (eliminatedSingles[i][ri]) {
666 singles++;
667 }
668 for (int j = i + 1; j < nRes; j++) {
669 int nRotJ = eliminatedPairs[i][ri][j].length;
670 pairCount += nRotJ;
671 for (int rj = 0; rj < nRotJ; rj++) {
672 if (eliminatedPairs[i][ri][j][rj]) {
673 pairs++;
674 }
675 }
676 }
677 }
678 }
679 return format(" %d out of %d rotamers eliminated.\n", singles, rotamerCount) + format(
680 " %d out of %d rotamer pairs eliminated.", pairs, pairCount);
681 }
682
683 public boolean validateDEE(Residue[] residues) {
684 int nRes = eliminatedSingles.length;
685
686 for (int i = 0; i < nRes; i++) {
687 Residue residueI = residues[i];
688 int ni = eliminatedSingles[i].length;
689 boolean valid = false;
690 for (int ri = 0; ri < ni; ri++) {
691 if (!check(i, ri)) {
692 valid = true;
693 }
694 }
695 if (!valid) {
696 logger.severe(
697 format(" Coding error: all %d rotamers for residue %s eliminated.", ni, residueI));
698 }
699 }
700
701
702 for (int i = 0; i < nRes; i++) {
703 Residue residueI = residues[i];
704 Rotamer[] rotI = residueI.getRotamers();
705 int ni = rotI.length;
706 for (int j = i + 1; j < nRes; j++) {
707 Residue residueJ = residues[j];
708 Rotamer[] rotJ = residueJ.getRotamers();
709 int nj = rotJ.length;
710 boolean valid = false;
711 for (int ri = 0; ri < ni; ri++) {
712 for (int rj = 0; rj < nj; rj++) {
713 if (!check(i, ri, j, rj)) {
714 valid = true;
715 }
716 }
717 }
718 if (!valid) {
719 logger.severe(format(" Coding error: all pairs for %s with residue %s eliminated.",
720 residueI.toFormattedString(false, true), residueJ));
721 }
722 }
723 }
724
725 return true;
726 }
727
728
729
730
731
732
733 private void allocateEliminationMemory(Residue[] residues) {
734 int nRes = residues.length;
735 eliminatedSingles = new boolean[nRes][];
736 eliminatedPairs = new boolean[nRes][][][];
737
738 rO.logIfRank0("\n Residue Nrot");
739 for (int i = 0; i < nRes; i++) {
740 Residue residueI = residues[i];
741 Rotamer[] rotamersI = residueI.getRotamers();
742 int lenRi = rotamersI.length;
743 rO.logIfRank0(format(" %3d %8s %4d", i + 1, residueI.toFormattedString(false, true), lenRi));
744 eliminatedSingles[i] = new boolean[lenRi];
745 eliminatedPairs[i] = new boolean[lenRi][][];
746
747 for (int ri = 0; ri < lenRi; ri++) {
748 eliminatedSingles[i][ri] = false;
749 eliminatedPairs[i][ri] = new boolean[nRes][];
750 for (int j = i + 1; j < nRes; j++) {
751 Residue residueJ = residues[j];
752 Rotamer[] rotamersJ = residueJ.getRotamers();
753 int lenRj = rotamersJ.length;
754 eliminatedPairs[i][ri][j] = new boolean[lenRj];
755 for (int rj = 0; rj < lenRj; rj++) {
756 eliminatedPairs[i][ri][j][rj] = false;
757 }
758 }
759 }
760 }
761 }
762
763
764
765
766
767
768
769
770
771 private boolean validRotamer(Residue[] residues, int i, int ri) {
772
773 if (check(i, ri)) {
774 return false;
775 }
776
777 if (maxRotCheckDepth > 1) {
778
779 int n = residues.length;
780 for (int j = 0; j < n; j++) {
781 if (j == i) {
782 continue;
783 }
784 if (rotamerPairCount(residues, i, ri, j) == 0) {
785 return false;
786 }
787 }
788 }
789 return true;
790 }
791
792
793
794
795
796
797
798
799 private int[] rotamerCount(Residue[] residues, int i) {
800 int nRes = residues.length;
801 Rotamer[] rotI = residues[i].getRotamers();
802 int ni = rotI.length;
803
804 if (maxRotCheckDepth == 0) {
805
806 return IntStream.range(0, ni).toArray();
807 }
808
809 return IntStream.range(0, ni).filter((int ri) -> {
810 if (check(i, ri)) {
811 return false;
812 }
813 if (maxRotCheckDepth > 1) {
814
815 for (int j = 0; j < nRes; j++) {
816 if (i == j) {
817 continue;
818 }
819 if (rotamerPairCount(residues, i, ri, j) == 0) {
820 return false;
821 }
822 }
823 }
824 return true;
825 }).toArray();
826 }
827
828
829
830
831
832
833
834
835
836
837
838 private boolean validRotamerPair(Residue[] residues, int i, int ri, int j, int rj) {
839
840 if (i == j) {
841 return false;
842 }
843
844
845 if (!validRotamer(residues, i, ri) || !validRotamer(residues, j, rj)) {
846 return false;
847 }
848
849
850 if (check(i, ri, j, rj)) {
851 return false;
852 }
853
854 if (maxRotCheckDepth > 1) {
855
856 int n = residues.length;
857 for (int k = 0; k < n; k++) {
858 if (k == i || k == j) {
859 continue;
860 }
861
862
863 if (rotamerTripleCount(residues, i, ri, j, rj, k) == 0) {
864
865 return false;
866 }
867 }
868 }
869 return true;
870 }
871
872
873
874
875
876
877
878
879
880
881 private int rotamerPairCount(Residue[] residues, int i, int ri, int j) {
882 if (i == j || check(i, ri)) {
883 return 0;
884 }
885 int pairCount = 0;
886 Rotamer[] rotJ = residues[j].getRotamers();
887 int nj = rotJ.length;
888
889 for (int rj = 0; rj < nj; rj++) {
890
891 if (!check(j, rj) && !check(i, ri, j, rj)) {
892
893 int nRes = residues.length;
894 boolean valid = true;
895 if (maxRotCheckDepth > 2) {
896 for (int k = 0; k < nRes; k++) {
897 if (k == i || k == j) {
898 continue;
899 }
900 if (rotamerTripleCount(residues, i, ri, j, rj, k) == 0) {
901 valid = false;
902 }
903 }
904 }
905 if (valid) {
906 pairCount++;
907 }
908 }
909 }
910 return pairCount;
911 }
912
913
914
915
916
917
918
919
920
921
922
923
924
925 private int rotamerTripleCount(Residue[] residues, int i, int ri, int j, int rj, int k) {
926 if (i == j || i == k || j == k) {
927 return 0;
928 }
929 int tripleCount = 0;
930 Rotamer[] rotK = residues[k].getRotamers();
931 int nk = rotK.length;
932
933 if (!check(i, ri) && !check(j, rj) && !check(i, ri, j, rj)) {
934
935 for (int rk = 0; rk < nk; rk++) {
936
937 if (!check(k, rk) && !check(i, ri, k, rk) && !check(j, rj, k, rk)) {
938 tripleCount++;
939 }
940 }
941 }
942 return tripleCount;
943 }
944 }