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

com.opengamma.strata.math.impl.interpolation.BasisFunctionGenerator Maven / Gradle / Ivy

There is a newer version: 2.12.46
Show newest version
/*
 * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.math.impl.interpolation;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.math.impl.FunctionUtils;

/**
 * Generator for a set of basis functions.
 */
public class BasisFunctionGenerator {

  /**
   * Generate a set of b-splines with a given polynomial degree on the specified knots.
   * @param knots holder for the knots and degree
   * @return a List of functions
   */
  public List> generateSet(BasisFunctionKnots knots) {
    ArgChecker.notNull(knots, "knots");

    double[] k = knots.getKnots();
    List> set = null;
    for (int d = 0; d <= knots.getDegree(); d++) {
      set = generateSet(k, d, set);
    }
    return set;
  }

  /**
   * Generate a set of N-dimensional b-splines as the produce of 1-dimensional b-splines with a given polynomial degree.
   * on the specified knots
   * @param knots holder for the knots and degree in each dimension
   * @return a List of functions
   */
  public List> generateSet(BasisFunctionKnots[] knots) {
    ArgChecker.noNulls(knots, "knots");
    int dim = knots.length;
    int[] nSplines = new int[dim];
    int product = 1;
    List>> oneDSets = new ArrayList<>(dim);
    for (int i = 0; i < dim; i++) {
      oneDSets.add(generateSet(knots[i]));
      nSplines[i] = knots[i].getNumSplines();
      product *= nSplines[i];
    }
    final List> functions = new ArrayList<>(product);

    for (int i = 0; i < product; i++) {
      int[] indices = FunctionUtils.fromTensorIndex(i, nSplines);
      functions.add(generateMultiDim(oneDSets, indices));
    }
    return functions;
  }

  /**
   * Generate the i^th basis function
   * @param data Container for the knots and degree of the basis function
   * @param index The index (from zero) of the function. Must be in range 0 to data.getNumSplines() (exclusive)
   *   For example if the degree is 1, and index is 0, this will cover the first three knots.
   * @return The i^th basis function
   */
  protected Function generate(BasisFunctionKnots data, final int index) {
    ArgChecker.notNull(data, "data");
    ArgChecker.isTrue(index >= 0 && index < data.getNumSplines(), "index must be in range {} to {} (exclusive)", 0, data.getNumSplines());
    return generate(data.getKnots(), data.getDegree(), index);
  }

  /**
   * Generate the n-dimensional basis function for the given index position. This is formed as the product of 1-d basis
   * functions.
   * @param oneDSets The sets of basis functions in each dimension
   * @param index index (from zero) of the basis function in each dimension.
   * @return A n-dimensional basis function
   */
  private Function generateMultiDim(List>> oneDSets, int[] index) {
    final int dim = index.length;
    final List> funcs = new ArrayList<>(dim);
    for (int i = 0; i < dim; i++) {
      funcs.add(oneDSets.get(i).get(index[i]));
    }

    return new Function() {
      @Override
      public Double apply(double[] x) {
        double product = 1.0;
        ArgChecker.isTrue(dim == x.length, "length of x {} was not equal to dimension {}", x.length, dim);
        for (int i = 0; i < dim; i++) {
          product *= funcs.get(i).apply(x[i]);
        }
        return product;
      }
    };
  }

  private List> generateSet(double[] knots, int degree, List> degreeM1Set) {

    int nSplines = knots.length - degree - 1;
    List> functions = new ArrayList<>(nSplines);
    for (int i = 0; i < nSplines; i++) {
      functions.add(generate(knots, degree, i, degreeM1Set));
    }
    return functions;
  }

  /**
   * Generate a basis function of the required degree either by using the set of functions one degree lower, or by recursion
   * @param knots The knots that support the basis functions
   * @param degree The required degree
   * @param index The index of the required function
   * @param degreeM1Set Set of basis functions one degree lower than required (can be null)
   * @return The basis function
   */
  private Function generate(double[] knots, int degree, int index, List> degreeM1Set) {

    if (degree == 0) {
      return new Function() {
        private final double _l = knots[index];
        private final double _h = knots[index + 1];

        @Override
        public Double apply(final Double x) {
          return (x >= _l && x < _h) ? 1.0 : 0.0;
        }
      };
    }

    if (degree == 1) {
      return new Function() {
        private final double _l = knots[index];
        private final double _m = knots[index + 1];
        private final double _h = knots[index + 2];
        private final double _inv1 = 1.0 / (knots[index + 1] - knots[index]);
        private final double _inv2 = 1.0 / (knots[index + 2] - knots[index + 1]);

        @Override
        public Double apply(final Double x) {
          return (x <= _l || x >= _h) ? 0.0 : (x <= _m ? (x - _l) * _inv1 : (_h - x) * _inv2);
        }
      };
    }

    // if degreeM1Set is unavailable, use the recursion
    final Function fa = degreeM1Set == null ? generate(knots, degree - 1, index) : degreeM1Set.get(index);
    final Function fb = degreeM1Set == null ? generate(knots, degree - 1, index + 1) : degreeM1Set.get(index + 1);

    return new Function() {
      private final double _inv1 = 1.0 / (knots[index + degree] - knots[index]);
      private final double _inv2 = 1.0 / (knots[index + degree + 1] - knots[index + 1]);
      private final double _xa = knots[index];
      private final double _xb = knots[index + degree + 1];

      @Override
      public Double apply(final Double x) {
        return (x - _xa) * _inv1 * fa.apply(x) + (_xb - x) * _inv2 * fb.apply(x);
      }
    };

  }

  /**
   * Generate a basis function of the required degree by recursion
   * @param knots The knots that support the basis functions
   * @param degree The required degree
   * @param index The index of the required function
   * @return The basis function
   */
  private Function generate(final double[] knots, final int degree, final int index) {
    return generate(knots, degree, index, null);
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy