/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.math.interpolation;
import org.apache.commons.lang.Validate;
import org.apache.commons.math.FunctionEvaluationException;
import org.apache.commons.math.MaxIterationsExceededException;
import org.apache.commons.math.analysis.UnivariateRealFunction;
import org.apache.commons.math.analysis.solvers.BrentSolver;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.interpolation.data.ArrayInterpolator1DDataBundle;
import com.opengamma.analytics.math.interpolation.data.InterpolationBoundedValues;
import com.opengamma.analytics.math.interpolation.data.Interpolator1DDataBundle;
import com.opengamma.analytics.math.util.wrapper.CommonsMathWrapper;
/**
* A one-dimensional interpolator using a vector of 12 values as input(seasonalities). The interpolated value of the function
* <i>y</i> at <i>x</i> between two data points <i>(x<sub>1</sub>,
* y<sub>1</sub>)</i> and <i>(x<sub>2</sub>, y<sub>2</sub>)</i> is given by a step interpolation using the following data:
* <br>x<sub>i</sub>=x<sub>1</sub>+(x<sub>2</sub>-x<sub>1</sub>)/12<br>
* <br>y<sub>i</sub>=y<sub>1</sub>Π<sub>j=0</sub><sup>i</sup> (1+growth+seasonalities[j])<br>
* where growth is the solution of the following equation
* <br>0=y<sub>1</sub>Π<sub>j=0</sub><sup>i</sup> (1+growth)-y<sub>2</sub><br>
*
*/
public class LogLinearWithSeasonalitiesInterpolator1D extends Interpolator1D {
private static final long serialVersionUID = 1L;
/**
* The number of month in a year.
*/
private static final int NB_MONTH = 12;
/**
* The seasonal factors.
*/
private final double[] _seasonalValues;
/**
* Construct a LogLinearWithSeasonalitiesInterpolator1D from seasonal values.
* @param monthlyFactors The seasonal values
*
*/
public LogLinearWithSeasonalitiesInterpolator1D(final double[] monthlyFactors) {
Validate.notNull(monthlyFactors, "Monthly factors");
Validate.isTrue(monthlyFactors.length == 11, "Monthly factors with incorrect length; should be 11");
double sum = 0.0;
final double[] seasonalValues = new double[NB_MONTH];
for (int loopmonth = 0; loopmonth < NB_MONTH - 1; loopmonth++) {
seasonalValues[loopmonth] = monthlyFactors[loopmonth];
sum = sum + monthlyFactors[loopmonth];
}
seasonalValues[NB_MONTH - 1] = 1.0 - sum;
_seasonalValues = seasonalValues;
}
@Override
public Double interpolate(final Interpolator1DDataBundle model, final Double value) {
Validate.notNull(value, "value");
Validate.notNull(model, "data bundle");
final InterpolationBoundedValues boundedValues = model.getBoundedValues(value);
final Double x1 = boundedValues.getLowerBoundKey();
final Double y1 = boundedValues.getLowerBoundValue();
if (model.getLowerBoundIndex(value) == model.size() - 1) {
return y1;
}
final Double x2 = boundedValues.getHigherBoundKey();
final Double y2 = boundedValues.getHigherBoundValue();
// nodes and values for the step interpolator
final double[] nodes = new double[12];
final double[] values = new double[12];
nodes[0] = x1;
values[0] = y1;
// solver used to find the growth
final BrentSolver solver = new BrentSolver();
// definition of the function to minimize
final Function1D<Double, Double> function = new Function1D<Double, Double>() {
@SuppressWarnings("synthetic-access")
@Override
public Double evaluate(final Double x) {
double result = y1;
for (int loopmonth = 0; loopmonth < NB_MONTH; loopmonth++) {
result = result * (1 + x + _seasonalValues[loopmonth]);
}
return result - y2;
}
};
// the initial guess for the solver is the solution when all seasonal values are set to 0.
final double initialGuess = Math.pow(y2 / y1, 1 / 12.0) - 1.0;
// We solve the equation define by the function and use the result to calculate values, nodes are also calculates.
final UnivariateRealFunction f = CommonsMathWrapper.wrapUnivariate(function);
double growth;
try {
growth = solver.solve(f, -.5, .5, initialGuess);
for (int loopmonth = 1; loopmonth < NB_MONTH; loopmonth++) {
nodes[loopmonth] = x1 + loopmonth * (x2 - x1) / 12.0;
values[loopmonth] = values[loopmonth - 1] * (1 + growth + _seasonalValues[loopmonth]);
}
} catch (final MaxIterationsExceededException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
} catch (final FunctionEvaluationException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
final Interpolator1DDataBundle dataBundle = getDataBundleFromSortedArrays(nodes, values);
final StepInterpolator1D stepInterpolator = new StepInterpolator1D();
return stepInterpolator.interpolate(dataBundle, value);
}
@Override
public Interpolator1DDataBundle getDataBundle(final double[] x, final double[] y) {
return new ArrayInterpolator1DDataBundle(x, y);
}
@Override
public Interpolator1DDataBundle getDataBundleFromSortedArrays(final double[] x, final double[] y) {
return new ArrayInterpolator1DDataBundle(x, y, true);
}
@Override
public double[] getNodeSensitivitiesForValue(final Interpolator1DDataBundle data, final Double value) {
return getFiniteDifferenceSensitivities(data, value);
}
}