NumCpp  2.12.1
A Templatized Header Only C++ Implementation of the Python NumPy Library
Poly1d.hpp
Go to the documentation of this file.
1
28#pragma once
29
30#include <iostream>
31#include <numeric>
32#include <string>
33#include <type_traits>
34#include <utility>
35#include <vector>
36
38#include "NumCpp/Core/Enums.hpp"
42#include "NumCpp/Core/Types.hpp"
44#include "NumCpp/Linalg/inv.hpp"
45#include "NumCpp/NdArray.hpp"
49
50namespace nc::polynomial
51{
52 //================================================================================
56 template<typename dtype>
57 class Poly1d
58 {
59 private:
60 STATIC_ASSERT_ARITHMETIC(dtype);
61
62 public:
63 //============================================================================
64 // Method Description:
68 Poly1d() = default;
69
70 //============================================================================
71 // Method Description:
78 Poly1d(const NdArray<dtype>& inValues, IsRoots isRoots = IsRoots::NO)
79 {
80 if (inValues.size() > DtypeInfo<uint8>::max())
81 {
82 THROW_INVALID_ARGUMENT_ERROR("can only make a polynomial of order " +
84 }
85
86 if (isRoots == IsRoots::YES)
87 {
88 coefficients_.push_back(1);
89 for (auto value : inValues)
90 {
91 NdArray<dtype> coeffs = { -(value), static_cast<dtype>(1) };
92 *this *= Poly1d<dtype>(coeffs, IsRoots::NO);
93 }
94 }
95 else
96 {
97 coefficients_.resize(inValues.size());
98 stl_algorithms::copy(inValues.begin(), inValues.end(), coefficients_.begin());
99 }
100 }
101
102 //============================================================================
103 // Method Description:
110 [[nodiscard]] double area(double a, double b) const
111 {
112 if (a > b)
113 {
114 std::swap(a, b);
115 }
116
117 auto polyIntegral = integ();
118 return polyIntegral(b) - polyIntegral(a);
119 }
120
121 //============================================================================
122 // Method Description:
127 template<typename dtypeOut>
129 {
130 auto newCoefficients = NdArray<dtypeOut>(1, static_cast<uint32>(coefficients_.size()));
131
132 const auto function = [](dtype value) -> dtypeOut { return static_cast<dtypeOut>(value); };
133
134 stl_algorithms::transform(coefficients_.begin(), coefficients_.end(), newCoefficients.begin(), function);
135
136 return Poly1d<dtypeOut>(newCoefficients);
137 }
138
139 //============================================================================
140 // Method Description:
145 [[nodiscard]] NdArray<dtype> coefficients() const
146 {
147 auto coefficientsCopy = coefficients_;
148 return NdArray<dtype>(coefficientsCopy);
149 }
150
151 //============================================================================
152 // Method Description:
156 [[nodiscard]] Poly1d<dtype> deriv() const
157 {
158 const auto numCoefficients = static_cast<uint32>(coefficients_.size());
159 if (numCoefficients == 0)
160 {
161 return {};
162 }
163 if (numCoefficients == 1)
164 {
165 return Poly1d<dtype>({ 0 });
166 }
167
168 NdArray<dtype> derivativeCofficients(1, numCoefficients - 1);
169
170 uint32 counter = 0;
171 for (uint32 i = 1; i < numCoefficients; ++i)
172 {
173 derivativeCofficients[counter++] = coefficients_[i] * i;
174 }
175
176 return Poly1d<dtype>(derivativeCofficients);
177 }
178
179 //============================================================================
180 // Method Description:
186 dtype eval(dtype xValue) const noexcept
187 {
188 return operator()(xValue);
189 }
190
191 //============================================================================
192 // Method Description:
198 NdArray<dtype> eval(const NdArray<dtype>& xValues) const noexcept
199 {
200 return operator()(xValues);
201 }
202
203 //============================================================================
204 // Method Description:
211 static Poly1d<double> fit(const NdArray<dtype>& xValues, const NdArray<dtype>& yValues, uint8 polyOrder)
212 {
213 const auto numMeasurements = xValues.size();
214
215 if (yValues.size() != numMeasurements)
216 {
217 THROW_INVALID_ARGUMENT_ERROR("Input x and y arrays must be of equal size.");
218 }
219
220 if (!xValues.isflat())
221 {
222 THROW_INVALID_ARGUMENT_ERROR("Input x must be a flattened [1, n] or [n, 1] array.");
223 }
224
225 if (!yValues.isflat())
226 {
227 THROW_INVALID_ARGUMENT_ERROR("Input y must be a flattened [n, 1] array.");
228 }
229
230 NdArray<double> a(numMeasurements, polyOrder + 1);
231 for (uint32 measIdx = 0; measIdx < numMeasurements; ++measIdx)
232 {
233 const auto xDouble = static_cast<double>(xValues[measIdx]);
234 for (uint8 order = 0; order < a.numCols(); ++order)
235 {
236 a(measIdx, order) = utils::power(xDouble, order);
237 }
238 }
239
240 NdArray<double> aInv;
241 if (a.issquare())
242 {
243 aInv = linalg::inv(a);
244 }
245 else
246 {
247 // psuedo-inverse
248 auto aT = a.transpose();
249 auto aTaInv = linalg::inv(aT.dot(a));
250 aInv = aTaInv.dot(aT);
251 }
252
253 auto x = aInv.dot(yValues.template astype<double>());
254 return Poly1d<double>(x);
255 }
256
257 //============================================================================
258 // Method Description:
266 static Poly1d<double> fit(const NdArray<dtype>& xValues,
267 const NdArray<dtype>& yValues,
268 const NdArray<dtype>& weights,
269 uint8 polyOrder)
270 {
271 const auto numMeasurements = xValues.size();
272
273 if (yValues.size() != numMeasurements)
274 {
275 THROW_INVALID_ARGUMENT_ERROR("Input x and y arrays must be of equal size.");
276 }
277
278 if (weights.size() != numMeasurements)
279 {
280 THROW_INVALID_ARGUMENT_ERROR("Input x and weights arrays must be of equal size.");
281 }
282
283 if (!xValues.isflat())
284 {
285 THROW_INVALID_ARGUMENT_ERROR("Input x must be a flattened [1, n] or [n, 1] array.");
286 }
287
288 if (!yValues.isflat())
289 {
290 THROW_INVALID_ARGUMENT_ERROR("Input y must be a flattened [n, 1] array.");
291 }
292
293 if (!weights.isflat())
294 {
295 THROW_INVALID_ARGUMENT_ERROR("Input weights must be a flattened [1, n] or [n, 1] array.");
296 }
297
298 NdArray<double> a(numMeasurements, polyOrder + 1);
299 for (uint32 measIdx = 0; measIdx < numMeasurements; ++measIdx)
300 {
301 const auto xDouble = static_cast<double>(xValues[measIdx]);
302 for (uint8 order = 0; order < a.numCols(); ++order)
303 {
304 a(measIdx, order) = utils::power(xDouble, order);
305 }
306 }
307
308 NdArray<double> aWeighted(a.shape());
309 NdArray<double> yWeighted(yValues.shape());
310
311 for (uint32 measIdx = 0; measIdx < numMeasurements; ++measIdx)
312 {
313 const auto weight = static_cast<double>(weights[measIdx]);
314
315 yWeighted[measIdx] = yValues[measIdx] * weight;
316 for (uint8 order = 0; order < a.numCols(); ++order)
317 {
318 aWeighted(measIdx, order) = a(measIdx, order) * weight;
319 }
320 }
321
322 NdArray<double> aInv;
323 if (aWeighted.issquare())
324 {
325 aInv = linalg::inv(aWeighted);
326 }
327 else
328 {
329 // psuedo-inverse
330 auto aT = a.transpose();
331 auto aTaInv = linalg::inv(aT.dot(aWeighted));
332 aInv = aTaInv.dot(aT);
333 }
334
335 auto x = aInv.dot(yWeighted);
336 return Poly1d<double>(x); // NOLINT(modernize-return-braced-init-list)
337 }
338
339 //============================================================================
340 // Method Description:
344 [[nodiscard]] Poly1d<double> integ() const
345 {
346 const auto numCoefficients = static_cast<uint32>(coefficients_.size());
347 if (numCoefficients == 0)
348 {
349 return {};
350 }
351
352 NdArray<double> integralCofficients(1, numCoefficients + 1);
353 integralCofficients[0] = 0.;
354
355 for (uint32 i = 0; i < numCoefficients; ++i)
356 {
357 integralCofficients[i + 1] = static_cast<double>(coefficients_[i]) / static_cast<double>(i + 1);
358 }
359
360 return Poly1d<double>(integralCofficients); // NOLINT(modernize-return-braced-init-list)
361 }
362
363 //============================================================================
364 // Method Description:
369 [[nodiscard]] uint32 order() const noexcept
370 {
371 return static_cast<uint32>(coefficients_.size() - 1);
372 }
373
374 //============================================================================
375 // Method Description:
379 void print() const
380 {
381 std::cout << *this << std::endl;
382 }
383
384 //============================================================================
385 // Method Description:
390 [[nodiscard]] std::string str() const
391 {
392 const auto numCoeffients = static_cast<uint32>(coefficients_.size());
393
394 std::string repr = "Poly1d<";
395 uint32 power = 0;
396 for (auto& coefficient : coefficients_)
397 {
398 if (utils::essentiallyEqual(coefficient, static_cast<dtype>(0)))
399 {
400 ++power;
401 continue;
402 }
403
404 repr += utils::num2str(coefficient);
405
406 if (power > 1)
407 {
408 repr += "x^" + utils::num2str(power);
409 }
410 else if (power == 1)
411 {
412 repr += "x";
413 }
414
415 ++power;
416
417 if (power < numCoeffients)
418 {
419 repr += " + ";
420 }
421 }
422
423 return repr + ">";
424 }
425
426 //============================================================================
427 // Method Description:
433 dtype operator()(dtype inValue) const noexcept
434 {
435 uint8 power = 0;
436 return std::accumulate(coefficients_.begin(),
437 coefficients_.end(),
438 dtype{ 0 },
439 [&power, inValue](dtype polyValue, const auto& coefficient) noexcept -> dtype
440 { return polyValue + coefficient * utils::power(inValue, power++); });
441 }
442
443 //============================================================================
444 // Method Description:
450 NdArray<dtype> operator()(const NdArray<dtype>& xValues) const noexcept
451 {
452 NdArray<dtype> returnArray(xValues.shape());
453
454 stl_algorithms::transform(xValues.begin(),
455 xValues.end(),
456 returnArray.begin(),
457 [this](const auto xValue) { return this->operator()(xValue); });
458 return returnArray;
459 }
460
461 //============================================================================
462 // Method Description:
468 Poly1d<dtype> operator+(const Poly1d<dtype>& inOtherPoly) const
469 {
470 return Poly1d<dtype>(*this) += inOtherPoly;
471 }
472
473 //============================================================================
474 // Method Description:
481 {
482 if (this->coefficients_.size() < inOtherPoly.coefficients_.size())
483 {
484 for (size_t i = 0; i < coefficients_.size(); ++i)
485 {
486 coefficients_[i] += inOtherPoly.coefficients_[i];
487 }
488 for (size_t i = coefficients_.size(); i < inOtherPoly.coefficients_.size(); ++i)
489 {
490 coefficients_.push_back(inOtherPoly.coefficients_[i]);
491 }
492 }
493 else
494 {
495 for (size_t i = 0; i < inOtherPoly.coefficients_.size(); ++i)
496 {
497 coefficients_[i] += inOtherPoly.coefficients_[i];
498 }
499 }
500
501 return *this;
502 }
503
504 //============================================================================
505 // Method Description:
511 Poly1d<dtype> operator-(const Poly1d<dtype>& inOtherPoly) const
512 {
513 return Poly1d<dtype>(*this) -= inOtherPoly;
514 }
515
516 //============================================================================
517 // Method Description:
524 {
525 if (this->coefficients_.size() < inOtherPoly.coefficients_.size())
526 {
527 for (size_t i = 0; i < coefficients_.size(); ++i)
528 {
529 coefficients_[i] -= inOtherPoly.coefficients_[i];
530 }
531 for (size_t i = coefficients_.size(); i < inOtherPoly.coefficients_.size(); ++i)
532 {
533 coefficients_.push_back(-inOtherPoly.coefficients_[i]);
534 }
535 }
536 else
537 {
538 for (size_t i = 0; i < inOtherPoly.coefficients_.size(); ++i)
539 {
540 coefficients_[i] -= inOtherPoly.coefficients_[i];
541 }
542 }
543
544 return *this;
545 }
546
547 //============================================================================
548 // Method Description:
554 Poly1d<dtype> operator*(const Poly1d<dtype>& inOtherPoly) const
555 {
556 return Poly1d<dtype>(*this) *= inOtherPoly;
557 }
558
559 //============================================================================
560 // Method Description:
567 {
568 const uint32 finalCoefficientsSize = order() + inOtherPoly.order() + 1;
569 std::vector<dtype> coeffsA(finalCoefficientsSize, 0);
570 std::vector<dtype> coeffsB(finalCoefficientsSize, 0);
571 stl_algorithms::copy(coefficients_.begin(), coefficients_.end(), coeffsA.begin());
572 stl_algorithms::copy(inOtherPoly.coefficients_.cbegin(), inOtherPoly.coefficients_.cend(), coeffsB.begin());
573
574 // now multiply out the coefficients
575 std::vector<dtype> finalCoefficients(finalCoefficientsSize, 0);
576 for (uint32 i = 0; i < finalCoefficientsSize; ++i)
577 {
578 for (uint32 k = 0; k <= i; ++k)
579 {
580 finalCoefficients[i] += coeffsA[k] * coeffsB[i - k];
581 }
582 }
583
584 this->coefficients_ = finalCoefficients;
585 return *this;
586 }
587
588 //============================================================================
589 // Method Description:
596 {
597 return Poly1d(*this) ^= inPower;
598 }
599
600 //============================================================================
601 // Method Description:
608 {
609 if (inPower == 0)
610 {
611 coefficients_.clear();
612 coefficients_.push_back(1);
613 return *this;
614 }
615 if (inPower == 1)
616 {
617 return *this;
618 }
619
620 auto thisPoly(*this);
621 for (uint32 power = 1; power < inPower; ++power)
622 {
623 *this *= thisPoly;
624 }
625
626 return *this;
627 }
628
629 //============================================================================
630 // Method Description:
637 friend std::ostream& operator<<(std::ostream& inOStream, const Poly1d<dtype>& inPoly)
638 {
639 inOStream << inPoly.str() << std::endl;
640 return inOStream;
641 }
642
643 private:
644 std::vector<dtype> coefficients_{};
645 };
646} // namespace nc::polynomial
#define THROW_INVALID_ARGUMENT_ERROR(msg)
Definition: Error.hpp:37
Holds info about the dtype.
Definition: DtypeInfo.hpp:41
Holds 1D and 2D arrays, the main work horse of the NumCpp library.
Definition: NdArrayCore.hpp:139
size_type size() const noexcept
Definition: NdArrayCore.hpp:4524
iterator end() noexcept
Definition: NdArrayCore.hpp:1623
self_type transpose() const
Definition: NdArrayCore.hpp:4882
bool issquare() const noexcept
Definition: NdArrayCore.hpp:3009
bool isflat() const noexcept
Definition: NdArrayCore.hpp:2945
self_type dot(const self_type &inOtherArray) const
Definition: NdArrayCore.hpp:2719
size_type numCols() const noexcept
Definition: NdArrayCore.hpp:3465
const Shape & shape() const noexcept
Definition: NdArrayCore.hpp:4511
iterator begin() noexcept
Definition: NdArrayCore.hpp:1315
Definition: Poly1d.hpp:58
Poly1d< dtype > operator-(const Poly1d< dtype > &inOtherPoly) const
Definition: Poly1d.hpp:511
Poly1d< dtype > operator*(const Poly1d< dtype > &inOtherPoly) const
Definition: Poly1d.hpp:554
static Poly1d< double > fit(const NdArray< dtype > &xValues, const NdArray< dtype > &yValues, const NdArray< dtype > &weights, uint8 polyOrder)
Definition: Poly1d.hpp:266
Poly1d< dtype > & operator^=(uint32 inPower)
Definition: Poly1d.hpp:607
Poly1d< dtype > operator+(const Poly1d< dtype > &inOtherPoly) const
Definition: Poly1d.hpp:468
dtype eval(dtype xValue) const noexcept
Definition: Poly1d.hpp:186
Poly1d< double > integ() const
Definition: Poly1d.hpp:344
Poly1d< dtype > & operator*=(const Poly1d< dtype > &inOtherPoly)
Definition: Poly1d.hpp:566
Poly1d(const NdArray< dtype > &inValues, IsRoots isRoots=IsRoots::NO)
Definition: Poly1d.hpp:78
Poly1d< dtype > & operator-=(const Poly1d< dtype > &inOtherPoly)
Definition: Poly1d.hpp:523
static Poly1d< double > fit(const NdArray< dtype > &xValues, const NdArray< dtype > &yValues, uint8 polyOrder)
Definition: Poly1d.hpp:211
Poly1d< dtypeOut > astype() const
Definition: Poly1d.hpp:128
NdArray< dtype > eval(const NdArray< dtype > &xValues) const noexcept
Definition: Poly1d.hpp:198
Poly1d< dtype > & operator+=(const Poly1d< dtype > &inOtherPoly)
Definition: Poly1d.hpp:480
NdArray< dtype > operator()(const NdArray< dtype > &xValues) const noexcept
Definition: Poly1d.hpp:450
std::string str() const
Definition: Poly1d.hpp:390
void print() const
Definition: Poly1d.hpp:379
Poly1d< dtype > operator^(uint32 inPower) const
Definition: Poly1d.hpp:595
uint32 order() const noexcept
Definition: Poly1d.hpp:369
dtype operator()(dtype inValue) const noexcept
Definition: Poly1d.hpp:433
double area(double a, double b) const
Definition: Poly1d.hpp:110
friend std::ostream & operator<<(std::ostream &inOStream, const Poly1d< dtype > &inPoly)
Definition: Poly1d.hpp:637
NdArray< dtype > coefficients() const
Definition: Poly1d.hpp:145
Poly1d< dtype > deriv() const
Definition: Poly1d.hpp:156
NdArray< double > inv(const NdArray< dtype > &inArray)
Definition: inv.hpp:54
Definition: chebyshev_t.hpp:39
OutputIt transform(InputIt first, InputIt last, OutputIt destination, UnaryOperation unaryFunction)
Definition: StlAlgorithms.hpp:775
OutputIt copy(InputIt first, InputIt last, OutputIt destination) noexcept
Definition: StlAlgorithms.hpp:97
std::string num2str(dtype inNumber)
Definition: num2str.hpp:44
dtype power(dtype inValue, uint8 inPower) noexcept
Definition: Utils/power.hpp:46
bool essentiallyEqual(dtype inValue1, dtype inValue2) noexcept
Definition: essentiallyEqual.hpp:49
constexpr dtype power(dtype inValue, uint8 inExponent) noexcept
Definition: Functions/power.hpp:52
void swap(NdArray< dtype > &inArray1, NdArray< dtype > &inArray2) noexcept
Definition: swap.hpp:42
IsRoots
Is Roots boolean.
Definition: Enums.hpp:92
std::uint8_t uint8
Definition: Types.hpp:42
std::uint32_t uint32
Definition: Types.hpp:40