/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.market.curve.interpolator; import java.io.Serializable; import com.opengamma.strata.collect.array.DoubleArray; /** * Extrapolator implementation. * <p> * The extrapolant is {@code exp(f(x))} where {@code f(x)} is a linear function * which is smoothly connected with a log-interpolator {@code exp(F(x))}. */ final class LogLinearCurveExtrapolator implements CurveExtrapolator, Serializable { /** * The serialization version id. */ private static final long serialVersionUID = 1L; /** * The extrapolator name. */ public static final String NAME = "LogLinear"; /** * The extrapolator instance. */ public static final CurveExtrapolator INSTANCE = new LogLinearCurveExtrapolator(); /** * The epsilon value. */ private static final double EPS = 1e-8; /** * Restricted constructor. */ private LogLinearCurveExtrapolator() { } // resolve instance private Object readResolve() { return INSTANCE; } //------------------------------------------------------------------------- @Override public String getName() { return NAME; } @Override public BoundCurveExtrapolator bind(DoubleArray xValues, DoubleArray yValues, BoundCurveInterpolator interpolator) { return new Bound(xValues, yValues, interpolator); } //------------------------------------------------------------------------- @Override public String toString() { return NAME; } //------------------------------------------------------------------------- /** * Bound extrapolator. */ static class Bound implements BoundCurveExtrapolator { private final int nodeCount; private final double firstXValue; private final double firstYValue; private final double firstYValueLog; private final double lastXValue; private final double lastYValue; private final double lastYValueLog; private final double eps; private final double leftGradient; private final double leftResValueInterpolator; private final DoubleArray leftSens; private final double rightGradient; private final double rightResValueInterpolator; private final DoubleArray rightSens; Bound(DoubleArray xValues, DoubleArray yValues, BoundCurveInterpolator interpolator) { this.nodeCount = xValues.size(); this.firstXValue = xValues.get(0); this.firstYValue = yValues.get(0); this.firstYValueLog = Math.log(firstYValue); this.lastXValue = xValues.get(nodeCount - 1); this.lastYValue = yValues.get(nodeCount - 1); this.lastYValueLog = Math.log(lastYValue); this.eps = EPS * (lastXValue - firstXValue); // left this.leftGradient = interpolator.firstDerivative(firstXValue) / interpolator.interpolate(firstXValue); this.leftResValueInterpolator = interpolator.interpolate(firstXValue + eps); this.leftSens = interpolator.parameterSensitivity(firstXValue + eps); // right this.rightGradient = interpolator.firstDerivative(lastXValue) / interpolator.interpolate(lastXValue); this.rightResValueInterpolator = interpolator.interpolate(lastXValue - eps); this.rightSens = interpolator.parameterSensitivity(lastXValue - eps); } //------------------------------------------------------------------------- @Override public double leftExtrapolate(double xValue) { return Math.exp(firstYValueLog + (xValue - firstXValue) * leftGradient); } @Override public double leftExtrapolateFirstDerivative(double xValue) { return leftGradient * Math.exp(firstYValueLog + (xValue - firstXValue) * leftGradient); } @Override public DoubleArray leftExtrapolateParameterSensitivity(double xValue) { double[] result = leftSens.toArray(); double resValueExtrapolator = leftExtrapolate(xValue); double factor1 = (xValue - firstXValue) / eps; double factor2 = factor1 * resValueExtrapolator / leftResValueInterpolator; int n = result.length; for (int i = 1; i < n; i++) { result[i] *= factor2; } result[0] = result[0] * factor2 + (1d - factor1) * resValueExtrapolator / firstYValue; return DoubleArray.ofUnsafe(result); } //------------------------------------------------------------------------- @Override public double rightExtrapolate(double xValue) { return Math.exp(lastYValueLog + (xValue - lastXValue) * rightGradient); } @Override public double rightExtrapolateFirstDerivative(double xValue) { return rightGradient * Math.exp(lastYValueLog + (xValue - lastXValue) * rightGradient); } @Override public DoubleArray rightExtrapolateParameterSensitivity(double xValue) { double[] result = rightSens.toArray(); double resValueExtrapolator = rightExtrapolate(xValue); double factor1 = (xValue - lastXValue) / eps; double factor2 = factor1 * resValueExtrapolator / rightResValueInterpolator; int n = result.length; for (int i = 0; i < n - 1; i++) { result[i] *= -factor2; } result[n - 1] = (1d + factor1) * resValueExtrapolator / lastYValue - result[n - 1] * factor2; return DoubleArray.ofUnsafe(result); } } }