/**
* Copyright (C) 2009 - 2009 by OpenGamma Inc.
*
* Please see distribution for license.
*/
package com.opengamma.analytics.math.integration;
import static org.testng.AssertJUnit.assertEquals;
import org.testng.annotations.Test;
import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.util.test.TestGroup;
/**
* Test.
*/
@Test(groups = TestGroup.UNIT)
public class GaussianQuadratureIntegrator1DTest {
private static final Function1D<Double, Double> ONE = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double x) {
return 1.0;
}
};
private static final Function1D<Double, Double> DF1 = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double x) {
return x * x * x * (x - 4);
}
};
private static final Function1D<Double, Double> F1 = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double x) {
return x * x * x * x * (x / 5. - 1);
}
};
private static final Function1D<Double, Double> DF2 = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double x) {
return Math.exp(-2 * x);
}
};
private static final Function1D<Double, Double> DF3 = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double x) {
return Math.exp(-x * x);
}
};
private static final Function1D<Double, Double> COS = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double x) {
return Math.cos(x);
}
};
private static final Function1D<Double, Double> COS_EXP = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double x) {
return Math.cos(x) * Math.exp(-x * x);
}
};
private static final double EPS = 1e-6;
@Test
public void testGaussLegendre() {
double upper = 2;
double lower = -6;
final Integrator1D<Double, Double> integrator = new GaussLegendreQuadratureIntegrator1D(6);
assertEquals(F1.evaluate(upper) - F1.evaluate(lower), integrator.integrate(DF1, lower, upper), EPS);
lower = -0.56;
upper = 1.4;
assertEquals(F1.evaluate(upper) - F1.evaluate(lower), integrator.integrate(DF1, lower, upper), EPS);
}
@Test
public void testGaussLaguerre() {
final double upper = Double.POSITIVE_INFINITY;
final double lower = 0;
final Integrator1D<Double, Double> integrator = new GaussLaguerreQuadratureIntegrator1D(15);
assertEquals(0.5, integrator.integrate(DF2, lower, upper), EPS);
}
@Test
public void testRungeKutta() {
final RungeKuttaIntegrator1D integrator = new RungeKuttaIntegrator1D();
final double lower = -1;
final double upper = 2;
assertEquals(F1.evaluate(upper) - F1.evaluate(lower), integrator.integrate(DF1, lower, upper), EPS);
}
@Test
public void testGaussJacobi() {
final double upper = 12;
final double lower = -1;
final Integrator1D<Double, Double> integrator = new GaussJacobiQuadratureIntegrator1D(7);
assertEquals(F1.evaluate(upper) - F1.evaluate(lower), integrator.integrate(DF1, lower, upper), EPS);
}
@Test
public void testGaussHermite() {
final double rootPI = Math.sqrt(Math.PI);
final double upper = Double.POSITIVE_INFINITY;
final double lower = Double.NEGATIVE_INFINITY;
final GaussHermiteQuadratureIntegrator1D integrator = new GaussHermiteQuadratureIntegrator1D(10);
assertEquals(rootPI, integrator.integrateFromPolyFunc(ONE), 1e-15);
assertEquals(rootPI, integrator.integrate(DF3, lower, upper), EPS);
}
@Test
public void testGaussHermite2() {
final RungeKuttaIntegrator1D rk = new RungeKuttaIntegrator1D(1e-15);
final Double expected = 2 * rk.integrate(COS_EXP, 0., 10.);
final GaussHermiteQuadratureIntegrator1D gh = new GaussHermiteQuadratureIntegrator1D(11);
final double res1 = gh.integrateFromPolyFunc(COS);
assertEquals(expected, res1, 1e-15); //11 points gets you machine precision
final double res2 = gh.integrate(COS_EXP, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
assertEquals(expected, res2, 1e-15);
}
/**
* This test demonstrates why it is a bad idea to use quadrature methods for non-smooth functions
*/
@Test
public void testBlackFormula() {
final double fwd = 5;
final double strike = 6.5;
final double t = 1.5;
final double vol = 0.35;
final double expected = BlackFormulaRepository.price(fwd, strike, t, vol, true);
final Function1D<Double, Double> func = getBlackIntergrand(fwd, strike, t, vol);
final Function1D<Double, Double> fullIntergrand = new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double x) {
return func.evaluate(x) * Math.exp(-x * x);
}
};
final RungeKuttaIntegrator1D rk = new RungeKuttaIntegrator1D(1e-15);
final double resRK = rk.integrate(fullIntergrand, 0., 10.); //The strike > fwd, so can start the integration at z=0 (i.e. s = fwd)
assertEquals("Runge Kutta", expected, resRK, 1e-15);
final GaussHermiteQuadratureIntegrator1D gh = new GaussHermiteQuadratureIntegrator1D(40);
final double resGH = gh.integrateFromPolyFunc(func);
assertEquals("Gauss Hermite", expected, resGH, 1e-2); //terrible accuracy even with 40 points
}
private Function1D<Double, Double> getBlackIntergrand(final double fwd, final double k, final double t, final double vol) {
final double rootPI = Math.sqrt(Math.PI);
final double sigmaSqrTO2 = vol * vol * t / 2;
final double sigmaRoot2T = vol * Math.sqrt(2 * t);
return new Function1D<Double, Double>() {
@Override
public Double evaluate(final Double x) {
final double s = fwd * Math.exp(-sigmaSqrTO2 + sigmaRoot2T * x);
return Math.max(s - k, 0) / rootPI;
}
};
}
}