OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
Polynomial.hpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the following paper when you publish your work:
33 *
34 * [1] Drisko et al., J. Open Source Softw. 9, 7004 (2024).
35 *
36 * Good starting points for code and simulation methodology are:
37 *
38 * [2] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
39 * [3] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
40 * [4] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
41 * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
42 * [6] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
43 * [7] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
44 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
45 * [9] Drisko & Gezelter, J. Chem. Theory Comput. 20, 4986-4997 (2024).
46 */
47
48/**
49 * @file Polynomial.hpp
50 * @author teng lin
51 * @date 11/16/2004
52 * @version 1.0
53 */
54
55#ifndef MATH_POLYNOMIAL_HPP
56#define MATH_POLYNOMIAL_HPP
57
58#include <config.h>
59
60#include <complex>
61#include <iostream>
62#include <list>
63#include <map>
64#include <utility>
65
66#include "math/Eigenvalue.hpp"
67
68namespace OpenMD {
69
70 template<typename Real>
71 Real fastpow(Real x, int N) {
72 Real result(1); // or 1.0?
73
74 for (int i = 0; i < N; ++i) {
75 result *= x;
76 }
77
78 return result;
79 }
80
81 /**
82 * @class Polynomial Polynomial.hpp "math/Polynomial.hpp"
83 * A generic Polynomial class
84 */
85 template<typename Real>
86 class Polynomial {
87 public:
88 using PolynomialType = Polynomial<Real>;
89 using ExponentType = int;
90 using CoefficientType = Real;
91 using PolynomialPairMap = std::map<ExponentType, CoefficientType>;
92 using iterator = typename PolynomialPairMap::iterator;
93 using const_iterator = typename PolynomialPairMap::const_iterator;
94
95 Polynomial() {}
96 Polynomial(Real v) { setCoefficient(0, v); }
97
98 /**
99 * Calculates the value of this Polynomial evaluated at the given x value.
100 * @return The value of this Polynomial evaluates at the given x value
101 * @param x the value of the independent variable for this
102 * Polynomial function
103 */
104 Real evaluate(const Real& x) {
105 Real result = Real();
106 ExponentType exponent;
107 CoefficientType coefficient;
108
109 for (iterator i = polyPairMap_.begin(); i != polyPairMap_.end(); ++i) {
110 exponent = i->first;
111 coefficient = i->second;
112 result += fastpow(x, exponent) * coefficient;
113 }
114
115 return result;
116 }
117
118 /**
119 * Returns the first derivative of this polynomial.
120 * @return the first derivative of this polynomial
121 * @param x
122 */
123 Real evaluateDerivative(const Real& x) {
124 Real result = Real();
125 ExponentType exponent;
126 CoefficientType coefficient;
127
128 for (iterator i = polyPairMap_.begin(); i != polyPairMap_.end(); ++i) {
129 exponent = i->first;
130 coefficient = i->second;
131 result += fastpow(x, exponent - 1) * coefficient * exponent;
132 }
133
134 return result;
135 }
136
137 /**
138 * Set the coefficent of the specified exponent, if the
139 * coefficient is already there, it will be overwritten.
140 * @param exponent exponent of a term in this Polynomial
141 * @param coefficient multiplier of a term in this Polynomial
142 */
143 void setCoefficient(int exponent, const Real& coefficient) {
144 polyPairMap_[exponent] = coefficient;
145 }
146
147 /**
148 * Set the coefficent of the specified exponent. If the
149 * coefficient is already there, just add the new coefficient to
150 * the old one, otherwise, just call setCoefficent
151 * @param exponent exponent of a term in this Polynomial
152 * @param coefficient multiplier of a term in this Polynomial
153 */
154 void addCoefficient(int exponent, const Real& coefficient) {
155 iterator i = polyPairMap_.find(exponent);
156
157 if (i != end()) {
158 i->second += coefficient;
159 } else {
160 setCoefficient(exponent, coefficient);
161 }
162 }
163
164 /**
165 * Returns the coefficient associated with the given power for
166 * this Polynomial.
167 * @return the coefficient associated with the given power for
168 * this Polynomial
169 * @param exponent exponent of any term in this Polynomial
170 */
171 Real getCoefficient(ExponentType exponent) {
172 iterator i = polyPairMap_.find(exponent);
173
174 if (i != end()) {
175 return i->second;
176 } else {
177 return Real(0);
178 }
179 }
180
181 iterator begin() { return polyPairMap_.begin(); }
182 const_iterator begin() const { return polyPairMap_.begin(); }
183
184 iterator end() { return polyPairMap_.end(); }
185 const_iterator end() const { return polyPairMap_.end(); }
186
187 iterator find(ExponentType exponent) { return polyPairMap_.find(exponent); }
188
189 size_t size() { return polyPairMap_.size(); }
190
191 int degree() {
192 int deg = 0;
193 for (iterator i = polyPairMap_.begin(); i != polyPairMap_.end(); ++i) {
194 if (i->first > deg) deg = i->first;
195 }
196 return deg;
197 }
198
199 PolynomialType& operator+=(const PolynomialType& p) {
200 typename Polynomial<Real>::const_iterator i;
201
202 for (i = p.begin(); i != p.end(); ++i) {
203 this->addCoefficient(i->first, i->second);
204 }
205
206 return *this;
207 }
208
209 PolynomialType& operator-=(const PolynomialType& p) {
210 typename Polynomial<Real>::const_iterator i;
211 for (i = p.begin(); i != p.end(); ++i) {
212 this->addCoefficient(i->first, -i->second);
213 }
214 return *this;
215 }
216
217 PolynomialType& operator*=(const PolynomialType& p) {
218 typename Polynomial<Real>::const_iterator i;
219 typename Polynomial<Real>::const_iterator j;
220 Polynomial<Real> p2(*this);
221
222 polyPairMap_.clear(); // clear out old map
223 for (i = p2.begin(); i != p2.end(); ++i) {
224 for (j = p.begin(); j != p.end(); ++j) {
225 this->addCoefficient(i->first + j->first, i->second * j->second);
226 }
227 }
228 return *this;
229 }
230
231 PolynomialType& operator*=(const Real v) {
232 typename Polynomial<Real>::const_iterator i;
233 // Polynomial<Real> result;
234
235 for (i = this->begin(); i != this->end(); ++i) {
236 this->setCoefficient(i->first, i->second * v);
237 }
238
239 return *this;
240 }
241
242 PolynomialType& operator+=(const Real v) {
243 this->addCoefficient(0, v);
244 return *this;
245 }
246
247 /**
248 * Returns the first derivative of this polynomial.
249 * @return the first derivative of this polynomial
250 */
251 PolynomialType* getDerivative() {
252 Polynomial<Real>* p = new Polynomial<Real>();
253
254 typename Polynomial<Real>::const_iterator i;
255 ExponentType exponent;
256 CoefficientType coefficient;
257
258 for (i = this->begin(); i != this->end(); ++i) {
259 exponent = i->first;
260 coefficient = i->second;
261 p->setCoefficient(exponent - 1, coefficient * exponent);
262 }
263
264 return p;
265 }
266
267 // Creates the Companion matrix for a given polynomial
268 DynamicRectMatrix<Real> CreateCompanion() {
269 int rank = degree();
270 DynamicRectMatrix<Real> mat(rank, rank);
271 Real majorCoeff = getCoefficient(rank);
272 for (int i = 0; i < rank; ++i) {
273 for (int j = 0; j < rank; ++j) {
274 if (i - j == 1) {
275 mat(i, j) = 1;
276 } else if (j == rank - 1) {
277 mat(i, j) = -1 * getCoefficient(i) / majorCoeff;
278 }
279 }
280 }
281 return mat;
282 }
283
284 // Find the Roots of a given polynomial
285 std::vector<std::complex<Real>> FindRoots() {
286 int rank = degree();
287 DynamicRectMatrix<Real> companion = CreateCompanion();
288 JAMA::Eigenvalue<Real> eig(companion);
289 DynamicVector<Real> reals, imags;
290 eig.getRealEigenvalues(reals);
291 eig.getImagEigenvalues(imags);
292
293 std::vector<std::complex<Real>> roots;
294 for (int i = 0; i < rank; i++) {
295 roots.push_back(std::complex<Real>(reals(i), imags(i)));
296 }
297
298 return roots;
299 }
300
301 std::vector<Real> FindRealRoots() {
302 const Real fEpsilon = 1.0e-8;
303 std::vector<Real> roots;
304 roots.clear();
305
306 const int deg = degree();
307
308 switch (deg) {
309 case 1: {
310 Real fC1 = getCoefficient(1);
311 Real fC0 = getCoefficient(0);
312 roots.push_back(-fC0 / fC1);
313 return roots;
314 }
315 case 2: {
316 Real fC2 = getCoefficient(2);
317 Real fC1 = getCoefficient(1);
318 Real fC0 = getCoefficient(0);
319 Real fDiscr = fC1 * fC1 - 4.0 * fC0 * fC2;
320 if (std::abs(fDiscr) <= fEpsilon) { fDiscr = (Real)0.0; }
321
322 if (fDiscr < (Real)0.0) { // complex roots only
323 return roots;
324 }
325
326 Real fTmp = ((Real)0.5) / fC2;
327
328 if (fDiscr > (Real)0.0) { // 2 real roots
329 fDiscr = std::sqrt(fDiscr);
330 roots.push_back(fTmp * (-fC1 - fDiscr));
331 roots.push_back(fTmp * (-fC1 + fDiscr));
332 } else {
333 roots.push_back(-fTmp * fC1); // 1 real root
334 }
335 }
336 return roots;
337 case 3: {
338 Real fC3 = getCoefficient(3);
339 Real fC2 = getCoefficient(2);
340 Real fC1 = getCoefficient(1);
341 Real fC0 = getCoefficient(0);
342
343 // make polynomial monic, x^3+c2*x^2+c1*x+c0
344 Real fInvC3 = ((Real)1.0) / fC3;
345 fC0 *= fInvC3;
346 fC1 *= fInvC3;
347 fC2 *= fInvC3;
348
349 // convert to y^3+a*y+b = 0 by x = y-c2/3
350 const Real fThird = (Real)1.0 / (Real)3.0;
351 const Real fTwentySeventh = (Real)1.0 / (Real)27.0;
352 Real fOffset = fThird * fC2;
353 Real fA = fC1 - fC2 * fOffset;
354 Real fB = fC0 + fC2 * (((Real)2.0) * fC2 * fC2 - ((Real)9.0) * fC1) *
355 fTwentySeventh;
356 Real fHalfB = ((Real)0.5) * fB;
357
358 Real fDiscr = fHalfB * fHalfB + fA * fA * fA * fTwentySeventh;
359 if (std::abs(fDiscr) <= fEpsilon) { fDiscr = (Real)0.0; }
360
361 if (fDiscr > (Real)0.0) { // 1 real, 2 complex roots
362
363 fDiscr = std::sqrt(fDiscr);
364 Real fTemp = -fHalfB + fDiscr;
365 Real root;
366 if (fTemp >= (Real)0.0) {
367 root = std::pow(fTemp, fThird);
368 } else {
369 root = -std::pow(-fTemp, fThird);
370 }
371 fTemp = -fHalfB - fDiscr;
372 if (fTemp >= (Real)0.0) {
373 root += std::pow(fTemp, fThird);
374 } else {
375 root -= std::pow(-fTemp, fThird);
376 }
377 root -= fOffset;
378
379 roots.push_back(root);
380 } else if (fDiscr < (Real)0.0) {
381 const Real fSqrt3 = std::sqrt((Real)3.0);
382 Real fDist = std::sqrt(-fThird * fA);
383 Real fAngle = fThird * std::atan2(std::sqrt(-fDiscr), -fHalfB);
384 Real fCos = cos(fAngle);
385 Real fSin = sin(fAngle);
386 roots.push_back(((Real)2.0) * fDist * fCos - fOffset);
387 roots.push_back(-fDist * (fCos + fSqrt3 * fSin) - fOffset);
388 roots.push_back(-fDist * (fCos - fSqrt3 * fSin) - fOffset);
389 } else {
390 Real fTemp;
391 if (fHalfB >= (Real)0.0) {
392 fTemp = -std::pow(fHalfB, fThird);
393 } else {
394 fTemp = std::pow(-fHalfB, fThird);
395 }
396 roots.push_back(((Real)2.0) * fTemp - fOffset);
397 roots.push_back(-fTemp - fOffset);
398 roots.push_back(-fTemp - fOffset);
399 }
400 }
401 return roots;
402 case 4: {
403 Real fC4 = getCoefficient(4);
404 Real fC3 = getCoefficient(3);
405 Real fC2 = getCoefficient(2);
406 Real fC1 = getCoefficient(1);
407 Real fC0 = getCoefficient(0);
408
409 // make polynomial monic, x^4+c3*x^3+c2*x^2+c1*x+c0
410 Real fInvC4 = ((Real)1.0) / fC4;
411 fC0 *= fInvC4;
412 fC1 *= fInvC4;
413 fC2 *= fInvC4;
414 fC3 *= fInvC4;
415
416 // reduction to resolvent cubic polynomial y^3+r2*y^2+r1*y+r0 = 0
417 Real fR0 = -fC3 * fC3 * fC0 + ((Real)4.0) * fC2 * fC0 - fC1 * fC1;
418 Real fR1 = fC3 * fC1 - ((Real)4.0) * fC0;
419 Real fR2 = -fC2;
420 Polynomial<Real> tempCubic;
421 tempCubic.setCoefficient(0, fR0);
422 tempCubic.setCoefficient(1, fR1);
423 tempCubic.setCoefficient(2, fR2);
424 tempCubic.setCoefficient(3, 1.0);
425 std::vector<Real> cubeRoots = tempCubic.FindRealRoots(); // always
426 // produces
427 // at
428 // least
429 // one
430 // root
431 Real fY = cubeRoots[0];
432
433 Real fDiscr = ((Real)0.25) * fC3 * fC3 - fC2 + fY;
434 if (std::abs(fDiscr) <= fEpsilon) { fDiscr = (Real)0.0; }
435
436 if (fDiscr > (Real)0.0) {
437 Real fR = std::sqrt(fDiscr);
438 Real fT1 = ((Real)0.75) * fC3 * fC3 - fR * fR - ((Real)2.0) * fC2;
439 Real fT2 =
440 (((Real)4.0) * fC3 * fC2 - ((Real)8.0) * fC1 - fC3 * fC3 * fC3) /
441 (((Real)4.0) * fR);
442
443 Real fTplus = fT1 + fT2;
444 Real fTminus = fT1 - fT2;
445 if (std::abs(fTplus) <= fEpsilon) { fTplus = (Real)0.0; }
446 if (std::abs(fTminus) <= fEpsilon) { fTminus = (Real)0.0; }
447
448 if (fTplus >= (Real)0.0) {
449 Real fD = std::sqrt(fTplus);
450 roots.push_back(-((Real)0.25) * fC3 + ((Real)0.5) * (fR + fD));
451 roots.push_back(-((Real)0.25) * fC3 + ((Real)0.5) * (fR - fD));
452 }
453 if (fTminus >= (Real)0.0) {
454 Real fE = std::sqrt(fTminus);
455 roots.push_back(-((Real)0.25) * fC3 + ((Real)0.5) * (fE - fR));
456 roots.push_back(-((Real)0.25) * fC3 - ((Real)0.5) * (fE + fR));
457 }
458 } else if (fDiscr < (Real)0.0) {
459 // roots.clear();
460 } else {
461 Real fT2 = fY * fY - ((Real)4.0) * fC0;
462 if (fT2 >= -fEpsilon) {
463 if (fT2 < (Real)0.0) { // round to zero
464 fT2 = (Real)0.0;
465 }
466 fT2 = ((Real)2.0) * std::sqrt(fT2);
467 Real fT1 = ((Real)0.75) * fC3 * fC3 - ((Real)2.0) * fC2;
468 if (fT1 + fT2 >= fEpsilon) {
469 Real fD = std::sqrt(fT1 + fT2);
470 roots.push_back(-((Real)0.25) * fC3 + ((Real)0.5) * fD);
471 roots.push_back(-((Real)0.25) * fC3 - ((Real)0.5) * fD);
472 }
473 if (fT1 - fT2 >= fEpsilon) {
474 Real fE = std::sqrt(fT1 - fT2);
475 roots.push_back(-((Real)0.25) * fC3 + ((Real)0.5) * fE);
476 roots.push_back(-((Real)0.25) * fC3 - ((Real)0.5) * fE);
477 }
478 }
479 }
480 }
481 return roots;
482 default: {
483 DynamicRectMatrix<Real> companion = CreateCompanion();
484 JAMA::Eigenvalue<Real> eig(companion);
485 DynamicVector<Real> reals, imags;
486 eig.getRealEigenvalues(reals);
487 eig.getImagEigenvalues(imags);
488
489 for (int i = 0; i < deg; i++) {
490 if (std::abs(imags(i)) < fEpsilon) roots.push_back(reals(i));
491 }
492 }
493 return roots;
494 }
495 }
496
497 private:
498 PolynomialPairMap polyPairMap_;
499 };
500
501 /**
502 * Generates and returns the product of two given Polynomials.
503 * @return A Polynomial containing the product of the two given Polynomial
504 * parameters
505 */
506 template<typename Real>
508 const Polynomial<Real>& p2) {
509 typename Polynomial<Real>::const_iterator i;
510 typename Polynomial<Real>::const_iterator j;
512
513 for (i = p1.begin(); i != p1.end(); ++i) {
514 for (j = p2.begin(); j != p2.end(); ++j) {
515 p.addCoefficient(i->first + j->first, i->second * j->second);
516 }
517 }
518
519 return p;
520 }
521
522 template<typename Real>
523 Polynomial<Real> operator*(const Polynomial<Real>& p, const Real v) {
524 typename Polynomial<Real>::const_iterator i;
525 Polynomial<Real> result;
526
527 for (i = p.begin(); i != p.end(); ++i) {
528 result.setCoefficient(i->first, i->second * v);
529 }
530
531 return result;
532 }
533
534 template<typename Real>
535 Polynomial<Real> operator*(const Real v, const Polynomial<Real>& p) {
536 typename Polynomial<Real>::const_iterator i;
537 Polynomial<Real> result;
538
539 for (i = p.begin(); i != p.end(); ++i) {
540 result.setCoefficient(i->first, i->second * v);
541 }
542
543 return result;
544 }
545
546 /**
547 * Generates and returns the sum of two given Polynomials.
548 * @param p1 the first polynomial
549 * @param p2 the second polynomial
550 */
551 template<typename Real>
553 const Polynomial<Real>& p2) {
554 Polynomial<Real> p(p1);
555
556 typename Polynomial<Real>::const_iterator i;
557
558 for (i = p2.begin(); i != p2.end(); ++i) {
559 p.addCoefficient(i->first, i->second);
560 }
561
562 return p;
563 }
564
565 /**
566 * Generates and returns the difference of two given Polynomials.
567 * @return
568 * @param p1 the first polynomial
569 * @param p2 the second polynomial
570 */
571 template<typename Real>
573 const Polynomial<Real>& p2) {
574 Polynomial<Real> p(p1);
575
576 typename Polynomial<Real>::const_iterator i;
577
578 for (i = p2.begin(); i != p2.end(); ++i) {
579 p.addCoefficient(i->first, -i->second);
580 }
581
582 return p;
583 }
584
585 /**
586 * Returns the first derivative of this polynomial.
587 * @return the first derivative of this polynomial
588 */
589 template<typename Real>
592
593 typename Polynomial<Real>::const_iterator i;
594 int exponent;
595 Real coefficient;
596
597 for (i = p1.begin(); i != p1.end(); ++i) {
598 exponent = i->first;
599 coefficient = i->second;
600 p->setCoefficient(exponent - 1, coefficient * exponent);
601 }
602
603 return p;
604 }
605
606 /**
607 * Tests if two polynomial have the same exponents
608 * @return true if all of the exponents in these Polynomial are identical
609 * @param p1 the first polynomial
610 * @param p2 the second polynomial
611 * @note this function does not compare the coefficient
612 */
613 template<typename Real>
614 bool equal(const Polynomial<Real>& p1, const Polynomial<Real>& p2) {
615 typename Polynomial<Real>::const_iterator i;
616 typename Polynomial<Real>::const_iterator j;
617
618 if (p1.size() != p2.size()) { return false; }
619
620 for (i = p1.begin(), j = p2.begin(); i != p1.end() && j != p2.end();
621 ++i, ++j) {
622 if (i->first != j->first) { return false; }
623 }
624
625 return true;
626 }
627
628 using DoublePolynomial = Polynomial<RealType>;
629} // namespace OpenMD
630
631#endif // MATH_POLYNOMIAL_HPP
Rectangular matrix class with contiguous flat storage.
A generic Polynomial class.
void addCoefficient(int exponent, const Real &coefficient)
Set the coefficent of the specified exponent.
Real evaluateDerivative(const Real &x)
Returns the first derivative of this polynomial.
Real getCoefficient(ExponentType exponent)
Returns the coefficient associated with the given power for this Polynomial.
Real evaluate(const Real &x)
Calculates the value of this Polynomial evaluated at the given x value.
PolynomialType * getDerivative()
Returns the first derivative of this polynomial.
void setCoefficient(int exponent, const Real &coefficient)
Set the coefficent of the specified exponent, if the coefficient is already there,...
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
DynamicRectMatrix< Real > operator-(const DynamicRectMatrix< Real > &m)
Negate the value of every element of this matrix.
bool equal(const Polynomial< Real > &p1, const Polynomial< Real > &p2)
Tests if two polynomial have the same exponents.
Polynomial< Real > * getDerivative(const Polynomial< Real > &p1)
Returns the first derivative of this polynomial.
DynamicRectMatrix< Real > operator*(const DynamicRectMatrix< Real > &m, Real s)
Return the multiplication of scalar and matrix (m * s).