/src/quantlib/ql/math/interpolations/mixedinterpolation.hpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2010 Ferdinando Ametrano |
5 | | |
6 | | This file is part of QuantLib, a free-software/open-source library |
7 | | for financial quantitative analysts and developers - http://quantlib.org/ |
8 | | |
9 | | QuantLib is free software: you can redistribute it and/or modify it |
10 | | under the terms of the QuantLib license. You should have received a |
11 | | copy of the license along with this program; if not, please email |
12 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
13 | | <https://www.quantlib.org/license.shtml>. |
14 | | |
15 | | This program is distributed in the hope that it will be useful, but WITHOUT |
16 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
17 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
18 | | */ |
19 | | |
20 | | /*! \file mixedinterpolation.hpp |
21 | | \brief mixed interpolation between discrete points |
22 | | */ |
23 | | |
24 | | #ifndef quantlib_mixed_interpolation_hpp |
25 | | #define quantlib_mixed_interpolation_hpp |
26 | | |
27 | | #include <ql/math/interpolations/linearinterpolation.hpp> |
28 | | #include <ql/math/interpolations/cubicinterpolation.hpp> |
29 | | #include <ql/utilities/dataformatters.hpp> |
30 | | |
31 | | namespace QuantLib { |
32 | | |
33 | | namespace detail { |
34 | | |
35 | | template <class I1, class I2> |
36 | | class MixedInterpolationImpl; |
37 | | |
38 | | } |
39 | | |
40 | | |
41 | | struct MixedInterpolation { |
42 | | enum Behavior { |
43 | | ShareRanges, /*!< Define both interpolations over the |
44 | | whole range defined by the passed |
45 | | iterators. This is the default |
46 | | behavior. */ |
47 | | SplitRanges /*!< Define the first interpolation over the |
48 | | first part of the range, and the second |
49 | | interpolation over the second part. */ |
50 | | }; |
51 | | }; |
52 | | |
53 | | //! mixed linear/cubic interpolation between discrete points |
54 | | /*! \ingroup interpolations |
55 | | \warning See the Interpolation class for information about the |
56 | | required lifetime of the underlying data. |
57 | | */ |
58 | | class MixedLinearCubicInterpolation : public Interpolation { |
59 | | public: |
60 | | /*! \pre the \f$ x \f$ values must be sorted. */ |
61 | | template <class I1, class I2> |
62 | | MixedLinearCubicInterpolation(const I1& xBegin, const I1& xEnd, |
63 | | const I2& yBegin, Size n, |
64 | | MixedInterpolation::Behavior behavior, |
65 | | CubicInterpolation::DerivativeApprox da, |
66 | | bool monotonic, |
67 | | CubicInterpolation::BoundaryCondition leftC, |
68 | | Real leftConditionValue, |
69 | | CubicInterpolation::BoundaryCondition rightC, |
70 | | Real rightConditionValue) { |
71 | | impl_ = ext::make_shared<detail::MixedInterpolationImpl<I1, I2>>( |
72 | | xBegin, xEnd, yBegin, n, behavior, |
73 | | Linear(), |
74 | | Cubic(da, monotonic, |
75 | | leftC, leftConditionValue, |
76 | | rightC, rightConditionValue)); |
77 | | impl_->update(); |
78 | | } |
79 | | }; |
80 | | |
81 | | //! mixed linear/cubic interpolation factory and traits |
82 | | /*! \ingroup interpolations */ |
83 | | class MixedLinearCubic { |
84 | | public: |
85 | | MixedLinearCubic(Size n, |
86 | | MixedInterpolation::Behavior behavior, |
87 | | CubicInterpolation::DerivativeApprox da, |
88 | | bool monotonic = true, |
89 | | CubicInterpolation::BoundaryCondition leftCondition |
90 | | = CubicInterpolation::SecondDerivative, |
91 | | Real leftConditionValue = 0.0, |
92 | | CubicInterpolation::BoundaryCondition rightCondition |
93 | | = CubicInterpolation::SecondDerivative, |
94 | | Real rightConditionValue = 0.0) |
95 | | : n_(n), behavior_(behavior), da_(da), monotonic_(monotonic), |
96 | | leftType_(leftCondition), rightType_(rightCondition), |
97 | 0 | leftValue_(leftConditionValue), rightValue_(rightConditionValue) {} |
98 | | template <class I1, class I2> |
99 | | Interpolation interpolate(const I1& xBegin, const I1& xEnd, |
100 | | const I2& yBegin) const { |
101 | | return MixedLinearCubicInterpolation(xBegin, xEnd, |
102 | | yBegin, n_, behavior_, |
103 | | da_, monotonic_, |
104 | | leftType_, leftValue_, |
105 | | rightType_, rightValue_); |
106 | | } |
107 | | // fix below |
108 | | static const bool global = true; |
109 | | static const Size requiredPoints = 3; |
110 | | private: |
111 | | Size n_; |
112 | | MixedInterpolation::Behavior behavior_; |
113 | | CubicInterpolation::DerivativeApprox da_; |
114 | | bool monotonic_; |
115 | | CubicInterpolation::BoundaryCondition leftType_, rightType_; |
116 | | Real leftValue_, rightValue_; |
117 | | }; |
118 | | |
119 | | // convenience classes |
120 | | |
121 | | class MixedLinearCubicNaturalSpline : public MixedLinearCubicInterpolation { |
122 | | public: |
123 | | /*! \pre the \f$ x \f$ values must be sorted. */ |
124 | | template <class I1, class I2> |
125 | | MixedLinearCubicNaturalSpline(const I1& xBegin, const I1& xEnd, |
126 | | const I2& yBegin, Size n, |
127 | | MixedInterpolation::Behavior behavior |
128 | | = MixedInterpolation::ShareRanges) |
129 | | : MixedLinearCubicInterpolation(xBegin, xEnd, yBegin, n, behavior, |
130 | | CubicInterpolation::Spline, false, |
131 | | CubicInterpolation::SecondDerivative, 0.0, |
132 | | CubicInterpolation::SecondDerivative, 0.0) {} |
133 | | }; |
134 | | |
135 | | class MixedLinearMonotonicCubicNaturalSpline : public MixedLinearCubicInterpolation { |
136 | | public: |
137 | | /*! \pre the \f$ x \f$ values must be sorted. */ |
138 | | template <class I1, class I2> |
139 | | MixedLinearMonotonicCubicNaturalSpline(const I1& xBegin, const I1& xEnd, |
140 | | const I2& yBegin, Size n, |
141 | | MixedInterpolation::Behavior behavior |
142 | | = MixedInterpolation::ShareRanges) |
143 | | : MixedLinearCubicInterpolation(xBegin, xEnd, yBegin, n, behavior, |
144 | | CubicInterpolation::Spline, true, |
145 | | CubicInterpolation::SecondDerivative, 0.0, |
146 | | CubicInterpolation::SecondDerivative, 0.0) {} |
147 | | }; |
148 | | |
149 | | class MixedLinearKrugerCubic : public MixedLinearCubicInterpolation { |
150 | | public: |
151 | | /*! \pre the \f$ x \f$ values must be sorted. */ |
152 | | template <class I1, class I2> |
153 | | MixedLinearKrugerCubic(const I1& xBegin, const I1& xEnd, |
154 | | const I2& yBegin, Size n, |
155 | | MixedInterpolation::Behavior behavior |
156 | | = MixedInterpolation::ShareRanges) |
157 | | : MixedLinearCubicInterpolation(xBegin, xEnd, yBegin, n, behavior, |
158 | | CubicInterpolation::Kruger, false, |
159 | | CubicInterpolation::SecondDerivative, 0.0, |
160 | | CubicInterpolation::SecondDerivative, 0.0) {} |
161 | | }; |
162 | | |
163 | | class MixedLinearFritschButlandCubic : public MixedLinearCubicInterpolation { |
164 | | public: |
165 | | /*! \pre the \f$ x \f$ values must be sorted. */ |
166 | | template <class I1, class I2> |
167 | | MixedLinearFritschButlandCubic(const I1& xBegin, const I1& xEnd, |
168 | | const I2& yBegin, Size n, |
169 | | MixedInterpolation::Behavior behavior |
170 | | = MixedInterpolation::ShareRanges) |
171 | | : MixedLinearCubicInterpolation(xBegin, xEnd, yBegin, n, behavior, |
172 | | CubicInterpolation::FritschButland, false, |
173 | | CubicInterpolation::SecondDerivative, 0.0, |
174 | | CubicInterpolation::SecondDerivative, 0.0) {} |
175 | | }; |
176 | | |
177 | | class MixedLinearParabolic : public MixedLinearCubicInterpolation { |
178 | | public: |
179 | | /*! \pre the \f$ x \f$ values must be sorted. */ |
180 | | template <class I1, class I2> |
181 | | MixedLinearParabolic(const I1& xBegin, const I1& xEnd, |
182 | | const I2& yBegin, Size n, |
183 | | MixedInterpolation::Behavior behavior |
184 | | = MixedInterpolation::ShareRanges) |
185 | | : MixedLinearCubicInterpolation(xBegin, xEnd, yBegin, n, behavior, |
186 | | CubicInterpolation::Parabolic, false, |
187 | | CubicInterpolation::SecondDerivative, 0.0, |
188 | | CubicInterpolation::SecondDerivative, 0.0) {} |
189 | | }; |
190 | | |
191 | | class MixedLinearMonotonicParabolic : public MixedLinearCubicInterpolation { |
192 | | public: |
193 | | /*! \pre the \f$ x \f$ values must be sorted. */ |
194 | | template <class I1, class I2> |
195 | | MixedLinearMonotonicParabolic(const I1& xBegin, const I1& xEnd, |
196 | | const I2& yBegin, Size n, |
197 | | MixedInterpolation::Behavior behavior |
198 | | = MixedInterpolation::ShareRanges) |
199 | | : MixedLinearCubicInterpolation(xBegin, xEnd, yBegin, n, behavior, |
200 | | CubicInterpolation::Parabolic, true, |
201 | | CubicInterpolation::SecondDerivative, 0.0, |
202 | | CubicInterpolation::SecondDerivative, 0.0) {} |
203 | | }; |
204 | | |
205 | | namespace detail { |
206 | | |
207 | | template <class I1, class I2> |
208 | | class MixedInterpolationImpl |
209 | | : public Interpolation::templateImpl<I1, I2> { |
210 | | public: |
211 | | template <class Interpolator1, class Interpolator2> |
212 | | MixedInterpolationImpl(const I1& xBegin, const I1& xEnd, |
213 | | const I2& yBegin, Size n, |
214 | | MixedInterpolation::Behavior behavior, |
215 | | const Interpolator1& factory1, |
216 | | const Interpolator2& factory2) |
217 | | : Interpolation::templateImpl<I1, I2>(xBegin, xEnd, yBegin, 1) { |
218 | | Size maxN = static_cast<Size>(xEnd - xBegin); |
219 | | // SplitRanges needs xBegin2_+1 to be valid |
220 | | if (behavior == MixedInterpolation::SplitRanges) { |
221 | | --maxN; |
222 | | } |
223 | | // This only checks that we pass valid iterators into interpolate() |
224 | | // calls below. The calls themselves check requiredPoints for each |
225 | | // of the segments. |
226 | | QL_REQUIRE(n <= maxN, "n is too large (" << n << " > " << maxN << ")"); |
227 | | |
228 | | xBegin2_ = this->xBegin_ + n; |
229 | | |
230 | | switch (behavior) { |
231 | | case MixedInterpolation::ShareRanges: |
232 | | interpolation1_ = factory1.interpolate(this->xBegin_, |
233 | | this->xEnd_, |
234 | | this->yBegin_); |
235 | | interpolation2_ = factory2.interpolate(this->xBegin_, |
236 | | this->xEnd_, |
237 | | this->yBegin_); |
238 | | break; |
239 | | case MixedInterpolation::SplitRanges: |
240 | | interpolation1_ = factory1.interpolate(this->xBegin_, |
241 | | this->xBegin2_ + 1, |
242 | | this->yBegin_); |
243 | | interpolation2_ = factory2.interpolate(this->xBegin2_, |
244 | | this->xEnd_, |
245 | | this->yBegin_ + n); |
246 | | break; |
247 | | default: |
248 | | QL_FAIL("unknown mixed-interpolation behavior: " << behavior); |
249 | | } |
250 | | } |
251 | | |
252 | | void update() { |
253 | | interpolation1_.update(); |
254 | | interpolation2_.update(); |
255 | | } |
256 | | Real value(Real x) const { |
257 | | if (x<*xBegin2_) |
258 | | return interpolation1_(x, true); |
259 | | return interpolation2_(x, true); |
260 | | } |
261 | | Real primitive(Real x) const { |
262 | | if (x<*xBegin2_) |
263 | | return interpolation1_.primitive(x, true); |
264 | | return interpolation2_.primitive(x, true) - |
265 | | interpolation2_.primitive(*xBegin2_, true) + |
266 | | interpolation1_.primitive(*xBegin2_, true); |
267 | | } |
268 | | Real derivative(Real x) const { |
269 | | if (x<*xBegin2_) |
270 | | return interpolation1_.derivative(x, true); |
271 | | return interpolation2_.derivative(x, true); |
272 | | } |
273 | | Real secondDerivative(Real x) const { |
274 | | if (x<*xBegin2_) |
275 | | return interpolation1_.secondDerivative(x, true); |
276 | | return interpolation2_.secondDerivative(x, true); |
277 | | } |
278 | | private: |
279 | | I1 xBegin2_; |
280 | | Interpolation interpolation1_, interpolation2_; |
281 | | }; |
282 | | |
283 | | } |
284 | | |
285 | | } |
286 | | |
287 | | #endif |