Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions QuantLib.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,7 @@
<ClInclude Include="ql\math\interpolations\kernelinterpolation2d.hpp" />
<ClInclude Include="ql\math\interpolations\lagrangeinterpolation.hpp" />
<ClInclude Include="ql\math\interpolations\linearinterpolation.hpp" />
<ClInclude Include="ql\math\interpolations\linearthenflatinterpolation.hpp" />
<ClInclude Include="ql\math\interpolations\loginterpolation.hpp" />
<ClInclude Include="ql\math\interpolations\mixedinterpolation.hpp" />
<ClInclude Include="ql\math\interpolations\multicubicspline.hpp" />
Expand Down
1 change: 1 addition & 0 deletions QuantLib.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -4527,6 +4527,7 @@
<ClInclude Include="ql\pricingengines\vanilla\cashdividendeuropeanengine.hpp">
<Filter>pricingengines\vanilla</Filter>
</ClInclude>
<ClInclude Include="ql\math\interpolations\linearthenflatinterpolation.hpp" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ql\methods\montecarlo\brownianbridge.cpp">
Expand Down
1 change: 1 addition & 0 deletions ql/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1479,6 +1479,7 @@ set(QL_HEADERS
math/interpolations/kernelinterpolation2d.hpp
math/interpolations/lagrangeinterpolation.hpp
math/interpolations/linearinterpolation.hpp
math/interpolations/linearthenflatinterpolation.hpp
math/interpolations/loginterpolation.hpp
math/interpolations/mixedinterpolation.hpp
math/interpolations/multicubicspline.hpp
Expand Down
1 change: 1 addition & 0 deletions ql/math/interpolations/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ this_include_HEADERS = \
kernelinterpolation2d.hpp \
lagrangeinterpolation.hpp \
linearinterpolation.hpp \
linearthenflatinterpolation.hpp \
loginterpolation.hpp \
mixedinterpolation.hpp \
multicubicspline.hpp \
Expand Down
1 change: 1 addition & 0 deletions ql/math/interpolations/all.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <ql/math/interpolations/kernelinterpolation2d.hpp>
#include <ql/math/interpolations/lagrangeinterpolation.hpp>
#include <ql/math/interpolations/linearinterpolation.hpp>
#include <ql/math/interpolations/linearthenflatinterpolation.hpp>
#include <ql/math/interpolations/loginterpolation.hpp>
#include <ql/math/interpolations/mixedinterpolation.hpp>
#include <ql/math/interpolations/multicubicspline.hpp>
Expand Down
88 changes: 88 additions & 0 deletions ql/math/interpolations/cubicinterpolation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,94 @@ namespace QuantLib {

}

class Cubic__FritschButland_nonMonotonic : public Cubic {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to add a class for every combination of constructor arguments in Cubic? Why not just use Cubic directly and pass any arguments that you like?

public:
Cubic__FritschButland_nonMonotonic()
: Cubic(CubicInterpolation::FritschButland, false,
CubicInterpolation::SecondDerivative, 0.0,
CubicInterpolation::SecondDerivative, 0.0) {}
};

class Cubic__FritschButland_monotonic : public Cubic {
public:
Cubic__FritschButland_monotonic()
: Cubic(CubicInterpolation::FritschButland, true,
CubicInterpolation::SecondDerivative, 0.0,
CubicInterpolation::SecondDerivative, 0.0) {}
};

class Cubic__NaturalSpline : public Cubic {
public:
Cubic__NaturalSpline()
: Cubic(CubicInterpolation::Spline, false,
CubicInterpolation::SecondDerivative, 0.0,
CubicInterpolation::SecondDerivative, 0.0) {}
};

class Cubic__Monotonic_NaturalSpline : public Cubic {
public:
Cubic__Monotonic_NaturalSpline()
: Cubic(CubicInterpolation::Spline, true,
CubicInterpolation::SecondDerivative, 0.0,
CubicInterpolation::SecondDerivative, 0.0) {}
};

class Cubic__SplineOvershootingMinimization1 : public Cubic {
public:
Cubic__SplineOvershootingMinimization1()
: Cubic(CubicInterpolation::SplineOM1, false,
CubicInterpolation::SecondDerivative, 0.0,
CubicInterpolation::SecondDerivative, 0.0) {}
};

class Cubic__SplineOvershootingMinimization2 : public Cubic {
public:
Cubic__SplineOvershootingMinimization2()
: Cubic(CubicInterpolation::SplineOM2, false,
CubicInterpolation::SecondDerivative, 0.0,
CubicInterpolation::SecondDerivative, 0.0) {}
};

class Cubic__Akima : public Cubic {
public:
Cubic__Akima()
: Cubic(CubicInterpolation::Akima, false,
CubicInterpolation::SecondDerivative, 0.0,
CubicInterpolation::SecondDerivative, 0.0) {}
};

class Cubic__Parabolic : public Cubic {
public:
Cubic__Parabolic()
: Cubic(CubicInterpolation::Parabolic, false,
CubicInterpolation::SecondDerivative, 0.0,
CubicInterpolation::SecondDerivative, 0.0) {}
};

class Cubic__MonotonicParabolic : public Cubic {
public:
Cubic__MonotonicParabolic()
: Cubic(CubicInterpolation::Parabolic, true,
CubicInterpolation::SecondDerivative, 0.0,
CubicInterpolation::SecondDerivative, 0.0) {}
};

class Cubic__Kruger : public Cubic {
public:
Cubic__Kruger()
: Cubic(CubicInterpolation::Kruger, false,
CubicInterpolation::SecondDerivative, 0.0,
CubicInterpolation::SecondDerivative, 0.0) {}
};

class Cubic__MonotonicKruger : public Cubic {
public:
Cubic__MonotonicKruger()
: Cubic(CubicInterpolation::Kruger, true,
CubicInterpolation::SecondDerivative, 0.0,
CubicInterpolation::SecondDerivative, 0.0) {}
};

}

#endif
117 changes: 117 additions & 0 deletions ql/math/interpolations/linearthenflatinterpolation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
Copyright (C) 2026 SoftSolution

This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/

QuantLib is free software: you can redistribute it and/or modify it
under the terms of the QuantLib license. You should have received a
copy of the license along with this program; if not, please email
<quantlib-dev@lists.sf.net>. The license is also available online at
<https://www.quantlib.org/license.shtml>.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
*/

/*! \file linearthenflatinterpolation.hpp
\brief linear interpolation between discrete points, that after last point becomes flat
*/

#ifndef quantlib_linearthenflat_interpolation_hpp
#define quantlib_linearthenflat_interpolation_hpp

#include <ql/math/interpolation.hpp>
#include <vector>

namespace QuantLib {

namespace detail {
template <class I1, class I2> class LinearThenFlatInterpolationImpl;
}

//! %Linear-then-flat interpolation between discrete points
class LinearThenFlatInterpolation : public Interpolation {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will probably be less work to implement this using the existing MixedInterpolation in ql/math/interpolations/mixedinterpolation.hpp

You can switch between Linear and ForwardFlat or BackwardFlat.

public:
/*! \pre the \f$ x \f$ values must be sorted. */
template <class I1, class I2>
LinearThenFlatInterpolation(const I1& xBegin,
const I1& xEnd,
const I2& yBegin) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to pass a switch point like MixedLinearCubic does. Right now your code switches after xEnd-xBegin points, i.e. after the end of the data. So it's always linear.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's intentional. It's not a mixed interpolation, it's a linear interpolation with flat extrapolation (am I right, @ArsenP0doba?)

If that's the case, though (and it's a legitimate use case, I think), we should probably think of a way to specify the extrapolation type for a generic interpolation, instead of having classes like LinearThenFlat, CubicThanFlat etc.

@eltoder eltoder May 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, can't this already be done with the FlatExtrapolator class?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I was thinking of something like FlatExtrapolator but I didn't remember we have it already :)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, FlatExtrapolator is not always useable out of the box, for instance with PiecewiseYieldCurve. We should probably add the missing bits.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lballabio ah yes, we are missing a factory class for it.

impl_ = ext::shared_ptr<Interpolation::Impl>(new
detail::LinearThenFlatInterpolationImpl<I1, I2>(xBegin, xEnd, yBegin));
impl_->update();
}
};

//! %Linear-then-flat interpolation factory and traits
class LinearThenFlat {
public:
template <class I1, class I2>
Interpolation interpolate(const I1& xBegin,
const I1& xEnd,
const I2& yBegin) const {
return LinearThenFlatInterpolation(xBegin, xEnd, yBegin);
}
static const bool global = false;
static const Size requiredPoints = 2;
};

namespace detail {

template <class I1, class I2>
class LinearThenFlatInterpolationImpl : public Interpolation::templateImpl<I1, I2> {
public:
LinearThenFlatInterpolationImpl(const I1& xBegin,
const I1& xEnd,
const I2& yBegin)
: Interpolation::templateImpl<I1, I2>(xBegin, xEnd, yBegin),
primitiveConst_(xEnd-xBegin), s_(xEnd-xBegin),
primitive_(xEnd-xBegin), n_(xEnd-xBegin) {}
void update() override {
primitiveConst_[0] = 0.0;
for (Size i=1; i<Size(this->xEnd_-this->xBegin_); ++i) {
Real dx = this->xBegin_[i]-this->xBegin_[i-1];
s_[i-1] = (this->yBegin_[i]-this->yBegin_[i-1])/dx;
primitiveConst_[i] = primitiveConst_[i-1]
+ dx*(this->yBegin_[i-1] + 0.5*dx*s_[i-1]);
}
}
Real value(Real x) const override {
if (x >= this->xBegin_[n_-1])
return this->yBegin_[n_-1];
Size i = this->locate(x);
return this->yBegin_[i] + (x-this->xBegin_[i])*s_[i];
}
Real primitive(Real x) const override {
// TODO: fix primitive implementation
if (x >= this->xBegin_[n_-1]) {
Size i = this->locate(x);
Real dx = x-this->xBegin_[i];
return primitive_[i] + dx*this->yBegin_[i];
}
Size i = this->locate(x);
Real dx = x-this->xBegin_[i];
return primitiveConst_[i] + dx*(this->yBegin_[i] + 0.5*dx*s_[i]);
}
Real derivative(Real x) const override {
if (x >= this->xBegin_[n_-1])
return 0.0;
Size i = this->locate(x);
return s_[i];
}
Real secondDerivative(Real) const override {
return 0.0;
}
private:
std::vector<Real> primitiveConst_, s_;
std::vector<Real> primitive_;
Size n_;
};

}

}

#endif