Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
stabilisedglls.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2017 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19/*! \file qle/math/stabilisedglls.hpp
20 \brief Numerically stabilised general linear least squares
21 \ingroup math
22*/
23
24#ifndef quantext_stabilised_glls_hpp
25#define quantext_stabilised_glls_hpp
26
27#include <ql/math/array.hpp>
28#include <ql/math/comparison.hpp>
29#include <ql/math/generallinearleastsquares.hpp>
30
31#include <boost/accumulators/accumulators.hpp>
32#include <boost/accumulators/statistics/mean.hpp>
33#include <boost/accumulators/statistics/stats.hpp>
34#include <boost/accumulators/statistics/variance.hpp>
35#include <boost/make_shared.hpp>
36#include <boost/type_traits.hpp>
37
38#include <vector>
39
40namespace QuantExt {
41using namespace QuantLib;
42using namespace boost::accumulators;
43
44//! Numerically stabilised general linear least squares
45/*! The input data is linearly transformed before performing the linear least squares fit.
46 The linear least squares fit on the transformed data is done using the
47 GeneralLinearLeastSquares class.
48 \ingroup math
49 */
50
52public:
53 enum Method {
54 None, // No stabilisation
55 MaxAbs, // Divide x and y values by max of abs of values (per x coordinate, y)
56 MeanStdDev // Subtract mean and divide by std dev (per x coordinate, y)
57 };
58 template <class xContainer, class yContainer, class vContainer>
59 StabilisedGLLS(const xContainer& x, const yContainer& y, const vContainer& v, const Method method = MeanStdDev);
60
61 const Array& transformedCoefficients() const { return glls_->coefficients(); }
62 const Array& transformedResiduals() const { return glls_->residuals(); }
63 const Array& transformedStandardErrors() const { return glls_->standardErrors(); }
64 const Array& transformedError() const { return glls_->error(); }
65
66 //! Transformation parameters (u => (u + shift) * multiplier for u = x, y)
67 const Array& xMultiplier() const { return xMultiplier_; }
68 const Array& xShift() const { return xShift_; }
69 const Real yMultiplier() const { return yMultiplier_; }
70 const Real yShift() const { return yShift_; }
71
72 Size size() const { return glls_->residuals().size(); }
73 Size dim() const { return glls_->coefficients().size(); }
74
75 //! evaluate regression function in terms of original x, y
76 template <class xType, class vContainer>
77 Real eval(xType x, vContainer& v, typename boost::enable_if<typename boost::is_arithmetic<xType>::type>::type* = 0);
78
79 //! evaluate regression function in terms of original x, y
80 template <class xType, class vContainer>
81 Real eval(xType x, vContainer& v,
82 typename boost::disable_if<typename boost::is_arithmetic<xType>::type>::type* = 0);
83
84protected:
88 QuantLib::ext::shared_ptr<GeneralLinearLeastSquares> glls_;
89
90 template <class xContainer, class yContainer, class vContainer>
91 void calculate(
92 xContainer x, yContainer y, vContainer v,
93 typename boost::enable_if<typename boost::is_arithmetic<typename xContainer::value_type>::type>::type* = 0);
94
95 template <class xContainer, class yContainer, class vContainer>
96 void calculate(
97 xContainer x, yContainer y, vContainer v,
98 typename boost::disable_if<typename boost::is_arithmetic<typename xContainer::value_type>::type>::type* = 0);
99};
100
101template <class xContainer, class yContainer, class vContainer>
102inline StabilisedGLLS::StabilisedGLLS(const xContainer& x, const yContainer& y, const vContainer& v,
103 const Method method)
104 : a_(v.end() - v.begin(), 0.0), err_(v.end() - v.begin(), 0.0), residuals_(y.end() - y.begin()),
105 standardErrors_(v.end() - v.begin()), method_(method) {
106 calculate(x, y, v);
107}
108
109template <class xContainer, class yContainer, class vContainer>
111 xContainer x, yContainer y, vContainer v,
112 typename boost::enable_if<typename boost::is_arithmetic<typename xContainer::value_type>::type>::type*) {
113
114 std::vector<Real> xData(x.end() - x.begin(), 0.0), yData(y.end() - y.begin(), 0.0);
115 xMultiplier_ = Array(1, 1.0);
116 xShift_ = Array(1, 0.0);
117 yMultiplier_ = 1.0;
118 yShift_ = 0.0;
119
120 switch (method_) {
121 case None:
122 break;
123 case MaxAbs: {
124 Real mx = 0.0, my = 0.0;
125 for (Size i = 0; i < static_cast<Size>(x.end() - x.begin()); ++i) {
126 mx = std::max(std::abs(x[i]), mx);
127 }
128 if (!QuantLib::close_enough(mx, 0.0))
129 xMultiplier_[0] = 1.0 / mx;
130 for (Size i = 0; i < static_cast<Size>(y.end() - y.begin()); ++i) {
131 my = std::max(std::abs(y[i]), my);
132 }
133 if (!QuantLib::close_enough(my, 0.0))
134 yMultiplier_ = 1.0 / my;
135 break;
136 }
137 case MeanStdDev: {
138 accumulator_set<Real, stats<boost::accumulators::tag::mean, boost::accumulators::tag::variance> > acc;
139 for (Size i = 0; i < static_cast<Size>(x.end() - x.begin()); ++i) {
140 acc(x[i]);
141 }
142 xShift_[0] = -mean(acc);
143 Real tmp = boost::accumulators::variance(acc);
144 if (!QuantLib::close_enough(tmp, 0.0))
145 xMultiplier_[0] = 1.0 / std::sqrt(tmp);
146 accumulator_set<Real, stats<boost::accumulators::tag::mean, boost::accumulators::tag::variance> > acc2;
147 for (Size i = 0; i < static_cast<Size>(y.end() - y.begin()); ++i) {
148 acc2(y[i]);
149 }
150 yShift_ = -mean(acc2);
151 Real tmp2 = boost::accumulators::variance(acc2);
152 if (!QuantLib::close_enough(tmp2, 0.0))
153 yMultiplier_ = 1.0 / std::sqrt(tmp2);
154 break;
155 }
156 default:
157 QL_FAIL("unknown stabilisation method");
158 }
159
160 for (Size i = 0; i < static_cast<Size>(x.end() - x.begin()); ++i) {
161 xData[i] = (x[i] + xShift_[0]) * xMultiplier_[0];
162 }
163 for (Size i = 0; i < static_cast<Size>(y.end() - y.begin()); ++i) {
164 yData[i] = (y[i] + yShift_) * yMultiplier_;
165 }
166
167 glls_ = QuantLib::ext::make_shared<GeneralLinearLeastSquares>(xData, yData, v);
168}
169
170template <class xContainer, class yContainer, class vContainer>
172 xContainer x, yContainer y, vContainer v,
173 typename boost::disable_if<typename boost::is_arithmetic<typename xContainer::value_type>::type>::type*) {
174
175 QL_REQUIRE(x.end() - x.begin() > 0, "StabilisedGLLS::calculate(): x container is empty");
176 QL_REQUIRE(x[0].end() - x[0].begin() > 0, "StabilisedGLLS:calculate(): x contains empty point(s)");
177
178 std::vector<Array> xData(x.end() - x.begin(), Array(x[0].end() - x[0].begin(), 0.0));
179 std::vector<Real> yData(y.end() - y.begin(), 0.0);
180 xMultiplier_ = Array(x[0].end() - x[0].begin(), 1.0);
181 xShift_ = Array(x[0].end() - x[0].begin(), 0.0);
182 yMultiplier_ = 1.0;
183 yShift_ = 0.0;
184
185 switch (method_) {
186 case None:
187 break;
188 case MaxAbs: {
189 Array m(x[0].end() - x[0].begin(), 0.0);
190 Real my = 0.0;
191 for (Size i = 0; i < static_cast<Size>(x.end() - x.begin()); ++i) {
192 for (Size j = 0; j < m.size(); ++j) {
193 m[j] = std::max(std::abs(x[i][j]), m[j]);
194 }
195 }
196 for (Size j = 0; j < m.size(); ++j) {
197 if (!QuantLib::close_enough(m[j], 0.0))
198 xMultiplier_[j] = 1.0 / m[j];
199 }
200 for (Size i = 0; i < static_cast<Size>(y.end() - y.begin()); ++i) {
201 my = std::max(std::abs(y[i]), my);
202 }
203 if (!QuantLib::close_enough(my, 0.0))
204 yMultiplier_ = 1.0 / my;
205 break;
206 }
207 case MeanStdDev: {
208 std::vector<accumulator_set<Real, stats<boost::accumulators::tag::mean, boost::accumulators::tag::variance> > > acc(x[0].end() - x[0].begin());
209 for (Size i = 0; i < static_cast<Size>(x.end() - x.begin()); ++i) {
210 for (Size j = 0; j < acc.size(); ++j) {
211 acc[j](x[i][j]);
212 }
213 }
214 for (Size j = 0; j < acc.size(); ++j) {
215 xShift_[j] = -mean(acc[j]);
216 Real tmp = boost::accumulators::variance(acc[j]);
217 if (!QuantLib::close_enough(tmp, 0.0))
218 xMultiplier_[j] = 1.0 / std::sqrt(tmp);
219 }
220 accumulator_set<Real, stats<boost::accumulators::tag::mean, boost::accumulators::tag::variance> > acc2;
221 for (Size i = 0; i < static_cast<Size>(y.end() - y.begin()); ++i) {
222 acc2(y[i]);
223 }
224 yShift_ = -mean(acc2);
225 Real tmp2 = boost::accumulators::variance(acc2);
226 if (!QuantLib::close_enough(tmp2, 0.0))
227 yMultiplier_ = 1.0 / std::sqrt(tmp2);
228 break;
229 }
230 default:
231 QL_FAIL("unknown stabilisation method");
232 break;
233 }
234
235 for (Size i = 0; i < static_cast<Size>(x.end() - x.begin()); ++i) {
236 for (Size j = 0; j < xMultiplier_.size(); ++j) {
237 xData[i][j] = (x[i][j] + xShift_[j]) * xMultiplier_[j];
238 }
239 }
240 for (Size i = 0; i < static_cast<Size>(y.end() - y.begin()); ++i) {
241 yData[i] = (y[i] + yShift_) * yMultiplier_;
242 }
243
244 glls_ = QuantLib::ext::make_shared<GeneralLinearLeastSquares>(xData, yData, v);
245}
246
247template <class xType, class vContainer>
248Real StabilisedGLLS::eval(xType x, vContainer& v,
249 typename boost::enable_if<typename boost::is_arithmetic<xType>::type>::type*) {
250 QL_REQUIRE(v.size() == glls_->dim(),
251 "StabilisedGLLS::eval(): v size (" << v.size() << ") must be equal to dim (" << glls_->dim());
252 Real tmp = 0.0;
253 for (Size i = 0; i < v.size(); ++i) {
254 tmp += glls_->coefficients()[i] * v[i]((x + xShift_[0]) * xMultiplier_[0]);
255 }
256 return tmp / yMultiplier_ - yShift_;
257}
258
259template <class xType, class vContainer>
260Real StabilisedGLLS::eval(xType x, vContainer& v,
261 typename boost::disable_if<typename boost::is_arithmetic<xType>::type>::type*) {
262 QL_REQUIRE(v.size() == glls_->dim(),
263 "StabilisedGLLS::eval(): v size (" << v.size() << ") must be equal to dim (" << glls_->dim());
264 Real tmp = 0.0;
265 for (Size i = 0; i < v.size(); ++i) {
266 xType xNew(x.end() - x.begin());
267 for (Size j = 0; j < static_cast<Size>(x.end() - x.begin()); ++j) {
268 xNew[j] = (x[j] + xShift_[j]) * xMultiplier_[j];
269 }
270 tmp += glls_->coefficients()[i] * v[i](xNew);
271 }
272 return tmp / yMultiplier_ - yShift_;
273}
274
275} // namespace QuantExt
276
277#endif
Numerically stabilised general linear least squares.
const Real yShift() const
void calculate(xContainer x, yContainer y, vContainer v, typename boost::enable_if< typename boost::is_arithmetic< typename xContainer::value_type >::type >::type *=0)
const Array & transformedStandardErrors() const
const Array & transformedCoefficients() const
Real eval(xType x, vContainer &v, typename boost::enable_if< typename boost::is_arithmetic< xType >::type >::type *=0)
evaluate regression function in terms of original x, y
const Real yMultiplier() const
const Array & xMultiplier() const
Transformation parameters (u => (u + shift) * multiplier for u = x, y)
const Array & transformedResiduals() const
const Array & xShift() const
const Array & transformedError() const
StabilisedGLLS(const xContainer &x, const yContainer &y, const vContainer &v, const Method method=MeanStdDev)
QuantLib::ext::shared_ptr< GeneralLinearLeastSquares > glls_
std::vector< Real > a_