/** * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.math.impl.differentiation; import java.util.function.Function; import com.opengamma.strata.collect.ArgChecker; /** * Differentiates a scalar function with respect to its argument using finite difference. * <p> * For a function $y = f(x)$ where $x$ and $y$ are scalars, this class produces * a function that returns the second derivative value for each point, i.e., $\frac{d^2 f}{dx^2}$. */ public class ScalarSecondOrderDifferentiator implements Differentiator<Double, Double, Double> { /** * Default steps size. */ private static final double DEFAULT_EPS = 1e-4; private final double eps; private final double epsSqr; private final double twoEps; private final double threeEps; /** * Creates an instance using the default values. */ public ScalarSecondOrderDifferentiator() { this(DEFAULT_EPS); } /** * Creates an instance specifying the step size. * * @param eps the step size */ public ScalarSecondOrderDifferentiator(double eps) { this.eps = eps; this.epsSqr = eps * eps; this.twoEps = 2d * eps; this.threeEps = 3d * eps; } //------------------------------------------------------------------------- @Override public Function<Double, Double> differentiate(Function<Double, Double> function) { ArgChecker.notNull(function, "function"); return new Function<Double, Double>() { @SuppressWarnings("synthetic-access") @Override public Double apply(Double x) { ArgChecker.notNull(x, "x"); return (function.apply(x + eps) + function.apply(x - eps) - 2d * function.apply(x)) / epsSqr; } }; } //------------------------------------------------------------------------- @Override public Function<Double, Double> differentiate(Function<Double, Double> function, Function<Double, Boolean> domain) { ArgChecker.notNull(function, "function"); ArgChecker.notNull(domain, "domain"); return new Function<Double, Double>() { @SuppressWarnings("synthetic-access") @Override public Double apply(Double x) { ArgChecker.notNull(x, "x"); ArgChecker.isTrue(domain.apply(x), "point {} is not in the function domain", x.toString()); if (!domain.apply(x + threeEps)) { if (!domain.apply(x - threeEps)) { throw new IllegalArgumentException("cannot get derivative at point " + x.toString()); } return (-function.apply(x - threeEps) + 4d * function.apply(x - twoEps) - 5d * function.apply(x - eps) + 2d * function.apply(x)) / epsSqr; } else { if (!domain.apply(x - eps)) { return (-function.apply(x + threeEps) + 4d * function.apply(x + twoEps) - 5d * function.apply(x + eps) + 2d * function.apply(x)) / epsSqr; } return (function.apply(x + eps) + function.apply(x - eps) - 2d * function.apply(x)) / epsSqr; } } }; } }