All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opengamma.strata.math.impl.differentiation.VectorFieldSecondOrderDifferentiator Maven / Gradle / Ivy

There is a newer version: 2.12.46
Show newest version
/*
 * Copyright (C) 2012 - 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;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.array.DoubleMatrix;

/**
 * The Vector field second order differentiator.
 */
public class VectorFieldSecondOrderDifferentiator implements Differentiator {

  private static final double DEFAULT_EPS = 1e-4;

  private final double eps;
  private final double epsSqr;
  private final VectorFieldFirstOrderDifferentiator vectorFieldDiff;
  private final MatrixFieldFirstOrderDifferentiator maxtrixFieldDiff;

  /**
   * Creates an instance using the default values.
   */
  public VectorFieldSecondOrderDifferentiator() {
    this.eps = DEFAULT_EPS;
    this.epsSqr = eps * eps;
    this.vectorFieldDiff = new VectorFieldFirstOrderDifferentiator(eps);
    this.maxtrixFieldDiff = new MatrixFieldFirstOrderDifferentiator(eps);
  }

  //-------------------------------------------------------------------------
  /**
   * This computes the second derivative of a vector field, which is a rank 3 tensor field.
   * The tensor is represented as an array of DoubleMatrix, where each matrix is
   * a Hessian (for the dependent variable y_i), so the indexing is
   * H^i_{j,k} =\partial^2y_i/\partial x_j \partial x_k
   * 
   * @param function  the function representing the vector field
   * @return a function representing the second derivative of the vector field (i.e. a rank 3 tensor field)
   */
  @Override
  public Function differentiate(
      Function function) {

    ArgChecker.notNull(function, "function");
    Function jacFunc = vectorFieldDiff.differentiate(function);
    Function hFunc = maxtrixFieldDiff.differentiate(jacFunc);
    return new Function() {
      @SuppressWarnings("synthetic-access")
      @Override
      public DoubleMatrix[] apply(DoubleArray x) {
        DoubleMatrix[] gamma = hFunc.apply(x);
        return reshapeTensor(gamma);
      }
    };
  }

  //-------------------------------------------------------------------------
  @Override
  public Function differentiate(
      Function function,
      Function domain) {

    ArgChecker.notNull(function, "function");
    Function jacFunc = vectorFieldDiff.differentiate(function, domain);
    Function hFunc = maxtrixFieldDiff.differentiate(jacFunc, domain);
    return new Function() {
      @SuppressWarnings("synthetic-access")
      @Override
      public DoubleMatrix[] apply(DoubleArray x) {
        DoubleMatrix[] gamma = hFunc.apply(x);
        return reshapeTensor(gamma);
      }
    };
  }

  /**
   * Gamma is in the  form gamma^i_{j,k} =\partial^2y_j/\partial x_i \partial x_k, where i is the
   * index of the matrix in the stack (3rd index of the tensor), and j,k are the individual
   * matrix indices. We would like it in the form H^i_{j,k} =\partial^2y_i/\partial x_j \partial x_k,
   * so that each matrix is a Hessian (for the dependent variable y_i), hence the reshaping below.
   * 
   * @param gamma  the rank 3 tensor
   * @return the reshaped rank 3 tensor
   */
  private DoubleMatrix[] reshapeTensor(DoubleMatrix[] gamma) {
    int m = gamma.length;
    int n = gamma[0].rowCount();
    ArgChecker.isTrue(gamma[0].columnCount() == m,
        "tenor wrong size. Seond index is {}, should be {}", gamma[0].columnCount(), m);
    DoubleMatrix[] res = new DoubleMatrix[n];
    for (int i = 0; i < n; i++) {
      double[][] temp = new double[m][m];
      for (int j = 0; j < m; j++) {
        DoubleMatrix gammaJ = gamma[j];
        for (int k = j; k < m; k++) {
          temp[j][k] = gammaJ.get(i, k);
        }
      }
      for (int j = 0; j < m; j++) {
        for (int k = 0; k < j; k++) {
          temp[j][k] = temp[k][j];
        }
      }
      res[i] = DoubleMatrix.copyOf(temp);
    }
    return res;
  }

  //-------------------------------------------------------------------------
  /**
   * Differentiate.
   * 
   * @param function  the function
   * @return the result
   */
  public Function differentiateFull(
      Function function) {

    return new Function() {
      @SuppressWarnings("synthetic-access")
      @Override
      public DoubleMatrix[] apply(DoubleArray x) {
        ArgChecker.notNull(x, "x");
        DoubleArray y = function.apply(x);
        int n = x.size();
        int m = y.size();
        double[][][] res = new double[m][n][n];

        for (int j = 0; j < n; j++) {
          double xj = x.get(j);
          DoubleArray xPlusOneEps = x.with(j, xj + eps);
          DoubleArray xMinusOneEps = x.with(j, xj - eps);
          DoubleArray up = function.apply(x.with(j, xj + eps));
          DoubleArray down = function.apply(xMinusOneEps);
          for (int i = 0; i < m; i++) {
            res[i][j][j] = (up.get(i) + down.get(i) - 2 * y.get(i)) / epsSqr;
          }
          for (int k = j + 1; k < n; k++) {
            double xk = x.get(k);
            DoubleArray downup = function.apply(xMinusOneEps.with(k, xk + eps));
            DoubleArray downdown = function.apply(xMinusOneEps.with(k, xk - eps));
            DoubleArray updown = function.apply(xPlusOneEps.with(k, xk - eps));
            DoubleArray upup = function.apply(xPlusOneEps.with(k, xk + eps));
            for (int i = 0; i < m; i++) {
              res[i][j][k] = (upup.get(i) + downdown.get(i) - updown.get(i) - downup.get(i)) / 4 / epsSqr;
            }
          }
        }
        DoubleMatrix[] mres = new DoubleMatrix[m];
        for (int i = 0; i < m; i++) {
          for (int j = 0; j < n; j++) {
            for (int k = 0; k < j; k++) {
              res[i][j][k] = res[i][k][j];
            }
          }
          mres[i] = DoubleMatrix.copyOf(res[i]);
        }
        return mres;
      }
    };
  }

  //-------------------------------------------------------------------------
  /**
   * Computes the second derivative of a vector field, without cross derivatives. 
   * 
   * This creates a function returning a matrix whose {i,j} element is 
   * $H^i_{j} = \partial^2y_i/\partial x_j \partial x_j$.
   * 
   * @param function  the function representing the vector field
   * @return a function representing the second derivative of the vector field (i.e. a rank 3 tensor field)
   */
  public Function differentiateNoCross(Function function) {

    return new Function() {
      @SuppressWarnings("synthetic-access")
      @Override
      public DoubleMatrix apply(DoubleArray x) {
        ArgChecker.notNull(x, "x");
        DoubleArray y = function.apply(x);
        int n = x.size();
        int m = y.size();
        double[][] res = new double[m][n];
        for (int j = 0; j < n; j++) {
          double xj = x.get(j);
          DoubleArray up = function.apply(x.with(j, xj + eps));
          DoubleArray down = function.apply(x.with(j, xj - eps));
          for (int i = 0; i < m; i++) {
            res[i][j] = (up.get(i) + down.get(i) - 2d * y.get(i)) / epsSqr;
          }
        }
        return DoubleMatrix.copyOf(res);
      }
    };
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy