QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.34
Loading...
Searching...
No Matches
laplaceinterpolation.cpp
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) 2015, 2024 Peter Caspers
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 <http://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 laplaceinterpolation.hpp
21 \brief Laplace interpolation of missing values
22*/
23
25#include <ql/math/matrix.hpp>
35
36namespace QuantLib {
37
38 LaplaceInterpolation::LaplaceInterpolation(std::function<Real(const std::vector<Size>&)> y,
39 std::vector<std::vector<Real>> x,
40 Real relTol,
41 Size maxIterMultiplier)
42 : y_(std::move(y)), x_(std::move(x)), relTol_(relTol), maxIterMultiplier_(maxIterMultiplier) {
43
44 // set up the mesher
45
46 std::vector<Size> dim;
47 coordinateIncluded_.resize(x_.size());
48 for (Size i = 0; i < x_.size(); ++i) {
49 coordinateIncluded_[i] = x_[i].size() > 1;
51 dim.push_back(x_[i].size());
52 }
53
55
57 return;
58 }
59
60 QL_REQUIRE(!dim.empty(), "LaplaceInterpolation: singular point or no points given");
61
62 layout_ = ext::make_shared<FdmLinearOpLayout>(dim);
63
64 std::vector<ext::shared_ptr<Fdm1dMesher>> meshers;
65 for (auto & i : x_) {
66 if (i.size() > 1)
67 meshers.push_back(ext::make_shared<Predefined1dMesher>(i));
68 }
69
70 auto mesher = ext::make_shared<FdmMesherComposite>(layout_, meshers);
71
72 // set up the Laplace operator and convert it to matrix
73
74 struct LaplaceOp : public FdmLinearOpComposite {
75 explicit LaplaceOp(const ext::shared_ptr<FdmMesher>& mesher) {
76 for (Size direction = 0; direction < mesher->layout()->dim().size(); ++direction) {
77 if (mesher->layout()->dim()[direction] > 1)
78 map_.push_back(SecondDerivativeOp(direction, mesher));
79 }
80 }
81 std::vector<TripleBandLinearOp> map_;
82
83 Size size() const override { QL_FAIL("no impl"); }
84 void setTime(Time t1, Time t2) override { QL_FAIL("no impl"); }
85 Array apply(const array_type& r) const override { QL_FAIL("no impl"); }
86 Array apply_mixed(const Array& r) const override { QL_FAIL("no impl"); }
87 Array apply_direction(Size direction, const Array& r) const override {
88 QL_FAIL("no impl");
89 }
90 Array solve_splitting(Size direction, const Array& r, Real s) const override {
91 QL_FAIL("no impl");
92 }
93 Array preconditioner(const Array& r, Real s) const override { QL_FAIL("no impl"); }
94 std::vector<SparseMatrix> toMatrixDecomp() const override {
95 std::vector<SparseMatrix> decomp;
96 decomp.reserve(map_.size());
97for (auto const& m : map_)
98 decomp.push_back(m.toMatrix());
99 return decomp;
100 }
101 };
102
103 SparseMatrix op = LaplaceOp(mesher).toMatrix();
104
105 // set up the linear system to solve
106
107 Size N = layout_->size();
108
109 SparseMatrix g(N, N, 5 * N);
110 Array rhs(N, 0.0), guess(N, 0.0);
111 Real guessTmp = 0.0;
112
113 struct f_A {
114 const SparseMatrix& g;
115 explicit f_A(const SparseMatrix& g) : g(g) {}
116 Array operator()(const Array& x) const { return prod(g, x); }
117 };
118
119 auto rowit = op.begin1();
120 Size count = 0;
121 std::vector<Real> corner_h(dim.size());
122 std::vector<Size> corner_neighbour_index(dim.size());
123 for (auto const& pos : *layout_) {
124 auto coord = pos.coordinates();
125 Real val =
126 y_(numberOfCoordinatesIncluded_ == x_.size() ? coord : fullCoordinates(coord));
127 QL_REQUIRE(rowit != op.end1() && rowit.index1() == count,
128 "LaplaceInterpolation: op matrix row iterator ("
129 << (rowit != op.end1() ? std::to_string(rowit.index1()) : "na")
130 << ") does not match expected row count (" << count << ")");
131 if (val == Null<Real>()) {
132 bool isCorner = true;
133 for (Size d = 0; d < dim.size() && isCorner; ++d) {
134 if (coord[d] == 0) {
135 corner_h[d] = meshers[d]->dplus(0);
136 corner_neighbour_index[d] = 1;
137 } else if (coord[d] == layout_->dim()[d] - 1) {
138 corner_h[d] = meshers[d]->dminus(dim[d] - 1);
139 corner_neighbour_index[d] = dim[d] - 2;
140 } else {
141 isCorner = false;
142 }
143 }
144 if (isCorner) {
145 // handling of the "corners", all second derivs are zero in the op
146 // this directly generalizes Numerical Recipes, 3rd ed, eq 3.8.6
147 Real sum_corner_h =
148 std::accumulate(corner_h.begin(), corner_h.end(), Real(0.0), std::plus<Real>());
149 for (Size j = 0; j < dim.size(); ++j) {
150 std::vector<Size> coord_j(coord);
151 coord_j[j] = corner_neighbour_index[j];
152 Real weight = 0.0;
153 for (Size i = 0; i < dim.size(); ++i) {
154 if (i != j)
155 weight += corner_h[i];
156 }
157 weight = dim.size() == 1 ? Real(1.0) : Real(weight / sum_corner_h);
158 g(count, layout_->index(coord_j)) = -weight;
159 }
160 g(count, count) = 1.0;
161 } else {
162 // point with at least one dimension with non-trivial second derivative
163 for (auto colit = rowit.begin(); colit != rowit.end(); ++colit)
164 g(count, colit.index2()) = *colit;
165 }
166 rhs[count] = 0.0;
167 guess[count] = guessTmp;
168 } else {
169 g(count, count) = 1;
170 rhs[count] = val;
171 guess[count] = guessTmp = val;
172 }
173 ++count;
174 ++rowit;
175 }
176
177 interpolatedValues_ = BiCGstab(f_A(g), maxIterMultiplier_ * N, relTol_).solve(rhs, guess).x;
178 }
179
180 std::vector<Size>
181 LaplaceInterpolation::projectedCoordinates(const std::vector<Size>& coordinates) const {
182 std::vector<Size> tmp;
183 for (Size i = 0; i < coordinates.size(); ++i) {
184 if (coordinateIncluded_[i])
185 tmp.push_back(coordinates[i]);
186 }
187 return tmp;
188 }
189
190 std::vector<Size>
191 LaplaceInterpolation::fullCoordinates(const std::vector<Size>& projectedCoordinates) const {
192 std::vector<Size> tmp(coordinateIncluded_.size(), 0);
193 for (Size i = 0, count = 0; i < coordinateIncluded_.size(); ++i) {
194 if (coordinateIncluded_[i])
195 tmp[i] = projectedCoordinates[count++];
196 }
197 return tmp;
198 }
199
200 Real LaplaceInterpolation::operator()(const std::vector<Size>& coordinates) const {
201 QL_REQUIRE(coordinates.size() == x_.size(), "LaplaceInterpolation::operator(): expected "
202 << x_.size() << " coordinates, got "
203 << coordinates.size());
205 Real val = y_(coordinates);
206 return val == Null<Real>() ? 0.0 : val;
207 } else {
209 coordinates :
210 projectedCoordinates(coordinates))];
211 }
212 }
213
215 const std::vector<Real>& x,
216 const std::vector<Real>& y,
217 Real relTol,
218 Size maxIterMultiplier) {
219
220 std::vector<std::vector<Real>> tmp;
221 tmp.push_back(y);
222 tmp.push_back(x);
223
224 if (y.empty()) {
225 tmp[0].resize(A.rows());
226 std::iota(tmp[0].begin(), tmp[0].end(), 0.0);
227 }
228
229 if (x.empty()) {
230 tmp[1].resize(A.columns());
231 std::iota(tmp[1].begin(), tmp[1].end(), 0.0);
232 }
233
234 LaplaceInterpolation interpolation(
235 [&A](const std::vector<Size>& coordinates) {
236 return A(coordinates[0], coordinates[1]);
237 },
238 tmp, relTol, maxIterMultiplier);
239
240 for (Size i = 0; i < A.rows(); ++i) {
241 for (Size j = 0; j < A.columns(); ++j) {
242 if (A(i, j) == Null<Real>())
243 A(i, j) = interpolation({i, j});
244 }
245 }
246 }
247
248} // namespace QuantLib
Biconjugate gradient stabilized method.
1-D array used in linear algebra.
Definition: array.hpp:52
BiCGStabResult solve(const Array &b, const Array &x0=Array()) const
Definition: bicgstab.cpp:37
std::vector< Size > fullCoordinates(const std::vector< Size > &projectedCoordinates) const
ext::shared_ptr< FdmLinearOpLayout > layout_
LaplaceInterpolation(std::function< Real(const std::vector< Size > &)> y, std::vector< std::vector< Real > > x, Real relTol=1E-6, Size maxIterMultiplier=10)
std::vector< Size > projectedCoordinates(const std::vector< Size > &coordinates) const
std::function< Real(const std::vector< Size > &)> y_
std::vector< std::vector< Real > > x_
Real operator()(const std::vector< Size > &coordinates) const
Matrix used in linear algebra.
Definition: matrix.hpp:41
Size rows() const
Definition: matrix.hpp:504
Size columns() const
Definition: matrix.hpp:508
template class providing a null value for a given type.
Definition: null.hpp:76
#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
One-dimensional simple FDM mesher object working on an index.
composite pattern for linear operators
memory layout of a fdm linear operator
FdmMesher which is a composite of Fdm1dMesher.
Real Time
continuous quantity with 1-year units
Definition: types.hpp:62
QL_REAL Real
real number
Definition: types.hpp:50
std::size_t Size
size of a container
Definition: types.hpp:58
Laplace interpolation of missing values.
matrix used in linear algebra.
Definition: any.hpp:35
Array prod(const SparseMatrix &A, const Array &x)
void laplaceInterpolation(Matrix &A, const std::vector< Real > &x, const std::vector< Real > &y, Real relTol, Size maxIterMultiplier)
boost::numeric::ublas::compressed_matrix< Real > SparseMatrix
STL namespace.
ext::shared_ptr< YieldTermStructure > r
One-dimensional mesher build from a given set of points.
second derivative operator
typedef for boost sparse matrix class
general triple band linear operator
std::uint64_t x_