com.opengamma.strata.math.impl.interpolation.BasisFunctionGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of strata-math Show documentation
Show all versions of strata-math Show documentation
Mathematic support for Strata
/*
* 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