Mantid
Loading...
Searching...
No Matches
PolyBase.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7#include <algorithm>
8#include <cmath>
9#include <complex>
10#include <functional>
11#include <gsl/gsl_poly.h>
12#include <iterator>
13#include <vector>
14
17
18namespace Mantid::mathLevel {
19
20std::ostream &operator<<(std::ostream &OX, const PolyBase &A)
27{
28 A.write(OX);
29 return OX;
30}
31
32PolyBase::PolyBase(const int iD)
33 : iDegree((iD > 0) ? iD : 0), afCoeff(iDegree + 1), Eaccuracy(1e-6)
38{}
39
40PolyBase::PolyBase(const int iD, const double E)
41 : iDegree((iD > 0) ? iD : 0), afCoeff(iDegree + 1), Eaccuracy(fabs(E))
47{}
48
49void PolyBase::setDegree(const int iD)
54{
55 iDegree = (iD > 0) ? iD : 0;
56 afCoeff.resize(iDegree + 1);
57}
58
64{
65 return iDegree;
66}
67
68PolyBase::operator const std::vector<double> &() const
73{
74 return afCoeff;
75}
76
77PolyBase::operator std::vector<double> &()
82{
83 return afCoeff;
84}
85
86double PolyBase::operator[](const int i) const
92{
93 if (i > iDegree || i < 0)
94 throw Kernel::Exception::IndexError(i, iDegree + 1, "PolyBase::operator[] const");
95 return afCoeff[i];
96}
97
98double &PolyBase::operator[](const int i)
104{
105 if (i > iDegree || i < 0)
106 throw Kernel::Exception::IndexError(i, iDegree + 1, "PolyBase::operator[]");
107 return afCoeff[i];
108}
109
110double PolyBase::operator()(const double X) const
116{
117 double Result = afCoeff[iDegree];
118 for (int i = iDegree - 1; i >= 0; i--) {
119 Result *= X;
120 Result += afCoeff[i];
121 }
122 return Result;
123}
124
131{
132 iDegree = (iDegree > A.iDegree) ? iDegree : A.iDegree;
133 afCoeff.resize(iDegree + 1);
134 for (int i = 0; i <= iDegree && i <= A.iDegree; i++)
135 afCoeff[i] += A.afCoeff[i];
136 return *this;
137}
138
145{
146 iDegree = (iDegree > A.iDegree) ? iDegree : A.iDegree;
147 afCoeff.resize(iDegree + 1);
148 for (int i = 0; i <= iDegree && i <= A.iDegree; i++)
149 afCoeff[i] -= A.afCoeff[i];
150 return *this;
151}
152
159{
160 const int iD = iDegree + A.iDegree;
161 std::vector<double> CX(iD + 1, 0.0); // all set to zero
162 for (int i = 0; i <= iDegree; i++)
163 for (int j = 0; j <= A.iDegree; j++) {
164 const int cIndex = i + j;
165 CX[cIndex] += afCoeff[i] * A.afCoeff[j];
166 }
167 afCoeff = CX;
168 return *this;
169}
170
177{
178 PolyBase kSum(*this);
179 return kSum += A;
180}
181
188{
189 PolyBase kSum(*this);
190 return kSum -= A;
191}
192
199{
200 PolyBase kSum(*this);
201 return kSum *= A;
202}
203
204PolyBase PolyBase::operator+(const double V) const
210{
211 PolyBase kSum(*this);
212 return kSum += V;
213}
214
215PolyBase PolyBase::operator-(const double V) const
221{
222 PolyBase kSum(*this);
223 return kSum -= V;
224}
225
226PolyBase PolyBase::operator*(const double V) const
232{
233 PolyBase kSum(*this);
234 return kSum *= V;
235}
236
237PolyBase PolyBase::operator/(const double V) const
243{
244 PolyBase kSum(*this);
245 return kSum /= V;
246}
247
248// ---------------- SCALARS --------------------------
249
256{
257 afCoeff[0] += V; // There is always zero component
258 return *this;
259}
260
267{
268 afCoeff[0] -= V; // There is always zero component
269 return *this;
270}
271
278{
279 using std::placeholders::_1;
280 transform(afCoeff.begin(), afCoeff.end(), afCoeff.begin(), std::bind(std::multiplies<double>(), _1, V));
281 return *this;
282}
283
290{
291 using std::placeholders::_1;
292 transform(afCoeff.begin(), afCoeff.end(), afCoeff.begin(), std::bind(std::divides<double>(), _1, V));
293 return *this;
294}
295
301{
302 PolyBase KOut(*this);
303 KOut *= -1.0;
304 return KOut;
305}
306
312{
313 PolyBase KOut(*this);
314 return KOut.derivative();
315}
316
322{
323 if (iDegree < 1) {
324 afCoeff[0] = 0.0;
325 return *this;
326 }
327
328 for (int i = 0; i < iDegree; i++)
329 afCoeff[i] = afCoeff[i + 1] * (i + 1);
330 iDegree--;
331
332 return *this;
333}
334
340{
341 PolyBase InvPoly(iDegree);
342 for (int i = 0; i <= iDegree; i++)
343 InvPoly.afCoeff[i] = afCoeff[iDegree - i];
344 return InvPoly;
345}
346
347void PolyBase::compress(const double epsilon)
354{
355 const double eps((epsilon > 0.0) ? epsilon : Eaccuracy);
356 for (; iDegree >= 0 && fabs(afCoeff[iDegree]) <= eps; iDegree--)
357 ;
358
359 if (iDegree >= 0) {
360 const double Leading = afCoeff[iDegree];
361 afCoeff[iDegree] = 1.0; // avoid rounding issues
362 for (int i = 0; i < iDegree; i++)
363 afCoeff[i] /= Leading;
364 } else
365 iDegree = 0;
366
367 afCoeff.resize(iDegree + 1);
368}
369
370void PolyBase::divide(const PolyBase &pD, PolyBase &pQ, PolyBase &pR, const double epsilon) const
386{
387 const int iQuotDegree = iDegree - pD.iDegree;
388 if (iQuotDegree >= 0) {
389 pQ.setDegree(iQuotDegree);
390 // temporary storage for the remainder
391 pR = *this;
392
393 // do the division
394 double fInv = 1.0 / pD[pD.iDegree];
395 for (int iQ = iQuotDegree; iQ >= 0; iQ--) {
396 int iR = pD.iDegree + iQ;
397 pQ[iQ] = fInv * pR[iR];
398 for (iR--; iR >= iQ; iR--)
399 pR.afCoeff[iR] -= pQ[iQ] * pD[iR - iQ];
400 }
401
402 // calculate the correct degree for the remainder
403 pR.compress(epsilon);
404 return;
405 }
406
407 pQ.setDegree(0);
408 pQ[0] = 0.0;
409 pR = *this;
410}
411
412std::vector<double> PolyBase::realRoots(const double epsilon)
418{
419 const double eps((epsilon > 0.0) ? epsilon : Eaccuracy);
420 std::vector<std::complex<double>> Croots = calcRoots(epsilon);
421 std::vector<double> Out;
422 std::vector<std::complex<double>>::const_iterator vc;
423 for (vc = Croots.begin(); vc != Croots.end(); ++vc) {
424 if (fabs(vc->imag()) < eps)
425 Out.emplace_back(vc->real());
426 }
427 return Out;
428}
429
430std::vector<std::complex<double>> PolyBase::calcRoots(const double epsilon)
442{
443 compress(epsilon);
444 std::vector<std::complex<double>> Out(iDegree);
445 // Zero State:
446 if (iDegree == 0)
447 return Out;
448
449 // x+a_0 =0
450 if (iDegree == 1) {
451 Out[0] = std::complex<double>(-afCoeff[0]);
452 return Out;
453 }
454 // x^2+a_1 x+c = 0
455 if (iDegree == 2) {
456 solveQuadratic(Out[0], Out[1]);
457 return Out;
458 }
459
460 // x^3+a_2 x^2+ a_1 x+c=0
461 if (iDegree == 3) {
462 solveCubic(Out[0], Out[1], Out[2]);
463 return Out;
464 }
465 // THERE IS A QUARTIC / QUINTIC Solution availiable but...
466 // WS contains the the hessian matrix if required (eigenvalues/vectors)
467 //
468 gsl_poly_complex_workspace *WS(gsl_poly_complex_workspace_alloc(iDegree + 1));
469 auto RZ = new double[2 * (iDegree + 1)];
470 gsl_poly_complex_solve(&afCoeff.front(), iDegree + 1, WS, RZ);
471 for (int i = 0; i < iDegree; i++)
472 Out[i] = std::complex<double>(RZ[2 * i], RZ[2 * i + 1]);
473 gsl_poly_complex_workspace_free(WS);
474 delete[] RZ;
475 return Out;
476}
477
478int PolyBase::solveQuadratic(std::complex<double> &AnsA, std::complex<double> &AnsB) const
486{
487 const double b = afCoeff[1];
488 const double c = afCoeff[0];
489
490 const double cf = b * b - 4.0 * c;
491 if (cf >= 0) /* Real Roots */
492 {
493 const double q = (b >= 0) ? -0.5 * (b + sqrt(cf)) : -0.5 * (b - sqrt(cf));
494 AnsA = std::complex<double>(q, 0.0);
495 AnsB = std::complex<double>(c / q, 0.0);
496 return (cf == 0) ? 1 : 2;
497 }
498
499 std::complex<double> CQ(-0.5 * b, (b >= 0 ? -0.5 * sqrt(-cf) : 0.5 * sqrt(-cf)));
500 AnsA = CQ;
501 AnsB = c / CQ;
502 return 2;
503}
504
505int PolyBase::solveCubic(std::complex<double> &AnsA, std::complex<double> &AnsB, std::complex<double> &AnsC) const
515{
516 double q, r; /* solution parameters */
517 double termR, discrim;
518 double r13;
519 // std::pair<std::complex<double>,std::complex<double> > SQ;
520
521 const double b = afCoeff[2];
522 const double c = afCoeff[1];
523 const double d = afCoeff[0];
524
525 q = (3.0 * c - (b * b)) / 9.0; // -q
526 r = -27.0 * d + b * (9.0 * c - 2.0 * b * b); // -r
527 r /= 54.0;
528
529 discrim = q * q * q + r * r; // r^2-qq^3
530 /* The first root is always real. */
531 termR = (b / 3.0);
532
533 if (discrim > 1e-13) /* one root real, two are complex */
534 {
535 double s, t, termI;
536 s = r + sqrt(discrim);
537 s = ((s < 0) ? -pow(-s, (1.0 / 3.0)) : pow(s, (1.0 / 3.0)));
538 t = r - sqrt(discrim);
539 t = ((t < 0) ? -pow(-t, (1.0 / 3.0)) : pow(t, (1.0 / 3.0)));
540 AnsA = std::complex<double>(-termR + s + t, 0.0);
541 // second real point.
542 termR += (s + t) / 2.0;
543 termI = sqrt(3.0) * (-t + s) / 2;
544 AnsB = std::complex<double>(-termR, termI);
545 AnsC = std::complex<double>(-termR, -termI);
546 return 3;
547 }
548
549 /* The remaining options are all real */
550
551 if (discrim < 1e-13) // All roots real
552 {
553 double q3;
554 q = -q;
555 q3 = q * q * q;
556 q3 = acos(-r / sqrt(q3));
557 r13 = -2.0 * sqrt(q);
558 AnsA = std::complex<double>(-termR + r13 * cos(q3 / 3.0), 0.0);
559 AnsB = std::complex<double>(-termR + r13 * cos((q3 + 2.0 * M_PI) / 3.0), 0.0);
560 AnsC = std::complex<double>(-termR + r13 * cos((q3 - 2.0 * M_PI) / 3.0), 0.0);
561 return 3;
562 }
563
564 // Only option left is that all roots are real and unequal
565 // (to get here, q*q*q=r*r)
566
567 r13 = ((r < 0) ? -pow(-r, (1.0 / 3.0)) : pow(r, (1.0 / 3.0)));
568 AnsA = std::complex<double>(-termR + 2.0 * r13, 0.0);
569 AnsB = std::complex<double>(-(r13 + termR), 0.0);
570 AnsC = std::complex<double>(-(r13 + termR), 0.0);
571 return 2;
572}
573
574void PolyBase::write(std::ostream &OX) const
579{
580 copy(afCoeff.begin(), afCoeff.end(), std::ostream_iterator<double>(OX, " "));
581}
582
583} // namespace Mantid::mathLevel
#define fabs(x)
Definition: Matrix.cpp:22
Exception for index errors.
Definition: Exception.h:284
Holds a polynominal as a primary type.
Definition: PolyBase.h:31
PolyBase operator+(const PolyBase &) const
PolyBase addition.
Definition: PolyBase.cpp:171
void setDegree(int const)
Set the degree value.
Definition: PolyBase.cpp:49
int solveCubic(std::complex< double > &, std::complex< double > &, std::complex< double > &) const
Solves Cubic equation Compress MUST have been called.
Definition: PolyBase.cpp:505
std::vector< double > afCoeff
Coefficients.
Definition: PolyBase.h:34
double operator()(double const) const
Calculate polynomial at x.
Definition: PolyBase.cpp:110
PolyBase operator/(double const) const
PolyBase division.
Definition: PolyBase.cpp:237
double Eaccuracy
Polynomic accuracy.
Definition: PolyBase.h:35
PolyBase & derivative()
Take derivative of this polynomial.
Definition: PolyBase.cpp:317
int solveQuadratic(std::complex< double > &, std::complex< double > &) const
Solves Complex Quadratic component.
Definition: PolyBase.cpp:478
PolyBase GetInversion() const
Inversion of the coefficients.
Definition: PolyBase.cpp:335
PolyBase(int const)
Constructor.
Definition: PolyBase.cpp:32
void write(std::ostream &) const
Basic write command.
Definition: PolyBase.cpp:574
int iDegree
Degree of polynomial [0 == constant].
Definition: PolyBase.h:33
PolyBase & operator+=(const PolyBase &)
Self addition value.
Definition: PolyBase.cpp:125
PolyBase & operator-=(const PolyBase &)
Self addition value.
Definition: PolyBase.cpp:139
void divide(const PolyBase &, PolyBase &, PolyBase &, double const =-1.0) const
Carry out polynomial division of this / pD (Euclidean algorithm) If 'this' is P(t) and the divisor is...
Definition: PolyBase.cpp:370
PolyBase getDerivative() const
Take derivative.
Definition: PolyBase.cpp:307
int getDegree() const
Accessor to degree size.
Definition: PolyBase.cpp:59
std::vector< double > realRoots(double const =-1.0)
Get just the real roots.
Definition: PolyBase.cpp:412
double operator[](int const) const
Accessor to component.
Definition: PolyBase.cpp:86
PolyBase operator*(const PolyBase &) const
PolyBase multiplication.
Definition: PolyBase.cpp:193
PolyBase & operator/=(double const)
PolyBase division scalar.
Definition: PolyBase.cpp:284
PolyBase operator-() const
PolyBase negation operator.
Definition: PolyBase.cpp:296
PolyBase & operator*=(const PolyBase &)
Self multiplication value.
Definition: PolyBase.cpp:153
void compress(double const)
Two part process remove coefficients of zero or near zero: Reduce degree by eliminating all (nearly) ...
Definition: PolyBase.cpp:347
std::vector< std::complex< double > > calcRoots(double const =-1.0)
Calculate all the roots of the polynominal.
Definition: PolyBase.cpp:430
std::ostream & operator<<(std::ostream &, const PolyBase &)
External Friend :: outputs point to a stream.
Definition: PolyBase.cpp:20