diff --git a/ql/math/optimization/costfunction.hpp b/ql/math/optimization/costfunction.hpp index d62fba398c..e5eb280bcb 100644 --- a/ql/math/optimization/costfunction.hpp +++ b/ql/math/optimization/costfunction.hpp @@ -96,6 +96,16 @@ namespace QuantLib { virtual Real finiteDifferenceEpsilon() const { return 1e-8; } }; + template + class SimpleCostFunction : public CostFunction { + public: + explicit SimpleCostFunction(ValuesFn values) : values_(std::move(values)) {} + + Array values(const Array& x) const override { return values_(x); } + private: + ValuesFn values_; + }; + class ParametersTransformation { public: virtual ~ParametersTransformation() = default; diff --git a/ql/termstructures/globalbootstrap.hpp b/ql/termstructures/globalbootstrap.hpp index a37d18b422..06b973a3b3 100644 --- a/ql/termstructures/globalbootstrap.hpp +++ b/ql/termstructures/globalbootstrap.hpp @@ -252,66 +252,39 @@ template void GlobalBootstrap::calculate() const { } // setup cost function - class TargetFunction : public CostFunction { - public: - // NOTE: TargetFunction retains passed pointers, so they must point to objects - // that outlive the call to optimizer->minimize(). We use pointers instead of - // const refs to avoid accidental binding to temporaries. - TargetFunction(Size firstHelper, - Size numberHelpers, - const std::function* additionalErrors, - Curve* ts, - const std::vector* lowerBounds, - const std::vector* upperBounds) - : firstHelper_(firstHelper), numberHelpers_(numberHelpers), - additionalErrors_(*additionalErrors), ts_(ts), - lowerBounds_(*lowerBounds), upperBounds_(*upperBounds) { - QL_REQUIRE(additionalErrors != nullptr, "null additionalErrors"); - QL_REQUIRE(lowerBounds != nullptr && upperBounds != nullptr, "null bounds"); - } + auto transformDirect = [&](const Real x, const Size i) { + return (std::atan(x) + M_PI_2) / M_PI * (upperBounds[i] - lowerBounds[i]) + lowerBounds[i]; + }; - Real transformDirect(const Real x, const Size i) const { - return (std::atan(x) + M_PI_2) / M_PI * (upperBounds_[i] - lowerBounds_[i]) + lowerBounds_[i]; - } + auto transformInverse = [&](const Real y, const Size i) { + return std::tan((y - lowerBounds[i]) * M_PI / (upperBounds[i] - lowerBounds[i]) - M_PI_2); + }; - Real transformInverse(const Real y, const Size i) const { - return std::tan((y - lowerBounds_[i]) * M_PI / (upperBounds_[i] - lowerBounds_[i]) - M_PI_2); + SimpleCostFunction cost([&](const Array& x) { + for (Size i = 0; i < x.size(); ++i) { + Traits::updateGuess(ts_->data_, transformDirect(x[i], i), i + 1); } - - Array values(const Array& x) const override { - for (Size i = 0; i < x.size(); ++i) { - Traits::updateGuess(ts_->data_, transformDirect(x[i], i), i + 1); - } - ts_->interpolation_.update(); - std::vector result(numberHelpers_); - for (Size i = 0; i < numberHelpers_; ++i) { - result[i] = ts_->instruments_[firstHelper_ + i]->quote()->value() - - ts_->instruments_[firstHelper_ + i]->impliedQuote(); - } - if (additionalErrors_) { - Array tmp = additionalErrors_(); - result.resize(numberHelpers_ + tmp.size()); - for (Size i = 0; i < tmp.size(); ++i) { - result[numberHelpers_ + i] = tmp[i]; - } + ts_->interpolation_.update(); + std::vector result(numberHelpers_); + for (Size i = 0; i < numberHelpers_; ++i) { + result[i] = ts_->instruments_[firstHelper_ + i]->quote()->value() - + ts_->instruments_[firstHelper_ + i]->impliedQuote(); + } + if (additionalErrors_) { + Array tmp = additionalErrors_(); + result.resize(numberHelpers_ + tmp.size()); + for (Size i = 0; i < tmp.size(); ++i) { + result[numberHelpers_ + i] = tmp[i]; } - return Array(result.begin(), result.end()); } - - private: - Size firstHelper_, numberHelpers_; - const std::function& additionalErrors_; - Curve* ts_; - const std::vector &lowerBounds_, &upperBounds_; - }; - TargetFunction cost(firstHelper_, numberHelpers_, &additionalErrors_, ts_, - &lowerBounds, &upperBounds); + return Array(result.begin(), result.end()); + }); // setup guess Array guess(numberBounds); for (Size i = 0; i < numberBounds; ++i) { // just pass zero as the first alive helper, it's not used in the standard QL traits anyway - guess[i] = cost.transformInverse(Traits::guess(i + 1, ts_, validCurve_, 0), i); + guess[i] = transformInverse(Traits::guess(i + 1, ts_, validCurve_, 0), i); } // setup problem