/** * 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<Double, Double> 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 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<Function<double[], Double>> generateSet(BasisFunctionKnots[] knots) { ArgChecker.noNulls(knots, "knots"); int dim = knots.length; int[] nSplines = new int[dim]; int product = 1; List<List<Function<Double, Double>>> 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<Function<double[], Double>> functions = new ArrayList<>(product); for (int i = 0; i < product; i++) { int[] indices = FunctionUtils.fromTensorIndex(i, nSplines); functions.add(generateMultiDim(oneDSets, indices)); } return functions; }
@Test public void testThreeD() { BasisFunctionKnots knots1 = BasisFunctionKnots.fromInternalKnots(KNOTS, 2); BasisFunctionKnots knots2 = BasisFunctionKnots.fromInternalKnots(KNOTS, 3); BasisFunctionKnots knots3 = BasisFunctionKnots.fromInternalKnots(KNOTS, 1); List<Function<double[], Double>> set = GENERATOR.generateSet(new BasisFunctionKnots[] {knots1, knots2, knots3 }); //pick of one of the basis functions for testing int index = FunctionUtils.toTensorIndex(new int[] {3, 3, 3 }, new int[] {knots1.getNumSplines(), knots2.getNumSplines(), knots3.getNumSplines() }); Function<double[], Double> func = set.get(index); assertEquals(1. / 3., func.apply(new double[] {2.0, 2.0, 3.0 }), 0.0); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testFunctionIndexOutOfRange2() { BasisFunctionKnots k = BasisFunctionKnots.fromKnots(KNOTS, 5); int nS = k.getNumSplines(); GENERATOR.generate(k, nS); }
@Test public void testTwoD() { BasisFunctionKnots knots1 = BasisFunctionKnots.fromInternalKnots(KNOTS, 2); BasisFunctionKnots knots2 = BasisFunctionKnots.fromInternalKnots(KNOTS, 3); List<Function<double[], Double>> set = GENERATOR.generateSet(new BasisFunctionKnots[] {knots1, knots2 }); //pick of one of the basis functions for testing int index = FunctionUtils.toTensorIndex(new int[] {3, 3 }, new int[] {knots1.getNumSplines(), knots2.getNumSplines() }); Function<double[], Double> func = set.get(index); assertEquals(1. / 3., func.apply(new double[] {2.0, 2.0 }), 0.0); assertEquals(1. / 2., func.apply(new double[] {2.5, 2.0 }), 0.0); assertEquals(1. / 8. / 48., func.apply(new double[] {1.5, 3.5 }), 0.0); assertEquals(0.0, func.apply(new double[] {4.0, 2.5 }), 0.0); }
@Test public void testUniform() { BasisFunctionKnots knots = BasisFunctionKnots.fromUniform(1.0, 2.0, 10, 3); assertEquals(3, knots.getDegree()); assertEquals(16, knots.getNumKnots()); assertEquals(12, knots.getNumSplines()); }
@Test public void testInternalKnots() { BasisFunctionKnots knots = BasisFunctionKnots.fromInternalKnots(KNOTS, 2); assertEquals(2, knots.getDegree()); assertEquals(15, knots.getNumKnots()); assertEquals(12, knots.getNumSplines()); }
@Test public void testKnots() { BasisFunctionKnots knots = BasisFunctionKnots.fromKnots(KNOTS, 3); assertEquals(3, knots.getDegree()); assertEquals(11, knots.getNumKnots()); assertEquals(7, knots.getNumSplines()); ArrayAsserts.assertArrayEquals(KNOTS, knots.getKnots(), 1e-15); }