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.potential.bonded;
39
40 import ffx.potential.MolecularAssembly;
41 import ffx.potential.parsers.PDBFilter;
42 import org.apache.commons.io.FilenameUtils;
43
44 import java.io.File;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.List;
48 import java.util.logging.Level;
49 import java.util.logging.Logger;
50
51 import static java.lang.String.format;
52 import static java.lang.System.arraycopy;
53 import static org.apache.commons.math3.util.FastMath.abs;
54 import static org.apache.commons.math3.util.FastMath.exp;
55 import static org.apache.commons.math3.util.FastMath.max;
56
57
58
59
60
61
62
63 public class SturmMethod {
64
65 private static final Logger logger = Logger.getLogger(SturmMethod.class.getName());
66 final int MAXPOW = 32;
67 private final double RELERROR;
68 private final int MAXIT;
69 private final int MAX_ITER_SECANT;
70 private final double[][] xyz_o = new double[5][3];
71 private double[] roots;
72
73
74 public SturmMethod() {
75 RELERROR = 1.0e-15;
76 MAXIT = 100;
77 MAX_ITER_SECANT = 20;
78 }
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 private static boolean modp(Polynomial u, Polynomial v, Polynomial r) {
95 double[] nr = r.coefficients;
96 int end = u.order;
97 double[] uc = u.coefficients;
98
99 if (end + 1 >= 0) arraycopy(uc, 0, nr, 0, end + 1);
100
101 if (v.coefficients[v.order] < 0.0) {
102 for (int k = u.order - v.order - 1; k >= 0; k -= 2) {
103 r.coefficients[k] = -r.coefficients[k];
104 }
105
106 for (int k = u.order - v.order; k >= 0; k--) {
107 for (int j = v.order + k - 1; j >= k; j--) {
108 r.coefficients[j] =
109 -r.coefficients[j] - r.coefficients[v.order + k] * v.coefficients[j - k];
110 }
111 }
112 } else {
113 for (int k = u.order - v.order; k >= 0; k--) {
114 for (int j = v.order + k - 1; j >= k; j--) {
115 r.coefficients[j] -= r.coefficients[v.order + k] * v.coefficients[j - k];
116 }
117 }
118 }
119
120 int k = v.order - 1;
121 while (k >= 0 && abs(r.coefficients[k]) < 1.0e-18) {
122 r.coefficients[k] = 0.0;
123 k--;
124 }
125
126 r.order = max(k, 0);
127
128 if (r.order > 0) {
129 return true;
130 } else if (r.order == 0) {
131 return false;
132 } else {
133 return false;
134 }
135 }
136
137
138
139
140
141
142 public double[][] getr_o() {
143 return xyz_o;
144 }
145
146
147
148
149
150
151
152
153 public int solveSturm(int order, double[] poly_coeffs, double[] roots) {
154 Polynomial[] sseq = new Polynomial[Polynomial.MAX_ORDER * 2];
155 double min, max;
156 int nroots, nchanges, np;
157 int[] atmin = new int[1];
158 int[] atmax = new int[1];
159 this.roots = roots;
160
161 for (int i = 0; i < Polynomial.MAX_ORDER * 2; i++) {
162 sseq[i] = new Polynomial();
163 }
164
165 if (order + 1 >= 0) arraycopy(poly_coeffs, 0, sseq[0].coefficients, 0, order + 1);
166
167 if (logger.isLoggable(Level.FINE)) {
168 StringBuilder string = new StringBuilder();
169 for (int i = order; i >= 0; i--) {
170 string.append(" Coefficients in Sturm solver\n");
171 string.append(format("%d %f\n", i, sseq[0].coefficients[i]));
172 }
173 logger.fine(string.toString());
174 }
175
176 np = buildSturm(order, sseq);
177
178 if (logger.isLoggable(Level.FINE)) {
179 StringBuilder string1 = new StringBuilder();
180 string1.append(" Sturm sequence for:\n");
181 for (int i = order; i >= 0; i--) {
182 string1.append(format("%f ", sseq[0].coefficients[i]));
183 string1.append("\n");
184 }
185 for (int i = 0; i <= np; i++) {
186 for (int j = sseq[i].order; j >= 0; j--) {
187 string1.append(format("%f ", sseq[i].coefficients[j]));
188 string1.append("\n");
189 }
190 }
191 logger.fine(string1.toString());
192 }
193
194
195 nroots = numRoots(np, sseq, atmin, atmax);
196 if (logger.isLoggable(Level.FINE)) {
197 logger.fine(format(" Number of real roots: %d\n", nroots));
198 }
199
200
201 min = -1.0;
202 nchanges = numChanges(np, sseq, min);
203
204 for (int i = 0; nchanges != atmin[0] && i != MAXPOW; i++) {
205 min *= 10.0;
206 nchanges = numChanges(np, sseq, min);
207 }
208
209 if (nchanges != atmin[0]) {
210 logger.fine(" Solve: unable to bracket all negative roots\n");
211 atmin[0] = nchanges;
212 }
213
214 max = 1.0;
215 nchanges = numChanges(np, sseq, max);
216
217 for (int i = 0; nchanges != atmax[0] && i != MAXPOW; i++) {
218 max *= 10.0;
219 nchanges = numChanges(np, sseq, max);
220 }
221 if (nchanges != atmax[0]) {
222 logger.fine(" Solve: unable to bracket all positive roots\n");
223 atmax[0] = nchanges;
224 }
225
226
227 nroots = atmin[0] - atmax[0];
228 sturmBisection(np, sseq, min, max, atmin[0], atmax[0], this.roots);
229
230
231 if (logger.isLoggable(Level.FINE)) {
232 if (nroots == 1) {
233 logger.fine(format("\n One distinct real root at x = %f\n", this.roots[0]));
234 } else {
235 StringBuilder string2 = new StringBuilder();
236 string2.append(format("\n %d distinct real roots for x: \n", nroots));
237 for (int i = 0; i != nroots; i++) {
238 string2.append(format("%f\n", this.roots[i]));
239 }
240 logger.fine(string2.toString());
241 }
242 }
243
244 return nroots;
245 }
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260 public File writePDBBackbone(
261 double[][] r_n,
262 double[][] r_a,
263 double[][] r_c,
264 int stt_res,
265 int end_res,
266 MolecularAssembly molAss,
267 int counter,
268 boolean writeFile) {
269
270 Polymer[] newChain = molAss.getChains();
271 List<Atom> backBoneAtoms;
272 double[] xyz_n = new double[3];
273 double[] xyz_a = new double[3];
274 double[] xyz_c = new double[3];
275
276 xyz_o[0][0] = 0.0;
277 xyz_o[0][1] = 0.0;
278 xyz_o[0][2] = 0.0;
279 xyz_o[4][0] = 0.0;
280 xyz_o[4][1] = 0.0;
281 xyz_o[4][2] = 0.0;
282
283 List<Atom> OAtoms = new ArrayList<>();
284
285 for (int i = stt_res + 1; i < end_res; i++) {
286 Residue newResidue = newChain[0].getResidue(i);
287 backBoneAtoms = newResidue.getBackboneAtoms();
288 for (Atom backBoneAtom : backBoneAtoms) {
289 switch (backBoneAtom.getAtomType().name) {
290 case "C":
291 xyz_c[0] = r_c[i - stt_res][0];
292 xyz_c[1] = r_c[i - stt_res][1];
293 xyz_c[2] = r_c[i - stt_res][2];
294 backBoneAtom.moveTo(xyz_c);
295 break;
296 case "N":
297 xyz_n[0] = r_n[i - stt_res][0];
298 xyz_n[1] = r_n[i - stt_res][1];
299 xyz_n[2] = r_n[i - stt_res][2];
300 backBoneAtom.moveTo(xyz_n);
301 break;
302 case "CA":
303 xyz_a[0] = r_a[i - stt_res][0];
304 xyz_a[1] = r_a[i - stt_res][1];
305 xyz_a[2] = r_a[i - stt_res][2];
306 backBoneAtom.moveTo(xyz_a);
307 break;
308 case "O":
309 OAtoms.add(backBoneAtom);
310 break;
311 default:
312 newResidue.deleteAtom(backBoneAtom);
313 break;
314 }
315 }
316
317 List<Atom> sideChainAtoms = newResidue.getSideChainAtoms();
318 for (Atom sideChainAtom : sideChainAtoms) {
319 newResidue.deleteAtom(sideChainAtom);
320 }
321 }
322
323 int oCount = 0;
324 for (int i = stt_res + 1; i < end_res; i++) {
325 Residue newResidue = newChain[0].getResidue(i);
326 backBoneAtoms = newResidue.getBackboneAtoms();
327 Atom CA = new Atom("CA");
328 Atom N = new Atom("N");
329 Atom C = new Atom("C");
330 Atom O = OAtoms.get(oCount);
331
332 for (Atom backBoneAtom : backBoneAtoms) {
333 switch (backBoneAtom.getAtomType().name) {
334 case "C":
335 C = backBoneAtom;
336 break;
337 case "N":
338 N = backBoneAtom;
339 break;
340 case "CA":
341 CA = backBoneAtom;
342 break;
343 default:
344 break;
345 }
346 }
347 BondedUtils.intxyz(O, C, 1.2255, CA, 122.4, N, 180, 0);
348 xyz_o[i - stt_res][0] = O.getX();
349 xyz_o[i - stt_res][1] = O.getY();
350 xyz_o[i - stt_res][2] = O.getZ();
351
352 oCount++;
353 }
354
355 File file = molAss.getFile();
356
357 String filename = FilenameUtils.removeExtension(file.getAbsolutePath());
358 if (!filename.contains("_loop")) {
359 filename = filename + "_loop";
360 }
361
362 File modifiedFile = new File(filename + ".pdb_" + counter);
363 PDBFilter modFilter = new PDBFilter(modifiedFile, molAss, null, null);
364
365 if (writeFile) {
366 modFilter.writeFile(modifiedFile, true);
367 }
368
369 return (modifiedFile);
370 }
371
372
373
374
375
376
377
378
379
380 private double hyperTan(double a, double x) {
381 double ax = a * x;
382 if (ax > 100.0) {
383 return 1.0;
384 } else if (ax < -100.0) {
385 return -1.0;
386 } else {
387 double exp_x1 = exp(ax);
388 double exp_x2 = exp(-ax);
389 return (exp_x1 - exp_x2) / (exp_x1 + exp_x2);
390 }
391 }
392
393
394
395
396
397
398
399
400
401 private int buildSturm(int ord, Polynomial[] sseq) {
402 double f;
403 double[] fp;
404 double[] fc;
405
406 sseq[0].order = ord;
407 sseq[1].order = ord - 1;
408
409
410 f = abs(sseq[0].coefficients[ord] * ord);
411 fp = sseq[1].coefficients;
412 fc = Arrays.copyOfRange(sseq[0].coefficients, 1, sseq[0].coefficients.length);
413
414 int j = 0;
415 for (int i = 1; i <= ord; i++) {
416 fp[j] = fc[j] * i / f;
417 j++;
418 }
419
420
421 int i;
422 for (i = 0;
423 i < sseq[0].coefficients.length - 2 && modp(sseq[i], sseq[i + 1], sseq[i + 2]);
424 i++) {
425
426 f = -Math.abs(sseq[i + 2].coefficients[sseq[i + 2].order]);
427 for (j = sseq[i + 2].order; j >= 0; j--) {
428 sseq[i + 2].coefficients[j] /= f;
429 }
430 }
431
432
433 sseq[i + 2].coefficients[0] = -sseq[i + 2].coefficients[0];
434
435 return (sseq[0].order - sseq[i + 2].order);
436 }
437
438
439
440
441
442
443
444
445
446
447 private int numRoots(int np, Polynomial[] sseq, int[] atneg, int[] atpos) {
448
449 int atposinf = 0;
450 int atneginf = 0;
451
452
453 double lf = sseq[0].coefficients[sseq[0].order];
454
455 for (int i = 1; i <= np; i++)
456 {
457 double f = sseq[i].coefficients[sseq[i].order];
458 if (lf == 0.0 || lf * f < 0) {
459 atposinf++;
460 }
461 lf = f;
462 }
463
464
465 if ((sseq[0].order & 1) != 0) {
466 lf = -sseq[0].coefficients[sseq[0].order];
467 } else {
468 lf = sseq[0].coefficients[sseq[0].order];
469 }
470
471 for (int i = 1; i <= np; i++) {
472 double f;
473 if ((sseq[i].order & 1) != 0) {
474 f = -sseq[i].coefficients[sseq[i].order];
475 } else {
476 f = sseq[i].coefficients[sseq[i].order];
477 }
478 if (lf == 0.0 || lf * f < 0) {
479 atneginf++;
480 }
481 lf = f;
482 }
483
484 atneg[0] = atneginf;
485 atpos[0] = atposinf;
486
487 return (atneginf - atposinf);
488 }
489
490
491
492
493
494
495
496
497
498 private int numChanges(int np, Polynomial[] sseq, double a) {
499
500 int changes = 0;
501 double lf = evalPoly(sseq[0].order, sseq[0].coefficients, a);
502 for (int i = 1; i <= np; i++) {
503 double f = evalPoly(sseq[i].order, sseq[i].coefficients, a);
504 if (lf == 0.0 || lf * f < 0) {
505 changes++;
506 }
507
508 lf = f;
509 }
510
511 return changes;
512 }
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527 private void sturmBisection(
528 int np, Polynomial[] sseq, double min, double max, int atmin, int atmax, double[] roots) {
529 double mid = 0;
530 int n1 = 0;
531 int n2 = 0;
532 int its, atmid, nroot;
533
534 nroot = atmin - atmax;
535 if (nroot == 1) {
536
537 if (modifiedRegulaFalsi(sseq[0].order, sseq[0].coefficients, min, max, roots)) {
538 fillArray(roots, this.roots);
539 return;
540 }
541
542
543 for (its = 0; its < MAXIT; its++) {
544 mid = (min + max) / 2;
545 atmid = numChanges(np, sseq, mid);
546 if (abs(mid) > RELERROR) {
547 if (abs((max - min) / mid) < RELERROR) {
548 roots[0] = mid;
549 fillArray(roots, this.roots);
550 return;
551 }
552 } else if (abs(max - min) < RELERROR) {
553 roots[0] = mid;
554 fillArray(roots, this.roots);
555 return;
556 }
557 if ((atmin - atmid) == 0) {
558 min = mid;
559 } else {
560 max = mid;
561 }
562 }
563 if (its == MAXIT) {
564 logger.info(
565 format(
566 " sbisect: overflow min %f max %f diff %f nroot %d n1 %d n2 %d\n",
567 min, max, max - min, nroot, n1, n2));
568 roots[0] = mid;
569 }
570 fillArray(roots, this.roots);
571 return;
572 }
573
574
575 for (its = 0; its < MAXIT; its++) {
576 mid = (min + max) / 2;
577 atmid = numChanges(np, sseq, mid);
578 n1 = atmin - atmid;
579 n2 = atmid - atmax;
580 if (n1 != 0 && n2 != 0) {
581 sturmBisection(np, sseq, min, mid, atmin, atmid, roots);
582
583
584 final double[] rootsSection = Arrays.copyOfRange(roots, n1, roots.length);
585 sturmBisection(np, sseq, mid, max, atmid, atmax, rootsSection);
586 fillArray(rootsSection, roots);
587 break;
588 }
589 if (n1 == 0) {
590 min = mid;
591 } else {
592 max = mid;
593 }
594 }
595
596 if (its == MAXIT) {
597 for (n1 = atmax; n1 < atmin; n1++) {
598 roots[n1 - atmax] = mid;
599 }
600 }
601 }
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622 private void fillArray(final double[] valuesArray, final double[] targetArray) {
623 final int tarLen = targetArray.length;
624 final int valLen = valuesArray.length;
625 if (tarLen >= valLen) {
626 for (int tarIndex = tarLen - valLen, valIndex = 0;
627 tarIndex < tarLen;
628 tarIndex++, valIndex++) {
629 targetArray[tarIndex] = valuesArray[valIndex];
630 }
631 } else {
632 logger.info(" sbisect: length of values array is too long for the target array; not filling");
633 }
634 }
635
636
637
638
639
640
641
642
643
644 private double evalPoly(int ord, double[] coef, double x) {
645
646 double[] fp = coef;
647 double f = fp[ord];
648
649 int i = ord;
650 for (i--; i >= 0; i--) {
651 f = x * f + fp[i];
652 }
653 return f;
654 }
655
656
657
658
659
660
661
662
663
664
665
666
667 private boolean modifiedRegulaFalsi(int ord, double[] coef, double a, double b, double[] val) {
668 double fa = coef[ord];
669 double fb = fa;
670
671 for (int i = ord - 1; i >= 0; i--) {
672 fa = a * fa + coef[i];
673 fb = b * fb + coef[i];
674 }
675 if (fa * fb > 0.0) {
676 return false;
677 }
678 double lfx = fa;
679 for (int its = 0; its < MAX_ITER_SECANT; its++) {
680 double x = (fb * a - fa * b) / (fb - fa);
681
682 if (x < a || x > b) {
683 x = 0.5 * (a + b);
684 }
685 double fx = coef[ord];
686 for (int i = ord - 1; i >= 0; i--) {
687 fx = x * fx + coef[i];
688 }
689 if (abs(x) > RELERROR) {
690 if (abs(fx / x) < RELERROR) {
691 val[0] = x;
692 return true;
693 }
694 } else if (abs(fx) < RELERROR) {
695 val[0] = x;
696 return true;
697 }
698 if ((fa * fx) < 0) {
699 b = x;
700 fb = fx;
701 if ((lfx * fx) > 0) {
702 fa /= 2;
703 }
704 } else {
705 a = x;
706 fa = fx;
707 if ((lfx * fx) > 0) {
708 fb /= 2;
709 }
710 }
711
712 lfx = fx;
713 }
714
715 return false;
716 }
717
718
719 private static class Polynomial {
720
721 static final int MAX_ORDER = 16;
722
723 public final double[] coefficients;
724
725 public int order;
726
727
728 private Polynomial() {
729 coefficients = new double[MAX_ORDER + 1];
730 }
731 }
732 }