QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.38
Loading...
Searching...
No Matches
cubicinterpolation.hpp
Go to the documentation of this file.
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
5 Copyright (C) 2001, 2002, 2003 Nicolas Di Césaré
6 Copyright (C) 2004, 2008, 2009, 2011 Ferdinando Ametrano
7 Copyright (C) 2009 Sylvain Bertrand
8 Copyright (C) 2013 Peter Caspers
9 Copyright (C) 2016 Nicholas Bertocchi
10
11 This file is part of QuantLib, a free-software/open-source library
12 for financial quantitative analysts and developers - http://quantlib.org/
13
14 QuantLib is free software: you can redistribute it and/or modify it
15 under the terms of the QuantLib license. You should have received a
16 copy of the license along with this program; if not, please email
17 <quantlib-dev@lists.sf.net>. The license is also available online at
18 <http://quantlib.org/license.shtml>.
19
20 This program is distributed in the hope that it will be useful, but WITHOUT
21 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22 FOR A PARTICULAR PURPOSE. See the license for more details.
23*/
24
25/*! \file cubicinterpolation.hpp
26 \brief cubic interpolation between discrete points
27*/
28
29#ifndef quantlib_cubic_interpolation_hpp
30#define quantlib_cubic_interpolation_hpp
31
32#include <algorithm>
33#include <ql/math/matrix.hpp>
36#include <vector>
37
38namespace QuantLib {
39
40 namespace detail {
41
43 public:
45 : n_(n), primitiveConst_(n-1), a_(n-1), b_(n-1), c_(n-1),
47 virtual ~CoefficientHolder() = default;
49 // P[i](x) = y[i] +
50 // a[i]*(x-x[i]) +
51 // b[i]*(x-x[i])^2 +
52 // c[i]*(x-x[i])^3
53 std::vector<Real> primitiveConst_, a_, b_, c_;
54 std::vector<bool> monotonicityAdjustments_;
55 };
56
57 template <class I1, class I2> class CubicInterpolationImpl;
58
59 }
60
61 //! %Cubic interpolation between discrete points.
62 /*! Cubic interpolation is fully defined when the ${f_i}$ function values
63 at points ${x_i}$ are supplemented with ${f^'_i}$ function derivative
64 values.
65
66 Different type of first derivative approximations are implemented,
67 both local and non-local. Local schemes (Fourth-order, Parabolic,
68 Modified Parabolic, Fritsch-Butland, Akima, Kruger) use only $f$ values
69 near $x_i$ to calculate each $f^'_i$. Non-local schemes (Spline with
70 different boundary conditions) use all ${f_i}$ values and obtain
71 ${f^'_i}$ by solving a linear system of equations. Local schemes
72 produce $C^1$ interpolants, while the spline schemes generate $C^2$
73 interpolants.
74
75 Hyman's monotonicity constraint filter is also implemented: it can be
76 applied to all schemes to ensure that in the regions of local
77 monotoniticity of the input (three successive increasing or decreasing
78 values) the interpolating cubic remains monotonic. If the interpolating
79 cubic is already monotonic, the Hyman filter leaves it unchanged
80 preserving all its original features.
81
82 In the case of $C^2$ interpolants the Hyman filter ensures local
83 monotonicity at the expense of the second derivative of the interpolant
84 which will no longer be continuous in the points where the filter has
85 been applied.
86
87 While some non-linear schemes (Modified Parabolic, Fritsch-Butland,
88 Kruger) are guaranteed to be locally monotonic in their original
89 approximation, all other schemes must be filtered according to the
90 Hyman criteria at the expense of their linearity.
91
92 See R. L. Dougherty, A. Edelman, and J. M. Hyman,
93 "Nonnegativity-, Monotonicity-, or Convexity-Preserving CubicSpline and
94 Quintic Hermite Interpolation"
95 Mathematics Of Computation, v. 52, n. 186, April 1989, pp. 471-494.
96
97 \todo implement missing schemes (FourthOrder and ModifiedParabolic) and
98 missing boundary conditions (Periodic and Lagrange).
99
100 \test to be adapted from old ones.
101
102 \ingroup interpolations
103 \warning See the Interpolation class for information about the
104 required lifetime of the underlying data.
105 */
107 public:
109 /*! Spline approximation (non-local, non-monotonic, linear[?]).
110 Different boundary conditions can be used on the left and right
111 boundaries: see BoundaryCondition.
112 */
114
115 //! Overshooting minimization 1st derivative
117
118 //! Overshooting minimization 2nd derivative
120
121 //! Fourth-order approximation (local, non-monotonic, linear)
123
124 //! Parabolic approximation (local, non-monotonic, linear)
126
127 //! Fritsch-Butland approximation (local, monotonic, non-linear)
129
130 //! Akima approximation (local, non-monotonic, non-linear)
132
133 //! Kruger approximation (local, monotonic, non-linear)
135
136 //! Weighted harmonic mean approximation (local, monotonic, non-linear)
138 };
140 //! Make second(-last) point an inactive knot
142
143 //! Match value of end-slope
145
146 //! Match value of second derivative at end
148
149 //! Match first and second derivative at either end
151
152 /*! Match end-slope to the slope of the cubic that matches
153 the first four data at the respective end
154 */
156 };
157 /*! \pre the \f$ x \f$ values must be sorted. */
158 template <class I1, class I2>
159 CubicInterpolation(const I1& xBegin,
160 const I1& xEnd,
161 const I2& yBegin,
163 bool monotonic,
165 Real leftConditionValue,
167 Real rightConditionValue) {
168 impl_ = ext::shared_ptr<Interpolation::Impl>(new
169 detail::CubicInterpolationImpl<I1,I2>(xBegin, xEnd, yBegin,
170 da,
171 monotonic,
172 leftCond,
173 leftConditionValue,
174 rightCond,
175 rightConditionValue));
176 impl_->update();
177 }
178 const std::vector<Real>& primitiveConstants() const {
179 return coeffs().primitiveConst_;
180 }
181 const std::vector<Real>& aCoefficients() const { return coeffs().a_; }
182 const std::vector<Real>& bCoefficients() const { return coeffs().b_; }
183 const std::vector<Real>& cCoefficients() const { return coeffs().c_; }
184 const std::vector<bool>& monotonicityAdjustments() const {
186 }
187 private:
189 return *dynamic_cast<detail::CoefficientHolder*>(impl_.get());
190 }
191 };
192
193
194 // convenience classes
195
197 public:
198 /*! \pre the \f$ x \f$ values must be sorted. */
199 template <class I1, class I2>
200 CubicNaturalSpline(const I1& xBegin,
201 const I1& xEnd,
202 const I2& yBegin)
203 : CubicInterpolation(xBegin, xEnd, yBegin,
204 Spline, false,
205 SecondDerivative, 0.0,
206 SecondDerivative, 0.0) {}
207 };
208
210 public:
211 /*! \pre the \f$ x \f$ values must be sorted. */
212 template <class I1, class I2>
214 const I1& xEnd,
215 const I2& yBegin)
216 : CubicInterpolation(xBegin, xEnd, yBegin,
217 Spline, true,
218 SecondDerivative, 0.0,
219 SecondDerivative, 0.0) {}
220 };
221
223 public:
224 /*! \pre the \f$ x \f$ values must be sorted. */
225 template <class I1, class I2>
227 const I1& xEnd,
228 const I2& yBegin)
229 : CubicInterpolation(xBegin, xEnd, yBegin,
230 SplineOM1, false,
231 SecondDerivative, 0.0,
232 SecondDerivative, 0.0) {}
233 };
234
236 public:
237 /*! \pre the \f$ x \f$ values must be sorted. */
238 template <class I1, class I2>
240 const I1& xEnd,
241 const I2& yBegin)
242 : CubicInterpolation(xBegin, xEnd, yBegin,
243 SplineOM2, false,
244 SecondDerivative, 0.0,
245 SecondDerivative, 0.0) {}
246 };
247
249 public:
250 /*! \pre the \f$ x \f$ values must be sorted. */
251 template <class I1, class I2>
252 AkimaCubicInterpolation(const I1& xBegin,
253 const I1& xEnd,
254 const I2& yBegin)
255 : CubicInterpolation(xBegin, xEnd, yBegin,
256 Akima, false,
257 SecondDerivative, 0.0,
258 SecondDerivative, 0.0) {}
259 };
260
262 public:
263 /*! \pre the \f$ x \f$ values must be sorted. */
264 template <class I1, class I2>
265 KrugerCubic(const I1& xBegin,
266 const I1& xEnd,
267 const I2& yBegin)
268 : CubicInterpolation(xBegin, xEnd, yBegin,
269 Kruger, false,
270 SecondDerivative, 0.0,
271 SecondDerivative, 0.0) {}
272 };
273
275 public:
276 /*! \pre the \f$ x \f$ values must be sorted. */
277 template <class I1, class I2>
278 HarmonicCubic(const I1& xBegin,
279 const I1& xEnd,
280 const I2& yBegin)
281 : CubicInterpolation(xBegin, xEnd, yBegin,
282 Harmonic, false,
283 SecondDerivative, 0.0,
284 SecondDerivative, 0.0) {}
285 };
286
288 public:
289 /*! \pre the \f$ x \f$ values must be sorted. */
290 template <class I1, class I2>
291 FritschButlandCubic(const I1& xBegin,
292 const I1& xEnd,
293 const I2& yBegin)
294 : CubicInterpolation(xBegin, xEnd, yBegin,
295 FritschButland, true,
296 SecondDerivative, 0.0,
297 SecondDerivative, 0.0) {}
298 };
299
301 public:
302 /*! \pre the \f$ x \f$ values must be sorted. */
303 template <class I1, class I2>
304 Parabolic(const I1& xBegin,
305 const I1& xEnd,
306 const I2& yBegin)
307 : CubicInterpolation(xBegin, xEnd, yBegin,
309 SecondDerivative, 0.0,
310 SecondDerivative, 0.0) {}
311 };
312
314 public:
315 /*! \pre the \f$ x \f$ values must be sorted. */
316 template <class I1, class I2>
317 MonotonicParabolic(const I1& xBegin,
318 const I1& xEnd,
319 const I2& yBegin)
320 : CubicInterpolation(xBegin, xEnd, yBegin,
321 Parabolic, true,
322 SecondDerivative, 0.0,
323 SecondDerivative, 0.0) {}
324 };
325
326 //! %Cubic interpolation factory and traits
327 /*! \ingroup interpolations */
328 class Cubic {
329 public:
332 bool monotonic = false,
335 Real leftConditionValue = 0.0,
338 Real rightConditionValue = 0.0)
339 : da_(da), monotonic_(monotonic),
340 leftType_(leftCondition), rightType_(rightCondition),
341 leftValue_(leftConditionValue), rightValue_(rightConditionValue) {}
342 template <class I1, class I2>
343 Interpolation interpolate(const I1& xBegin,
344 const I1& xEnd,
345 const I2& yBegin) const {
346 return CubicInterpolation(xBegin, xEnd, yBegin,
350 }
351 static const bool global = true;
352 static const Size requiredPoints = 2;
353 private:
358 };
359
360
361 namespace detail {
362
363 template <class I1, class I2>
365 public Interpolation::templateImpl<I1,I2> {
366 public:
367 CubicInterpolationImpl(const I1& xBegin,
368 const I1& xEnd,
369 const I2& yBegin,
371 bool monotonic,
373 Real leftConditionValue,
375 Real rightConditionValue)
376 : CoefficientHolder(xEnd-xBegin),
377 Interpolation::templateImpl<I1,I2>(xBegin, xEnd, yBegin,
378 Cubic::requiredPoints),
379 da_(da),
380 monotonic_(monotonic),
381 leftType_(leftCondition), rightType_(rightCondition),
382 leftValue_(leftConditionValue),
383 rightValue_(rightConditionValue),
384 tmp_(n_), dx_(n_-1), S_(n_-1), L_(n_) {
387 QL_REQUIRE((xEnd-xBegin) >= 4,
388 "Lagrange boundary condition requires at least "
389 "4 points (" << (xEnd-xBegin) << " are given)");
390 }
391 }
392
393 void update() override {
394
395 for (Size i=0; i<n_-1; ++i) {
396 dx_[i] = this->xBegin_[i+1] - this->xBegin_[i];
397 S_[i] = (this->yBegin_[i+1] - this->yBegin_[i])/dx_[i];
398 }
399
400 // first derivative approximation
402 for (Size i=1; i<n_-1; ++i) {
403 L_.setMidRow(i, dx_[i], 2.0*(dx_[i]+dx_[i-1]), dx_[i-1]);
404 tmp_[i] = 3.0*(dx_[i]*S_[i-1] + dx_[i-1]*S_[i]);
405 }
406
407 // left boundary condition
408 switch (leftType_) {
410 // ignoring end condition value
411 L_.setFirstRow(dx_[1]*(dx_[1]+dx_[0]),
412 (dx_[0]+dx_[1])*(dx_[0]+dx_[1]));
413 tmp_[0] = S_[0]*dx_[1]*(2.0*dx_[1]+3.0*dx_[0]) +
414 S_[1]*dx_[0]*dx_[0];
415 break;
417 L_.setFirstRow(1.0, 0.0);
418 tmp_[0] = leftValue_;
419 break;
421 L_.setFirstRow(2.0, 1.0);
422 tmp_[0] = 3.0*S_[0] - leftValue_*dx_[0]/2.0;
423 break;
425 QL_FAIL("this end condition is not implemented yet");
427 L_.setFirstRow(1.0, 0.0);
429 this->xBegin_[0],this->xBegin_[1],
430 this->xBegin_[2],this->xBegin_[3],
431 this->yBegin_[0],this->yBegin_[1],
432 this->yBegin_[2],this->yBegin_[3],
433 this->xBegin_[0]);
434 break;
435 default:
436 QL_FAIL("unknown end condition");
437 }
438
439 // right boundary condition
440 switch (rightType_) {
442 // ignoring end condition value
443 L_.setLastRow(-(dx_[n_-2]+dx_[n_-3])*(dx_[n_-2]+dx_[n_-3]),
444 -dx_[n_-3]*(dx_[n_-3]+dx_[n_-2]));
445 tmp_[n_-1] = -S_[n_-3]*dx_[n_-2]*dx_[n_-2] -
446 S_[n_-2]*dx_[n_-3]*(3.0*dx_[n_-2]+2.0*dx_[n_-3]);
447 break;
449 L_.setLastRow(0.0, 1.0);
450 tmp_[n_-1] = rightValue_;
451 break;
453 L_.setLastRow(1.0, 2.0);
454 tmp_[n_-1] = 3.0*S_[n_-2] + rightValue_*dx_[n_-2]/2.0;
455 break;
457 QL_FAIL("this end condition is not implemented yet");
459 L_.setLastRow(0.0,1.0);
461 this->xBegin_[n_-4],this->xBegin_[n_-3],
462 this->xBegin_[n_-2],this->xBegin_[n_-1],
463 this->yBegin_[n_-4],this->yBegin_[n_-3],
464 this->yBegin_[n_-2],this->yBegin_[n_-1],
465 this->xBegin_[n_-1]);
466 break;
467 default:
468 QL_FAIL("unknown end condition");
469 }
470
471 // solve the system
474 Matrix T_(n_-2, n_, 0.0);
475 for (Size i=0; i<n_-2; ++i) {
476 T_[i][i]=dx_[i]/6.0;
477 T_[i][i+1]=(dx_[i+1]+dx_[i])/3.0;
478 T_[i][i+2]=dx_[i+1]/6.0;
479 }
480 Matrix S_(n_-2, n_, 0.0);
481 for (Size i=0; i<n_-2; ++i) {
482 S_[i][i]=1.0/dx_[i];
483 S_[i][i+1]=-(1.0/dx_[i+1]+1.0/dx_[i]);
484 S_[i][i+2]=1.0/dx_[i+1];
485 }
486 Matrix Up_(n_, 2, 0.0);
487 Up_[0][0]=1;
488 Up_[n_-1][1]=1;
489 Matrix Us_(n_, n_-2, 0.0);
490 for (Size i=0; i<n_-2; ++i)
491 Us_[i+1][i]=1;
492 Matrix Z_ = Us_*inverse(T_*Us_);
493 Matrix I_(n_, n_, 0.0);
494 for (Size i=0; i<n_; ++i)
495 I_[i][i]=1;
496 Matrix V_ = (I_-Z_*T_)*Up_;
497 Matrix W_ = Z_*S_;
498 Matrix Q_(n_, n_, 0.0);
499 Q_[0][0]=1.0/(n_-1)*dx_[0]*dx_[0]*dx_[0];
500 Q_[0][1]=7.0/8*1.0/(n_-1)*dx_[0]*dx_[0]*dx_[0];
501 for (Size i=1; i<n_-1; ++i) {
502 Q_[i][i-1]=7.0/8*1.0/(n_-1)*dx_[i-1]*dx_[i-1]*dx_[i-1];
503 Q_[i][i]=1.0/(n_-1)*dx_[i]*dx_[i]*dx_[i]+1.0/(n_-1)*dx_[i-1]*dx_[i-1]*dx_[i-1];
504 Q_[i][i+1]=7.0/8*1.0/(n_-1)*dx_[i]*dx_[i]*dx_[i];
505 }
506 Q_[n_-1][n_-2]=7.0/8*1.0/(n_-1)*dx_[n_-2]*dx_[n_-2]*dx_[n_-2];
507 Q_[n_-1][n_-1]=1.0/(n_-1)*dx_[n_-2]*dx_[n_-2]*dx_[n_-2];
508 Matrix J_ = (I_-V_*inverse(transpose(V_)*Q_*V_)*transpose(V_)*Q_)*W_;
509 Array Y_(n_);
510 for (Size i=0; i<n_; ++i)
511 Y_[i]=this->yBegin_[i];
512 Array D_ = J_*Y_;
513 for (Size i=0; i<n_-1; ++i)
514 tmp_[i]=(Y_[i+1]-Y_[i])/dx_[i]-(2.0*D_[i]+D_[i+1])*dx_[i]/6.0;
515 tmp_[n_-1]=tmp_[n_-2]+D_[n_-2]*dx_[n_-2]+(D_[n_-1]-D_[n_-2])*dx_[n_-2]/2.0;
516
518 Matrix T_(n_-2, n_, 0.0);
519 for (Size i=0; i<n_-2; ++i) {
520 T_[i][i]=dx_[i]/6.0;
521 T_[i][i+1]=(dx_[i]+dx_[i+1])/3.0;
522 T_[i][i+2]=dx_[i+1]/6.0;
523 }
524 Matrix S_(n_-2, n_, 0.0);
525 for (Size i=0; i<n_-2; ++i) {
526 S_[i][i]=1.0/dx_[i];
527 S_[i][i+1]=-(1.0/dx_[i+1]+1.0/dx_[i]);
528 S_[i][i+2]=1.0/dx_[i+1];
529 }
530 Matrix Up_(n_, 2, 0.0);
531 Up_[0][0]=1;
532 Up_[n_-1][1]=1;
533 Matrix Us_(n_, n_-2, 0.0);
534 for (Size i=0; i<n_-2; ++i)
535 Us_[i+1][i]=1;
536 Matrix Z_ = Us_*inverse(T_*Us_);
537 Matrix I_(n_, n_, 0.0);
538 for (Size i=0; i<n_; ++i)
539 I_[i][i]=1;
540 Matrix V_ = (I_-Z_*T_)*Up_;
541 Matrix W_ = Z_*S_;
542 Matrix Q_(n_, n_, 0.0);
543 Q_[0][0]=1.0/(n_-1)*dx_[0];
544 Q_[0][1]=1.0/2*1.0/(n_-1)*dx_[0];
545 for (Size i=1; i<n_-1; ++i) {
546 Q_[i][i-1]=1.0/2*1.0/(n_-1)*dx_[i-1];
547 Q_[i][i]=1.0/(n_-1)*dx_[i]+1.0/(n_-1)*dx_[i-1];
548 Q_[i][i+1]=1.0/2*1.0/(n_-1)*dx_[i];
549 }
550 Q_[n_-1][n_-2]=1.0/2*1.0/(n_-1)*dx_[n_-2];
551 Q_[n_-1][n_-1]=1.0/(n_-1)*dx_[n_-2];
552 Matrix J_ = (I_-V_*inverse(transpose(V_)*Q_*V_)*transpose(V_)*Q_)*W_;
553 Array Y_(n_);
554 for (Size i=0; i<n_; ++i)
555 Y_[i]=this->yBegin_[i];
556 Array D_ = J_*Y_;
557 for (Size i=0; i<n_-1; ++i)
558 tmp_[i]=(Y_[i+1]-Y_[i])/dx_[i]-(2.0*D_[i]+D_[i+1])*dx_[i]/6.0;
559 tmp_[n_-1]=tmp_[n_-2]+D_[n_-2]*dx_[n_-2]+(D_[n_-1]-D_[n_-2])*dx_[n_-2]/2.0;
560 } else { // local schemes
561 if (n_==2)
562 tmp_[0] = tmp_[1] = S_[0];
563 else {
564 switch (da_) {
566 QL_FAIL("FourthOrder not implemented yet");
567 break;
569 // intermediate points
570 for (Size i=1; i<n_-1; ++i)
571 tmp_[i] = (dx_[i-1]*S_[i]+dx_[i]*S_[i-1])/(dx_[i]+dx_[i-1]);
572 // end points
573 tmp_[0] = ((2.0*dx_[ 0]+dx_[ 1])*S_[ 0] - dx_[ 0]*S_[ 1]) / (dx_[ 0]+dx_[ 1]);
574 tmp_[n_-1] = ((2.0*dx_[n_-2]+dx_[n_-3])*S_[n_-2] - dx_[n_-2]*S_[n_-3]) / (dx_[n_-2]+dx_[n_-3]);
575 break;
577 // intermediate points
578 for (Size i=1; i<n_-1; ++i) {
579 Real Smin = std::min(S_[i-1], S_[i]);
580 Real Smax = std::max(S_[i-1], S_[i]);
581 if(Smax+2.0*Smin == 0){
582 if (Smin*Smax < 0)
583 tmp_[i] = QL_MIN_REAL;
584 else if (Smin*Smax == 0)
585 tmp_[i] = 0;
586 else
587 tmp_[i] = QL_MAX_REAL;
588 }
589 else
590 tmp_[i] = 3.0*Smin*Smax/(Smax+2.0*Smin);
591 }
592 // end points
593 tmp_[0] = ((2.0*dx_[ 0]+dx_[ 1])*S_[ 0] - dx_[ 0]*S_[ 1]) / (dx_[ 0]+dx_[ 1]);
594 tmp_[n_-1] = ((2.0*dx_[n_-2]+dx_[n_-3])*S_[n_-2] - dx_[n_-2]*S_[n_-3]) / (dx_[n_-2]+dx_[n_-3]);
595 break;
597 tmp_[0] = (std::abs(S_[1]-S_[0])*2*S_[0]*S_[1]+std::abs(2*S_[0]*S_[1]-4*S_[0]*S_[0]*S_[1])*S_[0])/(std::abs(S_[1]-S_[0])+std::abs(2*S_[0]*S_[1]-4*S_[0]*S_[0]*S_[1]));
598 tmp_[1] = (std::abs(S_[2]-S_[1])*S_[0]+std::abs(S_[0]-2*S_[0]*S_[1])*S_[1])/(std::abs(S_[2]-S_[1])+std::abs(S_[0]-2*S_[0]*S_[1]));
599 for (Size i=2; i<n_-2; ++i) {
600 if ((S_[i-2]==S_[i-1]) && (S_[i]!=S_[i+1]))
601 tmp_[i] = S_[i-1];
602 else if ((S_[i-2]!=S_[i-1]) && (S_[i]==S_[i+1]))
603 tmp_[i] = S_[i];
604 else if (S_[i]==S_[i-1])
605 tmp_[i] = S_[i];
606 else if ((S_[i-2]==S_[i-1]) && (S_[i-1]!=S_[i]) && (S_[i]==S_[i+1]))
607 tmp_[i] = (S_[i-1]+S_[i])/2.0;
608 else
609 tmp_[i] = (std::abs(S_[i+1]-S_[i])*S_[i-1]+std::abs(S_[i-1]-S_[i-2])*S_[i])/(std::abs(S_[i+1]-S_[i])+std::abs(S_[i-1]-S_[i-2]));
610 }
611 tmp_[n_-2] = (std::abs(2*S_[n_-2]*S_[n_-3]-S_[n_-2])*S_[n_-3]+std::abs(S_[n_-3]-S_[n_-4])*S_[n_-2])/(std::abs(2*S_[n_-2]*S_[n_-3]-S_[n_-2])+std::abs(S_[n_-3]-S_[n_-4]));
612 tmp_[n_-1] = (std::abs(4*S_[n_-2]*S_[n_-2]*S_[n_-3]-2*S_[n_-2]*S_[n_-3])*S_[n_-2]+std::abs(S_[n_-2]-S_[n_-3])*2*S_[n_-2]*S_[n_-3])/(std::abs(4*S_[n_-2]*S_[n_-2]*S_[n_-3]-2*S_[n_-2]*S_[n_-3])+std::abs(S_[n_-2]-S_[n_-3]));
613 break;
615 // intermediate points
616 for (Size i=1; i<n_-1; ++i) {
617 if (S_[i-1]*S_[i]<0.0)
618 // slope changes sign at point
619 tmp_[i] = 0.0;
620 else
621 // slope will be between the slopes of the adjacent
622 // straight lines and should approach zero if the
623 // slope of either line approaches zero
624 tmp_[i] = 2.0/(1.0/S_[i-1]+1.0/S_[i]);
625 }
626 // end points
627 tmp_[0] = (3.0*S_[0]-tmp_[1])/2.0;
628 tmp_[n_-1] = (3.0*S_[n_-2]-tmp_[n_-2])/2.0;
629 break;
631 // intermediate points
632 for (Size i=1; i<n_-1; ++i) {
633 Real w1 = 2*dx_[i]+dx_[i-1];
634 Real w2 = dx_[i]+2*dx_[i-1];
635 if (S_[i-1]*S_[i]<=0.0)
636 // slope changes sign at point
637 tmp_[i] = 0.0;
638 else
639 // weighted harmonic mean of S_[i] and S_[i-1] if they
640 // have the same sign; otherwise 0
641 tmp_[i] = (w1+w2)/(w1/S_[i-1]+w2/S_[i]);
642 }
643 // end points [0]
644 tmp_[0] = ((2 * dx_[0] + dx_[1])*S_[0] - dx_[0] * S_[1]) / (dx_[1] + dx_[0]);
645 if (tmp_[0]*S_[0]<0.0) {
646 tmp_[0] = 0;
647 }
648 else if (S_[0]*S_[1]<0) {
649 if (std::fabs(tmp_[0])>std::fabs(3*S_[0])) {
650 tmp_[0] = 3*S_[0];
651 }
652 }
653 // end points [n-1]
654 tmp_[n_-1] = ((2*dx_[n_-2]+dx_[n_-3])*S_[n_-2]-dx_[n_-2]*S_[n_-3])/(dx_[n_-3]+dx_[n_-2]);
655 if (tmp_[n_-1]*S_[n_-2]<0.0) {
656 tmp_[n_-1] = 0;
657 }
658 else if (S_[n_-2]*S_[n_-3]<0) {
659 if (std::fabs(tmp_[n_-1])>std::fabs(3*S_[n_-2])) {
660 tmp_[n_-1] = 3*S_[n_-2];
661 }
662 }
663 break;
664 default:
665 QL_FAIL("unknown scheme");
666 }
667 }
668 }
669
670 std::fill(monotonicityAdjustments_.begin(),
671 monotonicityAdjustments_.end(), false);
672 // Hyman monotonicity constrained filter
673 if (monotonic_) {
674 Real correction;
675 Real pm, pu, pd, M;
676 for (Size i=0; i<n_; ++i) {
677 if (i==0) {
678 if (tmp_[i]*S_[0]>0.0) {
679 correction = tmp_[i]/std::fabs(tmp_[i]) *
680 std::min<Real>(std::fabs(tmp_[i]),
681 std::fabs(3.0*S_[0]));
682 } else {
683 correction = 0.0;
684 }
685 if (correction!=tmp_[i]) {
686 tmp_[i] = correction;
687 monotonicityAdjustments_[i] = true;
688 }
689 } else if (i==n_-1) {
690 if (tmp_[i]*S_[n_-2]>0.0) {
691 correction = tmp_[i]/std::fabs(tmp_[i]) *
692 std::min<Real>(std::fabs(tmp_[i]),
693 std::fabs(3.0*S_[n_-2]));
694 } else {
695 correction = 0.0;
696 }
697 if (correction!=tmp_[i]) {
698 tmp_[i] = correction;
699 monotonicityAdjustments_[i] = true;
700 }
701 } else {
702 pm=(S_[i-1]*dx_[i]+S_[i]*dx_[i-1])/
703 (dx_[i-1]+dx_[i]);
704 M = 3.0 * std::min({
705 std::fabs(S_[i-1]),
706 std::fabs(S_[i]),
707 std::fabs(pm)
708 });
709 if (i>1) {
710 if ((S_[i-1]-S_[i-2])*(S_[i]-S_[i-1])>0.0) {
711 pd=(S_[i-1]*(2.0*dx_[i-1]+dx_[i-2])
712 -S_[i-2]*dx_[i-1])/
713 (dx_[i-2]+dx_[i-1]);
714 if (pm*pd>0.0 && pm*(S_[i-1]-S_[i-2])>0.0) {
715 M = std::max<Real>(M, 1.5*std::min(
716 std::fabs(pm),std::fabs(pd)));
717 }
718 }
719 }
720 if (i<n_-2) {
721 if ((S_[i]-S_[i-1])*(S_[i+1]-S_[i])>0.0) {
722 pu=(S_[i]*(2.0*dx_[i]+dx_[i+1])-S_[i+1]*dx_[i])/
723 (dx_[i]+dx_[i+1]);
724 if (pm*pu>0.0 && -pm*(S_[i]-S_[i-1])>0.0) {
725 M = std::max<Real>(M, 1.5*std::min(
726 std::fabs(pm),std::fabs(pu)));
727 }
728 }
729 }
730 if (tmp_[i]*pm>0.0) {
731 correction = tmp_[i]/std::fabs(tmp_[i]) *
732 std::min(std::fabs(tmp_[i]), M);
733 } else {
734 correction = 0.0;
735 }
736 if (correction!=tmp_[i]) {
737 tmp_[i] = correction;
738 monotonicityAdjustments_[i] = true;
739 }
740 }
741 }
742 }
743
744
745 // cubic coefficients
746 for (Size i=0; i<n_-1; ++i) {
747 a_[i] = tmp_[i];
748 b_[i] = (3.0*S_[i] - tmp_[i+1] - 2.0*tmp_[i])/dx_[i];
749 c_[i] = (tmp_[i+1] + tmp_[i] - 2.0*S_[i])/(dx_[i]*dx_[i]);
750 }
751
752 primitiveConst_[0] = 0.0;
753 for (Size i=1; i<n_-1; ++i) {
755 + dx_[i-1] *
756 (this->yBegin_[i-1] + dx_[i-1] *
757 (a_[i-1]/2.0 + dx_[i-1] *
758 (b_[i-1]/3.0 + dx_[i-1] * c_[i-1]/4.0)));
759 }
760 }
761 Real value(Real x) const override {
762 Size j = this->locate(x);
763 Real dx_ = x-this->xBegin_[j];
764 return this->yBegin_[j] + dx_*(a_[j] + dx_*(b_[j] + dx_*c_[j]));
765 }
766 Real primitive(Real x) const override {
767 Size j = this->locate(x);
768 Real dx_ = x-this->xBegin_[j];
769 return primitiveConst_[j]
770 + dx_*(this->yBegin_[j] + dx_*(a_[j]/2.0
771 + dx_*(b_[j]/3.0 + dx_*c_[j]/4.0)));
772 }
773 Real derivative(Real x) const override {
774 Size j = this->locate(x);
775 Real dx_ = x-this->xBegin_[j];
776 return a_[j] + (2.0*b_[j] + 3.0*c_[j]*dx_)*dx_;
777 }
778 Real secondDerivative(Real x) const override {
779 Size j = this->locate(x);
780 Real dx_ = x-this->xBegin_[j];
781 return 2.0*b_[j] + 6.0*c_[j]*dx_;
782 }
783
784 private:
789 mutable Array tmp_;
790 mutable std::vector<Real> dx_, S_;
792
794 Real a, Real b, Real c, Real d,
795 Real u, Real v, Real w, Real z, Real x) const {
796 return (-((((a-c)*(b-c)*(c-x)*z-(a-d)*(b-d)*(d-x)*w)*(a-x+b-x)
797 +((a-c)*(b-c)*z-(a-d)*(b-d)*w)*(a-x)*(b-x))*(a-b)+
798 ((a-c)*(a-d)*v-(b-c)*(b-d)*u)*(c-d)*(c-x)*(d-x)
799 +((a-c)*(a-d)*(a-x)*v-(b-c)*(b-d)*(b-x)*u)
800 *(c-x+d-x)*(c-d)))/
801 ((a-b)*(a-c)*(a-d)*(b-c)*(b-d)*(c-d));
802 }
803 };
804
805 }
806
807}
808
809#endif
AkimaCubicInterpolation(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin)
1-D array used in linear algebra.
Definition: array.hpp:52
Cubic interpolation factory and traits
CubicInterpolation::DerivativeApprox da_
static const bool global
CubicInterpolation::BoundaryCondition rightType_
CubicInterpolation::BoundaryCondition leftType_
static const Size requiredPoints
Cubic(CubicInterpolation::DerivativeApprox da=CubicInterpolation::Kruger, bool monotonic=false, CubicInterpolation::BoundaryCondition leftCondition=CubicInterpolation::SecondDerivative, Real leftConditionValue=0.0, CubicInterpolation::BoundaryCondition rightCondition=CubicInterpolation::SecondDerivative, Real rightConditionValue=0.0)
Interpolation interpolate(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin) const
Cubic interpolation between discrete points.
const std::vector< Real > & cCoefficients() const
const std::vector< bool > & monotonicityAdjustments() const
const std::vector< Real > & bCoefficients() const
CubicInterpolation(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin, CubicInterpolation::DerivativeApprox da, bool monotonic, CubicInterpolation::BoundaryCondition leftCond, Real leftConditionValue, CubicInterpolation::BoundaryCondition rightCond, Real rightConditionValue)
const std::vector< Real > & aCoefficients() const
const std::vector< Real > & primitiveConstants() const
@ Kruger
Kruger approximation (local, monotonic, non-linear)
@ SplineOM2
Overshooting minimization 2nd derivative.
@ Parabolic
Parabolic approximation (local, non-monotonic, linear)
@ Harmonic
Weighted harmonic mean approximation (local, monotonic, non-linear)
@ FourthOrder
Fourth-order approximation (local, non-monotonic, linear)
@ SplineOM1
Overshooting minimization 1st derivative.
@ Akima
Akima approximation (local, non-monotonic, non-linear)
@ FritschButland
Fritsch-Butland approximation (local, monotonic, non-linear)
@ SecondDerivative
Match value of second derivative at end.
@ Periodic
Match first and second derivative at either end.
@ NotAKnot
Make second(-last) point an inactive knot.
@ FirstDerivative
Match value of end-slope.
const detail::CoefficientHolder & coeffs() const
CubicNaturalSpline(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin)
CubicSplineOvershootingMinimization1(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin)
CubicSplineOvershootingMinimization2(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin)
FritschButlandCubic(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin)
HarmonicCubic(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin)
basic template implementation
templateImpl(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin, const int requiredPoints=2)
base class for 1-D interpolations.
ext::shared_ptr< Impl > impl_
KrugerCubic(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin)
Matrix used in linear algebra.
Definition: matrix.hpp:41
MonotonicCubicNaturalSpline(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin)
MonotonicParabolic(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin)
Parabolic(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin)
Base implementation for tridiagonal operator.
Array solveFor(const Array &rhs) const
solve linear system for a given right-hand side
void setMidRow(Size, Real, Real, Real)
CubicInterpolation::DerivativeApprox da_
Real cubicInterpolatingPolynomialDerivative(Real a, Real b, Real c, Real d, Real u, Real v, Real w, Real z, Real x) const
CubicInterpolation::BoundaryCondition rightType_
CubicInterpolation::BoundaryCondition leftType_
CubicInterpolationImpl(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin, CubicInterpolation::DerivativeApprox da, bool monotonic, CubicInterpolation::BoundaryCondition leftCondition, Real leftConditionValue, CubicInterpolation::BoundaryCondition rightCondition, Real rightConditionValue)
Real secondDerivative(Real x) const override
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
#define QL_FAIL(message)
throw an error (possibly with file and line information)
Definition: errors.hpp:92
Date d
std::function< Real(Real)> b
#define QL_MAX_REAL
Definition: qldefines.hpp:176
#define QL_MIN_REAL
Definition: qldefines.hpp:175
QL_REAL Real
real number
Definition: types.hpp:50
std::size_t Size
size of a container
Definition: types.hpp:58
base class for 1-D interpolations
matrix used in linear algebra.
Definition: any.hpp:37
Matrix transpose(const Matrix &m)
Definition: matrix.hpp:700
Matrix inverse(const Matrix &m)
Definition: matrix.cpp:44
ext::shared_ptr< BlackVolTermStructure > v
tridiagonal operator