diff options
author | Raymond <siuchow@google.com> | 2015-04-02 10:43:13 -0700 |
---|---|---|
committer | Raymond <siuchow@google.com> | 2015-04-02 10:43:13 -0700 |
commit | dee0849a9704d532af0b550146cbafbaa6ee1d19 (patch) | |
tree | 8ccce3a046c214fb609977b7fc53c40cef7f9ea5 /src/main/java/org/apache/commons/math/analysis | |
parent | 55b0a5efc929efa9615babd3e760547f94e3518e (diff) | |
download | apache-commons-math-dee0849a9704d532af0b550146cbafbaa6ee1d19.tar.gz |
third party library: apache-commons-mathandroid-cts-6.0_r9android-cts-6.0_r8android-cts-6.0_r7android-cts-6.0_r6android-cts-6.0_r5android-cts-6.0_r4android-cts-6.0_r32android-cts-6.0_r31android-cts-6.0_r30android-cts-6.0_r3android-cts-6.0_r29android-cts-6.0_r28android-cts-6.0_r27android-cts-6.0_r26android-cts-6.0_r25android-cts-6.0_r24android-cts-6.0_r23android-cts-6.0_r22android-cts-6.0_r21android-cts-6.0_r20android-cts-6.0_r2android-cts-6.0_r19android-cts-6.0_r18android-cts-6.0_r17android-cts-6.0_r16android-cts-6.0_r15android-cts-6.0_r14android-cts-6.0_r13android-cts-6.0_r12android-cts-6.0_r1android-6.0.1_r9android-6.0.1_r81android-6.0.1_r80android-6.0.1_r8android-6.0.1_r79android-6.0.1_r78android-6.0.1_r77android-6.0.1_r74android-6.0.1_r73android-6.0.1_r72android-6.0.1_r70android-6.0.1_r7android-6.0.1_r69android-6.0.1_r68android-6.0.1_r67android-6.0.1_r66android-6.0.1_r65android-6.0.1_r63android-6.0.1_r62android-6.0.1_r61android-6.0.1_r60android-6.0.1_r59android-6.0.1_r58android-6.0.1_r57android-6.0.1_r56android-6.0.1_r55android-6.0.1_r54android-6.0.1_r53android-6.0.1_r52android-6.0.1_r51android-6.0.1_r50android-6.0.1_r5android-6.0.1_r49android-6.0.1_r48android-6.0.1_r47android-6.0.1_r46android-6.0.1_r45android-6.0.1_r43android-6.0.1_r42android-6.0.1_r41android-6.0.1_r40android-6.0.1_r4android-6.0.1_r33android-6.0.1_r32android-6.0.1_r31android-6.0.1_r30android-6.0.1_r3android-6.0.1_r28android-6.0.1_r27android-6.0.1_r26android-6.0.1_r25android-6.0.1_r24android-6.0.1_r22android-6.0.1_r21android-6.0.1_r20android-6.0.1_r18android-6.0.1_r17android-6.0.1_r16android-6.0.1_r13android-6.0.1_r12android-6.0.1_r11android-6.0.1_r10android-6.0.1_r1android-6.0.0_r7android-6.0.0_r6android-6.0.0_r5android-6.0.0_r41android-6.0.0_r4android-6.0.0_r3android-6.0.0_r26android-6.0.0_r25android-6.0.0_r24android-6.0.0_r23android-6.0.0_r2android-6.0.0_r13android-6.0.0_r12android-6.0.0_r11android-6.0.0_r1marshmallow-releasemarshmallow-mr3-releasemarshmallow-mr2-releasemarshmallow-mr1-releasemarshmallow-mr1-devmarshmallow-dr1.6-releasemarshmallow-dr1.5-releasemarshmallow-dr1.5-devmarshmallow-dr-releasemarshmallow-dr-dragon-releasemarshmallow-dr-devmarshmallow-devmarshmallow-cts-release
Change-Id: I52a325624a7f0dd652b362a9840626d6d9f3c42b
Diffstat (limited to 'src/main/java/org/apache/commons/math/analysis')
60 files changed, 9513 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math/analysis/BinaryFunction.java b/src/main/java/org/apache/commons/math/analysis/BinaryFunction.java new file mode 100644 index 0000000..227b72e --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/BinaryFunction.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math.analysis; + +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.util.FastMath; + + + +/** + * Base class for {@link BivariateRealFunction} that can be composed with other functions. + * + * @since 2.1 + * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $ + * @deprecated in 2.2 + */ +@Deprecated +public abstract class BinaryFunction implements BivariateRealFunction { + + /** The + operator method wrapped as a {@link BinaryFunction}. */ + public static final BinaryFunction ADD = new BinaryFunction() { + /** {@inheritDoc} */ + @Override + public double value(double x, double y) { + return x + y; + } + }; + + /** The - operator method wrapped as a {@link BinaryFunction}. */ + public static final BinaryFunction SUBTRACT = new BinaryFunction() { + /** {@inheritDoc} */ + @Override + public double value(double x, double y) { + return x - y; + } + }; + + /** The * operator method wrapped as a {@link BinaryFunction}. */ + public static final BinaryFunction MULTIPLY = new BinaryFunction() { + /** {@inheritDoc} */ + @Override + public double value(double x, double y) { + return x * y; + } + }; + + /** The / operator method wrapped as a {@link BinaryFunction}. */ + public static final BinaryFunction DIVIDE = new BinaryFunction() { + /** {@inheritDoc} */ + @Override + public double value(double x, double y) { + return x / y; + } + }; + + /** The {@code FastMath.pow} method wrapped as a {@link BinaryFunction}. */ + public static final BinaryFunction POW = new BinaryFunction() { + /** {@inheritDoc} */ + @Override + public double value(double x, double y) { + return FastMath.pow(x, y); + } + }; + + /** The {@code FastMath.atan2} method wrapped as a {@link BinaryFunction}. */ + public static final BinaryFunction ATAN2 = new BinaryFunction() { + /** {@inheritDoc} */ + @Override + public double value(double x, double y) { + return FastMath.atan2(x, y); + } + }; + + /** {@inheritDoc} */ + public abstract double value(double x, double y) throws FunctionEvaluationException; + + /** Get a composable function by fixing the first argument of the instance. + * @param fixedX fixed value of the first argument + * @return a function such that {@code f.value(y) == value(fixedX, y)} + */ + public ComposableFunction fix1stArgument(final double fixedX) { + return new ComposableFunction() { + @Override + /** {@inheritDoc} */ + public double value(double x) throws FunctionEvaluationException { + return BinaryFunction.this.value(fixedX, x); + } + }; + } + + /** Get a composable function by fixing the second argument of the instance. + * @param fixedY fixed value of the second argument + * @return a function such that {@code f.value(x) == value(x, fixedY)} + */ + public ComposableFunction fix2ndArgument(final double fixedY) { + return new ComposableFunction() { + @Override + /** {@inheritDoc} */ + public double value(double x) throws FunctionEvaluationException { + return BinaryFunction.this.value(x, fixedY); + } + }; + } + +} diff --git a/src/main/java/org/apache/commons/math/analysis/BivariateRealFunction.java b/src/main/java/org/apache/commons/math/analysis/BivariateRealFunction.java new file mode 100644 index 0000000..4b12312 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/BivariateRealFunction.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math.analysis; + +import org.apache.commons.math.FunctionEvaluationException; + +/** + * An interface representing a bivariate real function. + * + * @since 2.1 + * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $ + */ +public interface BivariateRealFunction { + /** + * Compute the value for the function. + * + * @param x Abscissa for which the function value should be computed. + * @param y Ordinate for which the function value should be computed. + * @return the value. + * @throws FunctionEvaluationException if the function evaluation fails. + */ + double value(double x, double y) + throws FunctionEvaluationException; + +} diff --git a/src/main/java/org/apache/commons/math/analysis/ComposableFunction.java b/src/main/java/org/apache/commons/math/analysis/ComposableFunction.java new file mode 100644 index 0000000..91c8132 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/ComposableFunction.java @@ -0,0 +1,506 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math.analysis; + +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.util.FastMath; + + +/** + * Base class for {@link UnivariateRealFunction} that can be composed with other functions. + * + * @since 2.1 + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + */ +public abstract class ComposableFunction implements UnivariateRealFunction { + + /** The constant function always returning 0. */ + public static final ComposableFunction ZERO = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return 0; + } + }; + + /** The constant function always returning 1. */ + public static final ComposableFunction ONE = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return 1; + } + }; + + /** The identity function. */ + public static final ComposableFunction IDENTITY = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return d; + } + }; + + /** The {@code FastMath.abs} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction ABS = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.abs(d); + } + }; + + /** The - operator wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction NEGATE = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return -d; + } + }; + + /** The invert operator wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction INVERT = new ComposableFunction () { + /** {@inheritDoc} */ + @Override + public double value(double d){ + return 1/d; + } + }; + + /** The {@code FastMath.sin} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction SIN = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.sin(d); + } + }; + + /** The {@code FastMath.sqrt} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction SQRT = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.sqrt(d); + } + }; + + /** The {@code FastMath.sinh} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction SINH = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.sinh(d); + } + }; + + /** The {@code FastMath.exp} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction EXP = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.exp(d); + } + }; + + /** The {@code FastMath.expm1} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction EXPM1 = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.expm1(d); + } + }; + + /** The {@code FastMath.asin} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction ASIN = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.asin(d); + } + }; + + /** The {@code FastMath.atan} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction ATAN = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.atan(d); + } + }; + + /** The {@code FastMath.tan} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction TAN = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.tan(d); + } + }; + + /** The {@code FastMath.tanh} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction TANH = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.tanh(d); + } + }; + + /** The {@code FastMath.cbrt} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction CBRT = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.cbrt(d); + } + }; + + /** The {@code FastMath.ceil} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction CEIL = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.ceil(d); + } + }; + + /** The {@code FastMath.floor} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction FLOOR = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.floor(d); + } + }; + + /** The {@code FastMath.log} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction LOG = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.log(d); + } + }; + + /** The {@code FastMath.log10} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction LOG10 = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.log10(d); + } + }; + + /** The {@code FastMath.log1p} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction LOG1P = new ComposableFunction () { + @Override + public double value(double d){ + return FastMath.log1p(d); + } + }; + + /** The {@code FastMath.cos} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction COS = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.cos(d); + } + }; + + /** The {@code FastMath.abs} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction ACOS = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.acos(d); + } + }; + + /** The {@code FastMath.cosh} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction COSH = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.cosh(d); + } + }; + + /** The {@code FastMath.rint} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction RINT = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.rint(d); + } + }; + + /** The {@code FastMath.signum} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction SIGNUM = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.signum(d); + } + }; + + /** The {@code FastMath.ulp} method wrapped as a {@link ComposableFunction}. */ + public static final ComposableFunction ULP = new ComposableFunction() { + /** {@inheritDoc} */ + @Override + public double value(double d) { + return FastMath.ulp(d); + } + }; + + /** Precompose the instance with another function. + * <p> + * The composed function h created by {@code h = g.of(f)} is such + * that {@code h.value(x) == g.value(f.value(x))} for all x. + * </p> + * @param f function to compose with + * @return a new function which computes {@code this.value(f.value(x))} + * @see #postCompose(UnivariateRealFunction) + */ + public ComposableFunction of(final UnivariateRealFunction f) { + return new ComposableFunction() { + @Override + /** {@inheritDoc} */ + public double value(double x) throws FunctionEvaluationException { + return ComposableFunction.this.value(f.value(x)); + } + }; + } + + /** Postcompose the instance with another function. + * <p> + * The composed function h created by {@code h = g.postCompose(f)} is such + * that {@code h.value(x) == f.value(g.value(x))} for all x. + * </p> + * @param f function to compose with + * @return a new function which computes {@code f.value(this.value(x))} + * @see #of(UnivariateRealFunction) + */ + public ComposableFunction postCompose(final UnivariateRealFunction f) { + return new ComposableFunction() { + @Override + /** {@inheritDoc} */ + public double value(double x) throws FunctionEvaluationException { + return f.value(ComposableFunction.this.value(x)); + } + }; + } + + /** + * Return a function combining the instance and another function. + * <p> + * The function h created by {@code h = g.combine(f, combiner)} is such that + * {@code h.value(x) == combiner.value(g.value(x), f.value(x))} for all x. + * </p> + * @param f function to combine with the instance + * @param combiner bivariate function used for combining + * @return a new function which computes {@code combine.value(this.value(x), f.value(x))} + */ + public ComposableFunction combine(final UnivariateRealFunction f, + final BivariateRealFunction combiner) { + return new ComposableFunction() { + @Override + /** {@inheritDoc} */ + public double value(double x) throws FunctionEvaluationException { + return combiner.value(ComposableFunction.this.value(x), f.value(x)); + } + }; + } + + /** + * Return a function adding the instance and another function. + * @param f function to combine with the instance + * @return a new function which computes {@code this.value(x) + f.value(x)} + */ + public ComposableFunction add(final UnivariateRealFunction f) { + return new ComposableFunction() { + @Override + /** {@inheritDoc} */ + public double value(double x) throws FunctionEvaluationException { + return ComposableFunction.this.value(x) + f.value(x); + } + }; + } + + /** + * Return a function adding a constant term to the instance. + * @param a term to add + * @return a new function which computes {@code this.value(x) + a} + */ + public ComposableFunction add(final double a) { + return new ComposableFunction() { + @Override + /** {@inheritDoc} */ + public double value(double x) throws FunctionEvaluationException { + return ComposableFunction.this.value(x) + a; + } + }; + } + + /** + * Return a function subtracting another function from the instance. + * @param f function to combine with the instance + * @return a new function which computes {@code this.value(x) - f.value(x)} + */ + public ComposableFunction subtract(final UnivariateRealFunction f) { + return new ComposableFunction() { + @Override + /** {@inheritDoc} */ + public double value(double x) throws FunctionEvaluationException { + return ComposableFunction.this.value(x) - f.value(x); + } + }; + } + + /** + * Return a function multiplying the instance and another function. + * @param f function to combine with the instance + * @return a new function which computes {@code this.value(x) * f.value(x)} + */ + public ComposableFunction multiply(final UnivariateRealFunction f) { + return new ComposableFunction() { + @Override + /** {@inheritDoc} */ + public double value(double x) throws FunctionEvaluationException { + return ComposableFunction.this.value(x) * f.value(x); + } + }; + } + + /** + * Return a function scaling the instance by a constant factor. + * @param scaleFactor constant scaling factor + * @return a new function which computes {@code this.value(x) * scaleFactor} + */ + public ComposableFunction multiply(final double scaleFactor) { + return new ComposableFunction() { + @Override + /** {@inheritDoc} */ + public double value(double x) throws FunctionEvaluationException { + return ComposableFunction.this.value(x) * scaleFactor; + } + }; + } + /** + * Return a function dividing the instance by another function. + * @param f function to combine with the instance + * @return a new function which computes {@code this.value(x) / f.value(x)} + */ + public ComposableFunction divide(final UnivariateRealFunction f) { + return new ComposableFunction() { + @Override + /** {@inheritDoc} */ + public double value(double x) throws FunctionEvaluationException { + return ComposableFunction.this.value(x) / f.value(x); + } + }; + } + + /** + * Generates a function that iteratively apply instance function on all + * elements of an array. + * <p> + * The generated function behaves as follows: + * <ul> + * <li>initialize result = initialValue</li> + * <li>iterate: {@code result = combiner.value(result, + * this.value(nextMultivariateEntry));}</li> + * <li>return result</li> + * </ul> + * </p> + * @param combiner combiner to use between entries + * @param initialValue initial value to use before first entry + * @return a new function that iteratively apply instance function on all + * elements of an array. + */ + public MultivariateRealFunction asCollector(final BivariateRealFunction combiner, + final double initialValue) { + return new MultivariateRealFunction() { + /** {@inheritDoc} */ + public double value(double[] point) + throws FunctionEvaluationException, IllegalArgumentException { + double result = initialValue; + for (final double entry : point) { + result = combiner.value(result, ComposableFunction.this.value(entry)); + } + return result; + } + }; + } + + /** + * Generates a function that iteratively apply instance function on all + * elements of an array. + * <p> + * Calling this method is equivalent to call {@link + * #asCollector(BivariateRealFunction, double) asCollector(BivariateRealFunction, 0.0)}. + * </p> + * @param combiner combiner to use between entries + * @return a new function that iteratively apply instance function on all + * elements of an array. + * @see #asCollector(BivariateRealFunction, double) + */ + public MultivariateRealFunction asCollector(final BivariateRealFunction combiner) { + return asCollector(combiner, 0.0); + } + + /** + * Generates a function that iteratively apply instance function on all + * elements of an array. + * <p> + * Calling this method is equivalent to call {@link + * #asCollector(BivariateRealFunction, double) asCollector(BinaryFunction.ADD, initialValue)}. + * </p> + * @param initialValue initial value to use before first entry + * @return a new function that iteratively apply instance function on all + * elements of an array. + * @see #asCollector(BivariateRealFunction, double) + * @see BinaryFunction#ADD + */ + public MultivariateRealFunction asCollector(final double initialValue) { + return asCollector(BinaryFunction.ADD, initialValue); + } + + /** + * Generates a function that iteratively apply instance function on all + * elements of an array. + * <p> + * Calling this method is equivalent to call {@link + * #asCollector(BivariateRealFunction, double) asCollector(BinaryFunction.ADD, 0.0)}. + * </p> + * @return a new function that iteratively apply instance function on all + * elements of an array. + * @see #asCollector(BivariateRealFunction, double) + * @see BinaryFunction#ADD + */ + public MultivariateRealFunction asCollector() { + return asCollector(BinaryFunction.ADD, 0.0); + } + + /** {@inheritDoc} */ + public abstract double value(double x) throws FunctionEvaluationException; + +} diff --git a/src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateRealFunction.java b/src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateRealFunction.java new file mode 100644 index 0000000..8d4f0c9 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateRealFunction.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math.analysis; + +/** + * Extension of {@link MultivariateRealFunction} representing a differentiable + * multivariate real function. + * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ + * @since 2.0 + */ +public interface DifferentiableMultivariateRealFunction extends MultivariateRealFunction { + + /** + * Returns the partial derivative of the function with respect to a point coordinate. + * <p> + * The partial derivative is defined with respect to point coordinate + * x<sub>k</sub>. If the partial derivatives with respect to all coordinates are + * needed, it may be more efficient to use the {@link #gradient()} method which will + * compute them all at once. + * </p> + * @param k index of the coordinate with respect to which the partial + * derivative is computed + * @return the partial derivative function with respect to k<sup>th</sup> point coordinate + */ + MultivariateRealFunction partialDerivative(int k); + + /** + * Returns the gradient function. + * <p>If only one partial derivative with respect to a specific coordinate is + * needed, it may be more efficient to use the {@link #partialDerivative(int)} method + * which will compute only the specified component.</p> + * @return the gradient function + */ + MultivariateVectorialFunction gradient(); + +} diff --git a/src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateVectorialFunction.java b/src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateVectorialFunction.java new file mode 100644 index 0000000..cc4ab8e --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateVectorialFunction.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math.analysis; + + +/** + * Extension of {@link MultivariateVectorialFunction} representing a differentiable + * multivariate vectorial function. + * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ + * @since 2.0 + */ +public interface DifferentiableMultivariateVectorialFunction + extends MultivariateVectorialFunction { + + /** + * Returns the jacobian function. + * @return the jacobian function + */ + MultivariateMatrixFunction jacobian(); + +} diff --git a/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateMatrixFunction.java b/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateMatrixFunction.java new file mode 100644 index 0000000..5f2f11b --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateMatrixFunction.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis; + +/** + * Extension of {@link UnivariateMatrixFunction} representing a differentiable univariate matrix function. + * + * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $ + * @since 2.0 + */ +public interface DifferentiableUnivariateMatrixFunction + extends UnivariateMatrixFunction { + + /** + * Returns the derivative of the function + * + * @return the derivative function + */ + UnivariateMatrixFunction derivative(); + +} diff --git a/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateRealFunction.java b/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateRealFunction.java new file mode 100644 index 0000000..1faaad3 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateRealFunction.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis; + +/** + * Extension of {@link UnivariateRealFunction} representing a differentiable univariate real function. + * + * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $ + */ +public interface DifferentiableUnivariateRealFunction + extends UnivariateRealFunction { + + /** + * Returns the derivative of the function + * + * @return the derivative function + */ + UnivariateRealFunction derivative(); + +} diff --git a/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateVectorialFunction.java b/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateVectorialFunction.java new file mode 100644 index 0000000..6566afe --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateVectorialFunction.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis; + +/** + * Extension of {@link UnivariateVectorialFunction} representing a differentiable univariate vectorial function. + * + * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $ + * @since 2.0 + */ +public interface DifferentiableUnivariateVectorialFunction + extends UnivariateVectorialFunction { + + /** + * Returns the derivative of the function + * + * @return the derivative function + */ + UnivariateVectorialFunction derivative(); + +} diff --git a/src/main/java/org/apache/commons/math/analysis/MultivariateMatrixFunction.java b/src/main/java/org/apache/commons/math/analysis/MultivariateMatrixFunction.java new file mode 100644 index 0000000..a5bad26 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/MultivariateMatrixFunction.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math.analysis; + + +import org.apache.commons.math.FunctionEvaluationException; + +/** + * An interface representing a multivariate matrix function. + * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $ + * @since 2.0 + */ +public interface MultivariateMatrixFunction { + + /** + * Compute the value for the function at the given point. + * @param point point at which the function must be evaluated + * @return function value for the given point + * @exception FunctionEvaluationException if the function evaluation fails + * @exception IllegalArgumentException if points dimension is wrong + */ + double[][] value(double[] point) + throws FunctionEvaluationException, IllegalArgumentException; + +} diff --git a/src/main/java/org/apache/commons/math/analysis/MultivariateRealFunction.java b/src/main/java/org/apache/commons/math/analysis/MultivariateRealFunction.java new file mode 100644 index 0000000..f054c51 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/MultivariateRealFunction.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math.analysis; + +import org.apache.commons.math.FunctionEvaluationException; + +/** + * An interface representing a multivariate real function. + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + * @since 2.0 + */ +public interface MultivariateRealFunction { + + /** + * Compute the value for the function at the given point. + * @param point point at which the function must be evaluated + * @return function value for the given point + * @exception FunctionEvaluationException if the function evaluation fails + * @exception IllegalArgumentException if points dimension is wrong + */ + double value(double[] point) + throws FunctionEvaluationException, IllegalArgumentException; + +} diff --git a/src/main/java/org/apache/commons/math/analysis/MultivariateVectorialFunction.java b/src/main/java/org/apache/commons/math/analysis/MultivariateVectorialFunction.java new file mode 100644 index 0000000..7e83a7c --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/MultivariateVectorialFunction.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math.analysis; + +import org.apache.commons.math.FunctionEvaluationException; + +/** + * An interface representing a multivariate vectorial function. + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + * @since 2.0 + */ +public interface MultivariateVectorialFunction { + + /** + * Compute the value for the function at the given point. + * @param point point at which the function must be evaluated + * @return function value for the given point + * @exception FunctionEvaluationException if the function evaluation fails + * @exception IllegalArgumentException if points dimension is wrong + */ + double[] value(double[] point) + throws FunctionEvaluationException, IllegalArgumentException; + +} diff --git a/src/main/java/org/apache/commons/math/analysis/TrivariateRealFunction.java b/src/main/java/org/apache/commons/math/analysis/TrivariateRealFunction.java new file mode 100644 index 0000000..3ebf24d --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/TrivariateRealFunction.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math.analysis; + +import org.apache.commons.math.FunctionEvaluationException; + +/** + * An interface representing a trivariate real function. + * + * @since 2.2 + * @version $Revision$ $Date$ + */ +public interface TrivariateRealFunction { + /** + * Compute the value for the function. + * + * @param x x-coordinate for which the function value should be computed. + * @param y y-coordinate for which the function value should be computed. + * @param z z-coordinate for which the function value should be computed. + * @return the value. + * @throws FunctionEvaluationException if the function evaluation fails. + */ + double value(double x, double y, double z) + throws FunctionEvaluationException; +} diff --git a/src/main/java/org/apache/commons/math/analysis/UnivariateMatrixFunction.java b/src/main/java/org/apache/commons/math/analysis/UnivariateMatrixFunction.java new file mode 100644 index 0000000..2db7349 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/UnivariateMatrixFunction.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis; + +import org.apache.commons.math.FunctionEvaluationException; + +/** + * An interface representing a univariate matrix function. + * + * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $ + * @since 2.0 + */ +public interface UnivariateMatrixFunction { + + /** + * Compute the value for the function. + * @param x the point for which the function value should be computed + * @return the value + * @throws FunctionEvaluationException if the function evaluation fails + */ + double[][] value(double x) throws FunctionEvaluationException; + +} diff --git a/src/main/java/org/apache/commons/math/analysis/UnivariateRealFunction.java b/src/main/java/org/apache/commons/math/analysis/UnivariateRealFunction.java new file mode 100644 index 0000000..298d8a7 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/UnivariateRealFunction.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis; + +import org.apache.commons.math.FunctionEvaluationException; + +/** + * An interface representing a univariate real function. + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + */ +public interface UnivariateRealFunction { + + /** + * Compute the value for the function. + * @param x the point for which the function value should be computed + * @return the value + * @throws FunctionEvaluationException if the function evaluation fails + */ + double value(double x) throws FunctionEvaluationException; + +} diff --git a/src/main/java/org/apache/commons/math/analysis/UnivariateVectorialFunction.java b/src/main/java/org/apache/commons/math/analysis/UnivariateVectorialFunction.java new file mode 100644 index 0000000..64e7e15 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/UnivariateVectorialFunction.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis; + +import org.apache.commons.math.FunctionEvaluationException; + +/** + * An interface representing a univariate vectorial function. + * + * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $ + * @since 2.0 + */ +public interface UnivariateVectorialFunction { + + /** + * Compute the value for the function. + * @param x the point for which the function value should be computed + * @return the value + * @throws FunctionEvaluationException if the function evaluation fails + */ + double[] value(double x) throws FunctionEvaluationException; + +} diff --git a/src/main/java/org/apache/commons/math/analysis/integration/LegendreGaussIntegrator.java b/src/main/java/org/apache/commons/math/analysis/integration/LegendreGaussIntegrator.java new file mode 100644 index 0000000..db6b76c --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/integration/LegendreGaussIntegrator.java @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.integration; + +import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.MaxIterationsExceededException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.util.FastMath; + +/** + * Implements the <a href="http://mathworld.wolfram.com/Legendre-GaussQuadrature.html"> + * Legendre-Gauss</a> quadrature formula. + * <p> + * Legendre-Gauss integrators are efficient integrators that can + * accurately integrate functions with few functions evaluations. A + * Legendre-Gauss integrator using an n-points quadrature formula can + * integrate exactly 2n-1 degree polynomials. + * </p> + * <p> + * These integrators evaluate the function on n carefully chosen + * abscissas in each step interval (mapped to the canonical [-1 1] interval). + * The evaluation abscissas are not evenly spaced and none of them are + * at the interval endpoints. This implies the function integrated can be + * undefined at integration interval endpoints. + * </p> + * <p> + * The evaluation abscissas x<sub>i</sub> are the roots of the degree n + * Legendre polynomial. The weights a<sub>i</sub> of the quadrature formula + * integrals from -1 to +1 ∫ Li<sup>2</sup> where Li (x) = + * ∏ (x-x<sub>k</sub>)/(x<sub>i</sub>-x<sub>k</sub>) for k != i. + * </p> + * <p> + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + * @since 1.2 + */ + +public class LegendreGaussIntegrator extends UnivariateRealIntegratorImpl { + + /** Abscissas for the 2 points method. */ + private static final double[] ABSCISSAS_2 = { + -1.0 / FastMath.sqrt(3.0), + 1.0 / FastMath.sqrt(3.0) + }; + + /** Weights for the 2 points method. */ + private static final double[] WEIGHTS_2 = { + 1.0, + 1.0 + }; + + /** Abscissas for the 3 points method. */ + private static final double[] ABSCISSAS_3 = { + -FastMath.sqrt(0.6), + 0.0, + FastMath.sqrt(0.6) + }; + + /** Weights for the 3 points method. */ + private static final double[] WEIGHTS_3 = { + 5.0 / 9.0, + 8.0 / 9.0, + 5.0 / 9.0 + }; + + /** Abscissas for the 4 points method. */ + private static final double[] ABSCISSAS_4 = { + -FastMath.sqrt((15.0 + 2.0 * FastMath.sqrt(30.0)) / 35.0), + -FastMath.sqrt((15.0 - 2.0 * FastMath.sqrt(30.0)) / 35.0), + FastMath.sqrt((15.0 - 2.0 * FastMath.sqrt(30.0)) / 35.0), + FastMath.sqrt((15.0 + 2.0 * FastMath.sqrt(30.0)) / 35.0) + }; + + /** Weights for the 4 points method. */ + private static final double[] WEIGHTS_4 = { + (90.0 - 5.0 * FastMath.sqrt(30.0)) / 180.0, + (90.0 + 5.0 * FastMath.sqrt(30.0)) / 180.0, + (90.0 + 5.0 * FastMath.sqrt(30.0)) / 180.0, + (90.0 - 5.0 * FastMath.sqrt(30.0)) / 180.0 + }; + + /** Abscissas for the 5 points method. */ + private static final double[] ABSCISSAS_5 = { + -FastMath.sqrt((35.0 + 2.0 * FastMath.sqrt(70.0)) / 63.0), + -FastMath.sqrt((35.0 - 2.0 * FastMath.sqrt(70.0)) / 63.0), + 0.0, + FastMath.sqrt((35.0 - 2.0 * FastMath.sqrt(70.0)) / 63.0), + FastMath.sqrt((35.0 + 2.0 * FastMath.sqrt(70.0)) / 63.0) + }; + + /** Weights for the 5 points method. */ + private static final double[] WEIGHTS_5 = { + (322.0 - 13.0 * FastMath.sqrt(70.0)) / 900.0, + (322.0 + 13.0 * FastMath.sqrt(70.0)) / 900.0, + 128.0 / 225.0, + (322.0 + 13.0 * FastMath.sqrt(70.0)) / 900.0, + (322.0 - 13.0 * FastMath.sqrt(70.0)) / 900.0 + }; + + /** Abscissas for the current method. */ + private final double[] abscissas; + + /** Weights for the current method. */ + private final double[] weights; + + /** + * Build a Legendre-Gauss integrator. + * @param n number of points desired (must be between 2 and 5 inclusive) + * @param defaultMaximalIterationCount maximum number of iterations + * @exception IllegalArgumentException if the number of points is not + * in the supported range + */ + public LegendreGaussIntegrator(final int n, final int defaultMaximalIterationCount) + throws IllegalArgumentException { + super(defaultMaximalIterationCount); + switch(n) { + case 2 : + abscissas = ABSCISSAS_2; + weights = WEIGHTS_2; + break; + case 3 : + abscissas = ABSCISSAS_3; + weights = WEIGHTS_3; + break; + case 4 : + abscissas = ABSCISSAS_4; + weights = WEIGHTS_4; + break; + case 5 : + abscissas = ABSCISSAS_5; + weights = WEIGHTS_5; + break; + default : + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.N_POINTS_GAUSS_LEGENDRE_INTEGRATOR_NOT_SUPPORTED, + n, 2, 5); + } + + } + + /** {@inheritDoc} */ + @Deprecated + public double integrate(final double min, final double max) + throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException { + return integrate(f, min, max); + } + + /** {@inheritDoc} */ + public double integrate(final UnivariateRealFunction f, final double min, final double max) + throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException { + + clearResult(); + verifyInterval(min, max); + verifyIterationCount(); + + // compute first estimate with a single step + double oldt = stage(f, min, max, 1); + + int n = 2; + for (int i = 0; i < maximalIterationCount; ++i) { + + // improve integral with a larger number of steps + final double t = stage(f, min, max, n); + + // estimate error + final double delta = FastMath.abs(t - oldt); + final double limit = + FastMath.max(absoluteAccuracy, + relativeAccuracy * (FastMath.abs(oldt) + FastMath.abs(t)) * 0.5); + + // check convergence + if ((i + 1 >= minimalIterationCount) && (delta <= limit)) { + setResult(t, i); + return result; + } + + // prepare next iteration + double ratio = FastMath.min(4, FastMath.pow(delta / limit, 0.5 / abscissas.length)); + n = FastMath.max((int) (ratio * n), n + 1); + oldt = t; + + } + + throw new MaxIterationsExceededException(maximalIterationCount); + + } + + /** + * Compute the n-th stage integral. + * @param f the integrand function + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param n number of steps + * @return the value of n-th stage integral + * @throws FunctionEvaluationException if an error occurs evaluating the + * function + */ + private double stage(final UnivariateRealFunction f, + final double min, final double max, final int n) + throws FunctionEvaluationException { + + // set up the step for the current stage + final double step = (max - min) / n; + final double halfStep = step / 2.0; + + // integrate over all elementary steps + double midPoint = min + halfStep; + double sum = 0.0; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < abscissas.length; ++j) { + sum += weights[j] * f.value(midPoint + halfStep * abscissas[j]); + } + midPoint += step; + } + + return halfStep * sum; + + } + +} diff --git a/src/main/java/org/apache/commons/math/analysis/integration/RombergIntegrator.java b/src/main/java/org/apache/commons/math/analysis/integration/RombergIntegrator.java new file mode 100644 index 0000000..9650af5 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/integration/RombergIntegrator.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.integration; + +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.MaxIterationsExceededException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.util.FastMath; + +/** + * Implements the <a href="http://mathworld.wolfram.com/RombergIntegration.html"> + * Romberg Algorithm</a> for integration of real univariate functions. For + * reference, see <b>Introduction to Numerical Analysis</b>, ISBN 038795452X, + * chapter 3. + * <p> + * Romberg integration employs k successive refinements of the trapezoid + * rule to remove error terms less than order O(N^(-2k)). Simpson's rule + * is a special case of k = 2.</p> + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + * @since 1.2 + */ +public class RombergIntegrator extends UnivariateRealIntegratorImpl { + + /** + * Construct an integrator for the given function. + * + * @param f function to integrate + * @deprecated as of 2.0 the integrand function is passed as an argument + * to the {@link #integrate(UnivariateRealFunction, double, double)}method. + */ + @Deprecated + public RombergIntegrator(UnivariateRealFunction f) { + super(f, 32); + } + + /** + * Construct an integrator. + */ + public RombergIntegrator() { + super(32); + } + + /** {@inheritDoc} */ + @Deprecated + public double integrate(final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException, IllegalArgumentException { + return integrate(f, min, max); + } + + /** {@inheritDoc} */ + public double integrate(final UnivariateRealFunction f, final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException, IllegalArgumentException { + + final int m = maximalIterationCount + 1; + double previousRow[] = new double[m]; + double currentRow[] = new double[m]; + + clearResult(); + verifyInterval(min, max); + verifyIterationCount(); + + TrapezoidIntegrator qtrap = new TrapezoidIntegrator(); + currentRow[0] = qtrap.stage(f, min, max, 0); + double olds = currentRow[0]; + for (int i = 1; i <= maximalIterationCount; ++i) { + + // switch rows + final double[] tmpRow = previousRow; + previousRow = currentRow; + currentRow = tmpRow; + + currentRow[0] = qtrap.stage(f, min, max, i); + for (int j = 1; j <= i; j++) { + // Richardson extrapolation coefficient + final double r = (1L << (2 * j)) - 1; + final double tIJm1 = currentRow[j - 1]; + currentRow[j] = tIJm1 + (tIJm1 - previousRow[j - 1]) / r; + } + final double s = currentRow[i]; + if (i >= minimalIterationCount) { + final double delta = FastMath.abs(s - olds); + final double rLimit = relativeAccuracy * (FastMath.abs(olds) + FastMath.abs(s)) * 0.5; + if ((delta <= rLimit) || (delta <= absoluteAccuracy)) { + setResult(s, i); + return result; + } + } + olds = s; + } + throw new MaxIterationsExceededException(maximalIterationCount); + } + + /** {@inheritDoc} */ + @Override + protected void verifyIterationCount() throws IllegalArgumentException { + super.verifyIterationCount(); + // at most 32 bisection refinements due to higher order divider + if (maximalIterationCount > 32) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.INVALID_ITERATIONS_LIMITS, + 0, 32); + } + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/integration/SimpsonIntegrator.java b/src/main/java/org/apache/commons/math/analysis/integration/SimpsonIntegrator.java new file mode 100644 index 0000000..045b54e --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/integration/SimpsonIntegrator.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.integration; + +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.MaxIterationsExceededException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.util.FastMath; + +/** + * Implements the <a href="http://mathworld.wolfram.com/SimpsonsRule.html"> + * Simpson's Rule</a> for integration of real univariate functions. For + * reference, see <b>Introduction to Numerical Analysis</b>, ISBN 038795452X, + * chapter 3. + * <p> + * This implementation employs basic trapezoid rule as building blocks to + * calculate the Simpson's rule of alternating 2/3 and 4/3.</p> + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + * @since 1.2 + */ +public class SimpsonIntegrator extends UnivariateRealIntegratorImpl { + + /** + * Construct an integrator for the given function. + * + * @param f function to integrate + * @deprecated as of 2.0 the integrand function is passed as an argument + * to the {@link #integrate(UnivariateRealFunction, double, double)}method. + */ + @Deprecated + public SimpsonIntegrator(UnivariateRealFunction f) { + super(f, 64); + } + + /** + * Construct an integrator. + */ + public SimpsonIntegrator() { + super(64); + } + + /** {@inheritDoc} */ + @Deprecated + public double integrate(final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException, IllegalArgumentException { + return integrate(f, min, max); + } + + /** {@inheritDoc} */ + public double integrate(final UnivariateRealFunction f, final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException, IllegalArgumentException { + + clearResult(); + verifyInterval(min, max); + verifyIterationCount(); + + TrapezoidIntegrator qtrap = new TrapezoidIntegrator(); + if (minimalIterationCount == 1) { + final double s = (4 * qtrap.stage(f, min, max, 1) - qtrap.stage(f, min, max, 0)) / 3.0; + setResult(s, 1); + return result; + } + // Simpson's rule requires at least two trapezoid stages. + double olds = 0; + double oldt = qtrap.stage(f, min, max, 0); + for (int i = 1; i <= maximalIterationCount; ++i) { + final double t = qtrap.stage(f, min, max, i); + final double s = (4 * t - oldt) / 3.0; + if (i >= minimalIterationCount) { + final double delta = FastMath.abs(s - olds); + final double rLimit = + relativeAccuracy * (FastMath.abs(olds) + FastMath.abs(s)) * 0.5; + if ((delta <= rLimit) || (delta <= absoluteAccuracy)) { + setResult(s, i); + return result; + } + } + olds = s; + oldt = t; + } + throw new MaxIterationsExceededException(maximalIterationCount); + } + + /** {@inheritDoc} */ + @Override + protected void verifyIterationCount() throws IllegalArgumentException { + super.verifyIterationCount(); + // at most 64 bisection refinements + if (maximalIterationCount > 64) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.INVALID_ITERATIONS_LIMITS, + 0, 64); + } + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/integration/TrapezoidIntegrator.java b/src/main/java/org/apache/commons/math/analysis/integration/TrapezoidIntegrator.java new file mode 100644 index 0000000..88903f5 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/integration/TrapezoidIntegrator.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.integration; + +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.MaxIterationsExceededException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.util.FastMath; + +/** + * Implements the <a href="http://mathworld.wolfram.com/TrapezoidalRule.html"> + * Trapezoidal Rule</a> for integration of real univariate functions. For + * reference, see <b>Introduction to Numerical Analysis</b>, ISBN 038795452X, + * chapter 3. + * <p> + * The function should be integrable.</p> + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + * @since 1.2 + */ +public class TrapezoidIntegrator extends UnivariateRealIntegratorImpl { + + /** Intermediate result. */ + private double s; + + /** + * Construct an integrator for the given function. + * + * @param f function to integrate + * @deprecated as of 2.0 the integrand function is passed as an argument + * to the {@link #integrate(UnivariateRealFunction, double, double)}method. + */ + @Deprecated + public TrapezoidIntegrator(UnivariateRealFunction f) { + super(f, 64); + } + + /** + * Construct an integrator. + */ + public TrapezoidIntegrator() { + super(64); + } + + /** + * Compute the n-th stage integral of trapezoid rule. This function + * should only be called by API <code>integrate()</code> in the package. + * To save time it does not verify arguments - caller does. + * <p> + * The interval is divided equally into 2^n sections rather than an + * arbitrary m sections because this configuration can best utilize the + * alrealy computed values.</p> + * + * @param f the integrand function + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param n the stage of 1/2 refinement, n = 0 is no refinement + * @return the value of n-th stage integral + * @throws FunctionEvaluationException if an error occurs evaluating the function + */ + double stage(final UnivariateRealFunction f, + final double min, final double max, final int n) + throws FunctionEvaluationException { + + if (n == 0) { + s = 0.5 * (max - min) * (f.value(min) + f.value(max)); + return s; + } else { + final long np = 1L << (n-1); // number of new points in this stage + double sum = 0; + final double spacing = (max - min) / np; // spacing between adjacent new points + double x = min + 0.5 * spacing; // the first new point + for (long i = 0; i < np; i++) { + sum += f.value(x); + x += spacing; + } + // add the new sum to previously calculated result + s = 0.5 * (s + sum * spacing); + return s; + } + } + + /** {@inheritDoc} */ + @Deprecated + public double integrate(final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException, IllegalArgumentException { + return integrate(f, min, max); + } + + /** {@inheritDoc} */ + public double integrate(final UnivariateRealFunction f, final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException, IllegalArgumentException { + + clearResult(); + verifyInterval(min, max); + verifyIterationCount(); + + double oldt = stage(f, min, max, 0); + for (int i = 1; i <= maximalIterationCount; ++i) { + final double t = stage(f, min, max, i); + if (i >= minimalIterationCount) { + final double delta = FastMath.abs(t - oldt); + final double rLimit = + relativeAccuracy * (FastMath.abs(oldt) + FastMath.abs(t)) * 0.5; + if ((delta <= rLimit) || (delta <= absoluteAccuracy)) { + setResult(t, i); + return result; + } + } + oldt = t; + } + throw new MaxIterationsExceededException(maximalIterationCount); + } + + /** {@inheritDoc} */ + @Override + protected void verifyIterationCount() throws IllegalArgumentException { + super.verifyIterationCount(); + // at most 64 bisection refinements + if (maximalIterationCount > 64) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.INVALID_ITERATIONS_LIMITS, + 0, 64); + } + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegrator.java b/src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegrator.java new file mode 100644 index 0000000..184bb44 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegrator.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.integration; + +import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.ConvergingAlgorithm; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.analysis.UnivariateRealFunction; + +/** + * Interface for univariate real integration algorithms. + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + * @since 1.2 + */ +public interface UnivariateRealIntegrator extends ConvergingAlgorithm { + + /** + * Set the lower limit for the number of iterations. + * <p> + * Minimal iteration is needed to avoid false early convergence, e.g. + * the sample points happen to be zeroes of the function. Users can + * use the default value or choose one that they see as appropriate.</p> + * <p> + * A <code>ConvergenceException</code> will be thrown if this number + * is not met.</p> + * + * @param count minimum number of iterations + */ + void setMinimalIterationCount(int count); + + /** + * Get the lower limit for the number of iterations. + * + * @return the actual lower limit + */ + int getMinimalIterationCount(); + + /** + * Reset the lower limit for the number of iterations to the default. + * <p> + * The default value is supplied by the implementation.</p> + * + * @see #setMinimalIterationCount(int) + */ + void resetMinimalIterationCount(); + + /** + * Integrate the function in the given interval. + * + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @return the value of integral + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the integrator detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the + * function + * @throws IllegalArgumentException if min > max or the endpoints do not + * satisfy the requirements specified by the integrator + * @deprecated replaced by {@link #integrate(UnivariateRealFunction, double, double)} + * since 2.0 + */ + @Deprecated + double integrate(double min, double max) + throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException; + + /** + * Integrate the function in the given interval. + * + * @param f the integrand function + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @return the value of integral + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the integrator detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min > max or the endpoints do not + * satisfy the requirements specified by the integrator + */ + double integrate(UnivariateRealFunction f, double min, double max) + throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException; + + /** + * Get the result of the last run of the integrator. + * + * @return the last result + * @throws IllegalStateException if there is no result available, either + * because no result was yet computed or the last attempt failed + */ + double getResult() throws IllegalStateException; + +} diff --git a/src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegratorImpl.java b/src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegratorImpl.java new file mode 100644 index 0000000..655a852 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegratorImpl.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.integration; + +import org.apache.commons.math.ConvergingAlgorithmImpl; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.exception.NullArgumentException; + +/** + * Provide a default implementation for several generic functions. + * + * @version $Revision: 1072409 $ $Date: 2011-02-19 19:50:36 +0100 (sam. 19 févr. 2011) $ + * @since 1.2 + */ +public abstract class UnivariateRealIntegratorImpl + extends ConvergingAlgorithmImpl implements UnivariateRealIntegrator { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 6248808456637441533L; + + /** minimum number of iterations */ + protected int minimalIterationCount; + + /** default minimum number of iterations */ + protected int defaultMinimalIterationCount; + + /** indicates whether an integral has been computed */ + protected boolean resultComputed = false; + + /** the last computed integral */ + protected double result; + + /** + * The integrand function. + * + * @deprecated as of 2.0 the integrand function is passed as an argument + * to the {@link #integrate(UnivariateRealFunction, double, double)}method. + */ + @Deprecated + protected UnivariateRealFunction f; + + /** + * Construct an integrator with given iteration count and accuracy. + * + * @param f the integrand function + * @param defaultMaximalIterationCount maximum number of iterations + * @throws IllegalArgumentException if f is null or the iteration + * limits are not valid + * @deprecated as of 2.0 the integrand function is passed as an argument + * to the {@link #integrate(UnivariateRealFunction, double, double)}method. + */ + @Deprecated + protected UnivariateRealIntegratorImpl(final UnivariateRealFunction f, + final int defaultMaximalIterationCount) + throws IllegalArgumentException { + super(defaultMaximalIterationCount, 1.0e-15); + if (f == null) { + throw new NullArgumentException(LocalizedFormats.FUNCTION); + } + + this.f = f; + + // parameters that are problem specific + setRelativeAccuracy(1.0e-6); + this.defaultMinimalIterationCount = 3; + this.minimalIterationCount = defaultMinimalIterationCount; + + verifyIterationCount(); + } + + /** + * Construct an integrator with given iteration count and accuracy. + * + * @param defaultMaximalIterationCount maximum number of iterations + * @throws IllegalArgumentException if f is null or the iteration + * limits are not valid + */ + protected UnivariateRealIntegratorImpl(final int defaultMaximalIterationCount) + throws IllegalArgumentException { + super(defaultMaximalIterationCount, 1.0e-15); + + // parameters that are problem specific + setRelativeAccuracy(1.0e-6); + this.defaultMinimalIterationCount = 3; + this.minimalIterationCount = defaultMinimalIterationCount; + + verifyIterationCount(); + } + + /** + * Access the last computed integral. + * + * @return the last computed integral + * @throws IllegalStateException if no integral has been computed + */ + public double getResult() throws IllegalStateException { + if (resultComputed) { + return result; + } else { + throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_RESULT_AVAILABLE); + } + } + + /** + * Convenience function for implementations. + * + * @param newResult the result to set + * @param iterationCount the iteration count to set + */ + protected final void setResult(double newResult, int iterationCount) { + this.result = newResult; + this.iterationCount = iterationCount; + this.resultComputed = true; + } + + /** + * Convenience function for implementations. + */ + protected final void clearResult() { + this.iterationCount = 0; + this.resultComputed = false; + } + + /** {@inheritDoc} */ + public void setMinimalIterationCount(int count) { + minimalIterationCount = count; + } + + /** {@inheritDoc} */ + public int getMinimalIterationCount() { + return minimalIterationCount; + } + + /** {@inheritDoc} */ + public void resetMinimalIterationCount() { + minimalIterationCount = defaultMinimalIterationCount; + } + + /** + * Verifies that the endpoints specify an interval. + * + * @param lower lower endpoint + * @param upper upper endpoint + * @throws IllegalArgumentException if not interval + */ + protected void verifyInterval(double lower, double upper) throws + IllegalArgumentException { + if (lower >= upper) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.ENDPOINTS_NOT_AN_INTERVAL, + lower, upper); + } + } + + /** + * Verifies that the upper and lower limits of iterations are valid. + * + * @throws IllegalArgumentException if not valid + */ + protected void verifyIterationCount() throws IllegalArgumentException { + if ((minimalIterationCount <= 0) || (maximalIterationCount <= minimalIterationCount)) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.INVALID_ITERATIONS_LIMITS, + minimalIterationCount, maximalIterationCount); + } + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/integration/package.html b/src/main/java/org/apache/commons/math/analysis/integration/package.html new file mode 100644 index 0000000..5bbab0e --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/integration/package.html @@ -0,0 +1,22 @@ +<html> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ --> + <body> + Numerical integration (quadrature) algorithms for univariate real functions. + </body> +</html> diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatingFunction.java b/src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatingFunction.java new file mode 100644 index 0000000..c786a4d --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatingFunction.java @@ -0,0 +1,558 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.DimensionMismatchException; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.analysis.BivariateRealFunction; +import org.apache.commons.math.exception.NoDataException; +import org.apache.commons.math.exception.OutOfRangeException; +import org.apache.commons.math.util.MathUtils; + +/** + * Function that implements the + * <a href="http://en.wikipedia.org/wiki/Bicubic_interpolation"> + * bicubic spline interpolation</a>. + * + * @version $Revision$ $Date$ + * @since 2.1 + */ +public class BicubicSplineInterpolatingFunction + implements BivariateRealFunction { + /** + * Matrix to compute the spline coefficients from the function values + * and function derivatives values + */ + private static final double[][] AINV = { + { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 }, + { -3,3,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0 }, + { 2,-2,0,0,1,1,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0 }, + { 0,0,0,0,0,0,0,0,-3,3,0,0,-2,-1,0,0 }, + { 0,0,0,0,0,0,0,0,2,-2,0,0,1,1,0,0 }, + { -3,0,3,0,0,0,0,0,-2,0,-1,0,0,0,0,0 }, + { 0,0,0,0,-3,0,3,0,0,0,0,0,-2,0,-1,0 }, + { 9,-9,-9,9,6,3,-6,-3,6,-6,3,-3,4,2,2,1 }, + { -6,6,6,-6,-3,-3,3,3,-4,4,-2,2,-2,-2,-1,-1 }, + { 2,0,-2,0,0,0,0,0,1,0,1,0,0,0,0,0 }, + { 0,0,0,0,2,0,-2,0,0,0,0,0,1,0,1,0 }, + { -6,6,6,-6,-4,-2,4,2,-3,3,-3,3,-2,-1,-2,-1 }, + { 4,-4,-4,4,2,2,-2,-2,2,-2,2,-2,1,1,1,1 } + }; + + /** Samples x-coordinates */ + private final double[] xval; + /** Samples y-coordinates */ + private final double[] yval; + /** Set of cubic splines patching the whole data grid */ + private final BicubicSplineFunction[][] splines; + /** + * Partial derivatives + * The value of the first index determines the kind of derivatives: + * 0 = first partial derivatives wrt x + * 1 = first partial derivatives wrt y + * 2 = second partial derivatives wrt x + * 3 = second partial derivatives wrt y + * 4 = cross partial derivatives + */ + private BivariateRealFunction[][][] partialDerivatives = null; + + /** + * @param x Sample values of the x-coordinate, in increasing order. + * @param y Sample values of the y-coordinate, in increasing order. + * @param f Values of the function on every grid point. + * @param dFdX Values of the partial derivative of function with respect + * to x on every grid point. + * @param dFdY Values of the partial derivative of function with respect + * to y on every grid point. + * @param d2FdXdY Values of the cross partial derivative of function on + * every grid point. + * @throws DimensionMismatchException if the various arrays do not contain + * the expected number of elements. + * @throws org.apache.commons.math.exception.NonMonotonousSequenceException + * if {@code x} or {@code y} are not strictly increasing. + * @throws NoDataException if any of the arrays has zero length. + */ + public BicubicSplineInterpolatingFunction(double[] x, + double[] y, + double[][] f, + double[][] dFdX, + double[][] dFdY, + double[][] d2FdXdY) + throws DimensionMismatchException { + final int xLen = x.length; + final int yLen = y.length; + + if (xLen == 0 || yLen == 0 || f.length == 0 || f[0].length == 0) { + throw new NoDataException(); + } + if (xLen != f.length) { + throw new DimensionMismatchException(xLen, f.length); + } + if (xLen != dFdX.length) { + throw new DimensionMismatchException(xLen, dFdX.length); + } + if (xLen != dFdY.length) { + throw new DimensionMismatchException(xLen, dFdY.length); + } + if (xLen != d2FdXdY.length) { + throw new DimensionMismatchException(xLen, d2FdXdY.length); + } + + MathUtils.checkOrder(x); + MathUtils.checkOrder(y); + + xval = x.clone(); + yval = y.clone(); + + final int lastI = xLen - 1; + final int lastJ = yLen - 1; + splines = new BicubicSplineFunction[lastI][lastJ]; + + for (int i = 0; i < lastI; i++) { + if (f[i].length != yLen) { + throw new DimensionMismatchException(f[i].length, yLen); + } + if (dFdX[i].length != yLen) { + throw new DimensionMismatchException(dFdX[i].length, yLen); + } + if (dFdY[i].length != yLen) { + throw new DimensionMismatchException(dFdY[i].length, yLen); + } + if (d2FdXdY[i].length != yLen) { + throw new DimensionMismatchException(d2FdXdY[i].length, yLen); + } + final int ip1 = i + 1; + for (int j = 0; j < lastJ; j++) { + final int jp1 = j + 1; + final double[] beta = new double[] { + f[i][j], f[ip1][j], f[i][jp1], f[ip1][jp1], + dFdX[i][j], dFdX[ip1][j], dFdX[i][jp1], dFdX[ip1][jp1], + dFdY[i][j], dFdY[ip1][j], dFdY[i][jp1], dFdY[ip1][jp1], + d2FdXdY[i][j], d2FdXdY[ip1][j], d2FdXdY[i][jp1], d2FdXdY[ip1][jp1] + }; + + splines[i][j] = new BicubicSplineFunction(computeSplineCoefficients(beta)); + } + } + } + + /** + * {@inheritDoc} + */ + public double value(double x, double y) { + final int i = searchIndex(x, xval); + if (i == -1) { + throw new OutOfRangeException(x, xval[0], xval[xval.length - 1]); + } + final int j = searchIndex(y, yval); + if (j == -1) { + throw new OutOfRangeException(y, yval[0], yval[yval.length - 1]); + } + + final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]); + final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]); + + return splines[i][j].value(xN, yN); + } + + /** + * @param x x-coordinate. + * @param y y-coordinate. + * @return the value at point (x, y) of the first partial derivative with + * respect to x. + * @since 2.2 + */ + public double partialDerivativeX(double x, double y) { + return partialDerivative(0, x, y); + } + /** + * @param x x-coordinate. + * @param y y-coordinate. + * @return the value at point (x, y) of the first partial derivative with + * respect to y. + * @since 2.2 + */ + public double partialDerivativeY(double x, double y) { + return partialDerivative(1, x, y); + } + /** + * @param x x-coordinate. + * @param y y-coordinate. + * @return the value at point (x, y) of the second partial derivative with + * respect to x. + * @since 2.2 + */ + public double partialDerivativeXX(double x, double y) { + return partialDerivative(2, x, y); + } + /** + * @param x x-coordinate. + * @param y y-coordinate. + * @return the value at point (x, y) of the second partial derivative with + * respect to y. + * @since 2.2 + */ + public double partialDerivativeYY(double x, double y) { + return partialDerivative(3, x, y); + } + /** + * @param x x-coordinate. + * @param y y-coordinate. + * @return the value at point (x, y) of the second partial cross-derivative. + * @since 2.2 + */ + public double partialDerivativeXY(double x, double y) { + return partialDerivative(4, x, y); + } + + /** + * @param which First index in {@link #partialDerivatives}. + * @param x x-coordinate. + * @param y y-coordinate. + * @return the value at point (x, y) of the selected partial derivative. + * @throws FunctionEvaluationException + */ + private double partialDerivative(int which, double x, double y) { + if (partialDerivatives == null) { + computePartialDerivatives(); + } + + final int i = searchIndex(x, xval); + if (i == -1) { + throw new OutOfRangeException(x, xval[0], xval[xval.length - 1]); + } + final int j = searchIndex(y, yval); + if (j == -1) { + throw new OutOfRangeException(y, yval[0], yval[yval.length - 1]); + } + + final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]); + final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]); + + try { + return partialDerivatives[which][i][j].value(xN, yN); + } catch (FunctionEvaluationException fee) { + // this should never happen + throw new RuntimeException(fee); + } + + } + + /** + * Compute all partial derivatives. + */ + private void computePartialDerivatives() { + final int lastI = xval.length - 1; + final int lastJ = yval.length - 1; + partialDerivatives = new BivariateRealFunction[5][lastI][lastJ]; + + for (int i = 0; i < lastI; i++) { + for (int j = 0; j < lastJ; j++) { + final BicubicSplineFunction f = splines[i][j]; + partialDerivatives[0][i][j] = f.partialDerivativeX(); + partialDerivatives[1][i][j] = f.partialDerivativeY(); + partialDerivatives[2][i][j] = f.partialDerivativeXX(); + partialDerivatives[3][i][j] = f.partialDerivativeYY(); + partialDerivatives[4][i][j] = f.partialDerivativeXY(); + } + } + } + + /** + * @param c Coordinate. + * @param val Coordinate samples. + * @return the index in {@code val} corresponding to the interval + * containing {@code c}, or {@code -1} if {@code c} is out of the + * range defined by the end values of {@code val}. + */ + private int searchIndex(double c, double[] val) { + if (c < val[0]) { + return -1; + } + + final int max = val.length; + for (int i = 1; i < max; i++) { + if (c <= val[i]) { + return i - 1; + } + } + + return -1; + } + + /** + * Compute the spline coefficients from the list of function values and + * function partial derivatives values at the four corners of a grid + * element. They must be specified in the following order: + * <ul> + * <li>f(0,0)</li> + * <li>f(1,0)</li> + * <li>f(0,1)</li> + * <li>f(1,1)</li> + * <li>f<sub>x</sub>(0,0)</li> + * <li>f<sub>x</sub>(1,0)</li> + * <li>f<sub>x</sub>(0,1)</li> + * <li>f<sub>x</sub>(1,1)</li> + * <li>f<sub>y</sub>(0,0)</li> + * <li>f<sub>y</sub>(1,0)</li> + * <li>f<sub>y</sub>(0,1)</li> + * <li>f<sub>y</sub>(1,1)</li> + * <li>f<sub>xy</sub>(0,0)</li> + * <li>f<sub>xy</sub>(1,0)</li> + * <li>f<sub>xy</sub>(0,1)</li> + * <li>f<sub>xy</sub>(1,1)</li> + * </ul> + * where the subscripts indicate the partial derivative with respect to + * the corresponding variable(s). + * + * @param beta List of function values and function partial derivatives + * values. + * @return the spline coefficients. + */ + private double[] computeSplineCoefficients(double[] beta) { + final double[] a = new double[16]; + + for (int i = 0; i < 16; i++) { + double result = 0; + final double[] row = AINV[i]; + for (int j = 0; j < 16; j++) { + result += row[j] * beta[j]; + } + a[i] = result; + } + + return a; + } +} + +/** + * 2D-spline function. + * + * @version $Revision$ $Date$ + */ +class BicubicSplineFunction + implements BivariateRealFunction { + + /** Number of points. */ + private static final short N = 4; + + /** Coefficients */ + private final double[][] a; + + /** First partial derivative along x. */ + private BivariateRealFunction partialDerivativeX; + + /** First partial derivative along y. */ + private BivariateRealFunction partialDerivativeY; + + /** Second partial derivative along x. */ + private BivariateRealFunction partialDerivativeXX; + + /** Second partial derivative along y. */ + private BivariateRealFunction partialDerivativeYY; + + /** Second crossed partial derivative. */ + private BivariateRealFunction partialDerivativeXY; + + /** + * Simple constructor. + * @param a Spline coefficients + */ + public BicubicSplineFunction(double[] a) { + this.a = new double[N][N]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + this.a[i][j] = a[i + N * j]; + } + } + } + + /** + * {@inheritDoc} + */ + public double value(double x, double y) { + if (x < 0 || x > 1) { + throw new OutOfRangeException(x, 0, 1); + } + if (y < 0 || y > 1) { + throw new OutOfRangeException(y, 0, 1); + } + + final double x2 = x * x; + final double x3 = x2 * x; + final double[] pX = {1, x, x2, x3}; + + final double y2 = y * y; + final double y3 = y2 * y; + final double[] pY = {1, y, y2, y3}; + + return apply(pX, pY, a); + } + + /** + * Compute the value of the bicubic polynomial. + * + * @param pX Powers of the x-coordinate. + * @param pY Powers of the y-coordinate. + * @param coeff Spline coefficients. + * @return the interpolated value. + */ + private double apply(double[] pX, double[] pY, double[][] coeff) { + double result = 0; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + result += coeff[i][j] * pX[i] * pY[j]; + } + } + + return result; + } + + /** + * @return the partial derivative wrt {@code x}. + */ + public BivariateRealFunction partialDerivativeX() { + if (partialDerivativeX == null) { + computePartialDerivatives(); + } + + return partialDerivativeX; + } + /** + * @return the partial derivative wrt {@code y}. + */ + public BivariateRealFunction partialDerivativeY() { + if (partialDerivativeY == null) { + computePartialDerivatives(); + } + + return partialDerivativeY; + } + /** + * @return the second partial derivative wrt {@code x}. + */ + public BivariateRealFunction partialDerivativeXX() { + if (partialDerivativeXX == null) { + computePartialDerivatives(); + } + + return partialDerivativeXX; + } + /** + * @return the second partial derivative wrt {@code y}. + */ + public BivariateRealFunction partialDerivativeYY() { + if (partialDerivativeYY == null) { + computePartialDerivatives(); + } + + return partialDerivativeYY; + } + /** + * @return the second partial cross-derivative. + */ + public BivariateRealFunction partialDerivativeXY() { + if (partialDerivativeXY == null) { + computePartialDerivatives(); + } + + return partialDerivativeXY; + } + + /** + * Compute all partial derivatives functions. + */ + private void computePartialDerivatives() { + final double[][] aX = new double[N][N]; + final double[][] aY = new double[N][N]; + final double[][] aXX = new double[N][N]; + final double[][] aYY = new double[N][N]; + final double[][] aXY = new double[N][N]; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + final double c = a[i][j]; + aX[i][j] = i * c; + aY[i][j] = j * c; + aXX[i][j] = (i - 1) * aX[i][j]; + aYY[i][j] = (j - 1) * aY[i][j]; + aXY[i][j] = j * aX[i][j]; + } + } + + partialDerivativeX = new BivariateRealFunction() { + public double value(double x, double y) { + final double x2 = x * x; + final double[] pX = {0, 1, x, x2}; + + final double y2 = y * y; + final double y3 = y2 * y; + final double[] pY = {1, y, y2, y3}; + + return apply(pX, pY, aX); + } + }; + partialDerivativeY = new BivariateRealFunction() { + public double value(double x, double y) { + final double x2 = x * x; + final double x3 = x2 * x; + final double[] pX = {1, x, x2, x3}; + + final double y2 = y * y; + final double[] pY = {0, 1, y, y2}; + + return apply(pX, pY, aY); + } + }; + partialDerivativeXX = new BivariateRealFunction() { + public double value(double x, double y) { + final double[] pX = {0, 0, 1, x}; + + final double y2 = y * y; + final double y3 = y2 * y; + final double[] pY = {1, y, y2, y3}; + + return apply(pX, pY, aXX); + } + }; + partialDerivativeYY = new BivariateRealFunction() { + public double value(double x, double y) { + final double x2 = x * x; + final double x3 = x2 * x; + final double[] pX = {1, x, x2, x3}; + + final double[] pY = {0, 0, 1, y}; + + return apply(pX, pY, aYY); + } + }; + partialDerivativeXY = new BivariateRealFunction() { + public double value(double x, double y) { + final double x2 = x * x; + final double[] pX = {0, 1, x, x2}; + + final double y2 = y * y; + final double[] pY = {0, 1, y, y2}; + + return apply(pX, pY, aXY); + } + }; + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolator.java new file mode 100644 index 0000000..42f73c8 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolator.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.DimensionMismatchException; +import org.apache.commons.math.MathException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; +import org.apache.commons.math.exception.NoDataException; +import org.apache.commons.math.util.MathUtils; + +/** + * Generates a bicubic interpolating function. + * + * @version $Revision: 980944 $ $Date: 2010-07-30 22:31:11 +0200 (ven. 30 juil. 2010) $ + * @since 2.2 + */ +public class BicubicSplineInterpolator + implements BivariateRealGridInterpolator { + /** + * {@inheritDoc} + */ + public BicubicSplineInterpolatingFunction interpolate(final double[] xval, + final double[] yval, + final double[][] fval) + throws MathException, IllegalArgumentException { + if (xval.length == 0 || yval.length == 0 || fval.length == 0) { + throw new NoDataException(); + } + if (xval.length != fval.length) { + throw new DimensionMismatchException(xval.length, fval.length); + } + + MathUtils.checkOrder(xval); + MathUtils.checkOrder(yval); + + final int xLen = xval.length; + final int yLen = yval.length; + + // Samples (first index is y-coordinate, i.e. subarray variable is x) + // 0 <= i < xval.length + // 0 <= j < yval.length + // fX[j][i] = f(xval[i], yval[j]) + final double[][] fX = new double[yLen][xLen]; + for (int i = 0; i < xLen; i++) { + if (fval[i].length != yLen) { + throw new DimensionMismatchException(fval[i].length, yLen); + } + + for (int j = 0; j < yLen; j++) { + fX[j][i] = fval[i][j]; + } + } + + final SplineInterpolator spInterpolator = new SplineInterpolator(); + + // For each line y[j] (0 <= j < yLen), construct a 1D spline with + // respect to variable x + final PolynomialSplineFunction[] ySplineX = new PolynomialSplineFunction[yLen]; + for (int j = 0; j < yLen; j++) { + ySplineX[j] = spInterpolator.interpolate(xval, fX[j]); + } + + // For each line x[i] (0 <= i < xLen), construct a 1D spline with + // respect to variable y generated by array fY_1[i] + final PolynomialSplineFunction[] xSplineY = new PolynomialSplineFunction[xLen]; + for (int i = 0; i < xLen; i++) { + xSplineY[i] = spInterpolator.interpolate(yval, fval[i]); + } + + // Partial derivatives with respect to x at the grid knots + final double[][] dFdX = new double[xLen][yLen]; + for (int j = 0; j < yLen; j++) { + final UnivariateRealFunction f = ySplineX[j].derivative(); + for (int i = 0; i < xLen; i++) { + dFdX[i][j] = f.value(xval[i]); + } + } + + // Partial derivatives with respect to y at the grid knots + final double[][] dFdY = new double[xLen][yLen]; + for (int i = 0; i < xLen; i++) { + final UnivariateRealFunction f = xSplineY[i].derivative(); + for (int j = 0; j < yLen; j++) { + dFdY[i][j] = f.value(yval[j]); + } + } + + // Cross partial derivatives + final double[][] d2FdXdY = new double[xLen][yLen]; + for (int i = 0; i < xLen ; i++) { + final int nI = nextIndex(i, xLen); + final int pI = previousIndex(i); + for (int j = 0; j < yLen; j++) { + final int nJ = nextIndex(j, yLen); + final int pJ = previousIndex(j); + d2FdXdY[i][j] = (fval[nI][nJ] - fval[nI][pJ] - + fval[pI][nJ] + fval[pI][pJ]) / + ((xval[nI] - xval[pI]) * (yval[nJ] - yval[pJ])); + } + } + + // Create the interpolating splines + return new BicubicSplineInterpolatingFunction(xval, yval, fval, + dFdX, dFdY, d2FdXdY); + } + + /** + * Compute the next index of an array, clipping if necessary. + * It is assumed (but not checked) that {@code i} is larger than or equal to 0}. + * + * @param i Index + * @param max Upper limit of the array + * @return the next index + */ + private int nextIndex(int i, int max) { + final int index = i + 1; + return index < max ? index : index - 1; + } + /** + * Compute the previous index of an array, clipping if necessary. + * It is assumed (but not checked) that {@code i} is smaller than the size of the array. + * + * @param i Index + * @return the previous index + */ + private int previousIndex(int i) { + final int index = i - 1; + return index >= 0 ? index : 0; + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/BivariateRealGridInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/BivariateRealGridInterpolator.java new file mode 100644 index 0000000..218d328 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/BivariateRealGridInterpolator.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.MathException; +import org.apache.commons.math.analysis.BivariateRealFunction; + +/** + * Interface representing a bivariate real interpolating function where the + * sample points must be specified on a regular grid. + * + * @version $Revision: 936391 $ $Date: 2010-04-21 19:00:56 +0200 (mer. 21 avril 2010) $ + */ +public interface BivariateRealGridInterpolator { + /** + * Computes an interpolating function for the data set. + * + * @param xval All the x-coordinates of the interpolation points, sorted + * in increasing order. + * @param yval All the y-coordinates of the interpolation points, sorted + * in increasing order. + * @param fval The values of the interpolation points on all the grid knots: + * {@code fval[i][j] = f(xval[i], yval[j])}. + * @return a function which interpolates the data set. + * @throws MathException if arguments violate assumptions made by the + * interpolation algorithm. + */ + BivariateRealFunction interpolate(double[] xval, double[] yval, double[][] fval) + throws MathException; +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/DividedDifferenceInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/DividedDifferenceInterpolator.java new file mode 100644 index 0000000..9b80079 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/DividedDifferenceInterpolator.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import java.io.Serializable; + +import org.apache.commons.math.DuplicateSampleAbscissaException; +import org.apache.commons.math.analysis.polynomials.PolynomialFunctionLagrangeForm; +import org.apache.commons.math.analysis.polynomials.PolynomialFunctionNewtonForm; + +/** + * Implements the <a href=" + * "http://mathworld.wolfram.com/NewtonsDividedDifferenceInterpolationFormula.html"> + * Divided Difference Algorithm</a> for interpolation of real univariate + * functions. For reference, see <b>Introduction to Numerical Analysis</b>, + * ISBN 038795452X, chapter 2. + * <p> + * The actual code of Neville's evaluation is in PolynomialFunctionLagrangeForm, + * this class provides an easy-to-use interface to it.</p> + * + * @version $Revision: 825919 $ $Date: 2009-10-16 16:51:55 +0200 (ven. 16 oct. 2009) $ + * @since 1.2 + */ +public class DividedDifferenceInterpolator implements UnivariateRealInterpolator, + Serializable { + + /** serializable version identifier */ + private static final long serialVersionUID = 107049519551235069L; + + /** + * Computes an interpolating function for the data set. + * + * @param x the interpolating points array + * @param y the interpolating values array + * @return a function which interpolates the data set + * @throws DuplicateSampleAbscissaException if arguments are invalid + */ + public PolynomialFunctionNewtonForm interpolate(double x[], double y[]) throws + DuplicateSampleAbscissaException { + + /** + * a[] and c[] are defined in the general formula of Newton form: + * p(x) = a[0] + a[1](x-c[0]) + a[2](x-c[0])(x-c[1]) + ... + + * a[n](x-c[0])(x-c[1])...(x-c[n-1]) + */ + PolynomialFunctionLagrangeForm.verifyInterpolationArray(x, y); + + /** + * When used for interpolation, the Newton form formula becomes + * p(x) = f[x0] + f[x0,x1](x-x0) + f[x0,x1,x2](x-x0)(x-x1) + ... + + * f[x0,x1,...,x[n-1]](x-x0)(x-x1)...(x-x[n-2]) + * Therefore, a[k] = f[x0,x1,...,xk], c[k] = x[k]. + * <p> + * Note x[], y[], a[] have the same length but c[]'s size is one less.</p> + */ + final double[] c = new double[x.length-1]; + System.arraycopy(x, 0, c, 0, c.length); + + final double[] a = computeDividedDifference(x, y); + return new PolynomialFunctionNewtonForm(a, c); + + } + + /** + * Returns a copy of the divided difference array. + * <p> + * The divided difference array is defined recursively by <pre> + * f[x0] = f(x0) + * f[x0,x1,...,xk] = (f(x1,...,xk) - f(x0,...,x[k-1])) / (xk - x0) + * </pre></p> + * <p> + * The computational complexity is O(N^2).</p> + * + * @param x the interpolating points array + * @param y the interpolating values array + * @return a fresh copy of the divided difference array + * @throws DuplicateSampleAbscissaException if any abscissas coincide + */ + protected static double[] computeDividedDifference(final double x[], final double y[]) + throws DuplicateSampleAbscissaException { + + PolynomialFunctionLagrangeForm.verifyInterpolationArray(x, y); + + final double[] divdiff = y.clone(); // initialization + + final int n = x.length; + final double[] a = new double [n]; + a[0] = divdiff[0]; + for (int i = 1; i < n; i++) { + for (int j = 0; j < n-i; j++) { + final double denominator = x[j+i] - x[j]; + if (denominator == 0.0) { + // This happens only when two abscissas are identical. + throw new DuplicateSampleAbscissaException(x[j], j, j+i); + } + divdiff[j] = (divdiff[j+1] - divdiff[j]) / denominator; + } + a[i] = divdiff[0]; + } + + return a; + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/LinearInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/LinearInterpolator.java new file mode 100644 index 0000000..71ab9a9 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/LinearInterpolator.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.exception.DimensionMismatchException; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.exception.NumberIsTooSmallException; +import org.apache.commons.math.analysis.polynomials.PolynomialFunction; +import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; +import org.apache.commons.math.util.MathUtils; + +/** + * Implements a linear function for interpolation of real univariate functions. + * @version $Revision$ $Date$ + * @since 2.2 + */ +public class LinearInterpolator implements UnivariateRealInterpolator { + /** + * Computes a linear interpolating function for the data set. + * @param x the arguments for the interpolation points + * @param y the values for the interpolation points + * @return a function which interpolates the data set + * @throws DimensionMismatchException if {@code x} and {@code y} + * have different sizes. + * @throws org.apache.commons.math.exception.NonMonotonousSequenceException + * if {@code x} is not sorted in strict increasing order. + * @throws NumberIsTooSmallException if the size of {@code x} is smaller + * than 2. + */ + public PolynomialSplineFunction interpolate(double x[], double y[]) { + if (x.length != y.length) { + throw new DimensionMismatchException(x.length, y.length); + } + + if (x.length < 2) { + throw new NumberIsTooSmallException(LocalizedFormats.NUMBER_OF_POINTS, + x.length, 2, true); + } + + // Number of intervals. The number of data points is n + 1. + int n = x.length - 1; + + MathUtils.checkOrder(x); + + // Slope of the lines between the datapoints. + final double m[] = new double[n]; + for (int i = 0; i < n; i++) { + m[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]); + } + + PolynomialFunction polynomials[] = new PolynomialFunction[n]; + final double coefficients[] = new double[2]; + for (int i = 0; i < n; i++) { + coefficients[0] = y[i]; + coefficients[1] = m[i]; + polynomials[i] = new PolynomialFunction(coefficients); + } + + return new PolynomialSplineFunction(x, polynomials); + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/LoessInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/LoessInterpolator.java new file mode 100644 index 0000000..5f00e14 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/LoessInterpolator.java @@ -0,0 +1,463 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import java.io.Serializable; +import java.util.Arrays; + +import org.apache.commons.math.MathException; +import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; +import org.apache.commons.math.exception.util.Localizable; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.util.FastMath; + +/** + * Implements the <a href="http://en.wikipedia.org/wiki/Local_regression"> + * Local Regression Algorithm</a> (also Loess, Lowess) for interpolation of + * real univariate functions. + * <p/> + * For reference, see + * <a href="http://www.math.tau.ac.il/~yekutiel/MA seminar/Cleveland 1979.pdf"> + * William S. Cleveland - Robust Locally Weighted Regression and Smoothing + * Scatterplots</a> + * <p/> + * This class implements both the loess method and serves as an interpolation + * adapter to it, allowing to build a spline on the obtained loess fit. + * + * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $ + * @since 2.0 + */ +public class LoessInterpolator + implements UnivariateRealInterpolator, Serializable { + + /** Default value of the bandwidth parameter. */ + public static final double DEFAULT_BANDWIDTH = 0.3; + + /** Default value of the number of robustness iterations. */ + public static final int DEFAULT_ROBUSTNESS_ITERS = 2; + + /** + * Default value for accuracy. + * @since 2.1 + */ + public static final double DEFAULT_ACCURACY = 1e-12; + + /** serializable version identifier. */ + private static final long serialVersionUID = 5204927143605193821L; + + /** + * The bandwidth parameter: when computing the loess fit at + * a particular point, this fraction of source points closest + * to the current point is taken into account for computing + * a least-squares regression. + * <p/> + * A sensible value is usually 0.25 to 0.5. + */ + private final double bandwidth; + + /** + * The number of robustness iterations parameter: this many + * robustness iterations are done. + * <p/> + * A sensible value is usually 0 (just the initial fit without any + * robustness iterations) to 4. + */ + private final int robustnessIters; + + /** + * If the median residual at a certain robustness iteration + * is less than this amount, no more iterations are done. + */ + private final double accuracy; + + /** + * Constructs a new {@link LoessInterpolator} + * with a bandwidth of {@link #DEFAULT_BANDWIDTH}, + * {@link #DEFAULT_ROBUSTNESS_ITERS} robustness iterations + * and an accuracy of {#link #DEFAULT_ACCURACY}. + * See {@link #LoessInterpolator(double, int, double)} for an explanation of + * the parameters. + */ + public LoessInterpolator() { + this.bandwidth = DEFAULT_BANDWIDTH; + this.robustnessIters = DEFAULT_ROBUSTNESS_ITERS; + this.accuracy = DEFAULT_ACCURACY; + } + + /** + * Constructs a new {@link LoessInterpolator} + * with given bandwidth and number of robustness iterations. + * <p> + * Calling this constructor is equivalent to calling {link {@link + * #LoessInterpolator(double, int, double) LoessInterpolator(bandwidth, + * robustnessIters, LoessInterpolator.DEFAULT_ACCURACY)} + * </p> + * + * @param bandwidth when computing the loess fit at + * a particular point, this fraction of source points closest + * to the current point is taken into account for computing + * a least-squares regression.</br> + * A sensible value is usually 0.25 to 0.5, the default value is + * {@link #DEFAULT_BANDWIDTH}. + * @param robustnessIters This many robustness iterations are done.</br> + * A sensible value is usually 0 (just the initial fit without any + * robustness iterations) to 4, the default value is + * {@link #DEFAULT_ROBUSTNESS_ITERS}. + * @throws MathException if bandwidth does not lie in the interval [0,1] + * or if robustnessIters is negative. + * @see #LoessInterpolator(double, int, double) + */ + public LoessInterpolator(double bandwidth, int robustnessIters) throws MathException { + this(bandwidth, robustnessIters, DEFAULT_ACCURACY); + } + + /** + * Constructs a new {@link LoessInterpolator} + * with given bandwidth, number of robustness iterations and accuracy. + * + * @param bandwidth when computing the loess fit at + * a particular point, this fraction of source points closest + * to the current point is taken into account for computing + * a least-squares regression.</br> + * A sensible value is usually 0.25 to 0.5, the default value is + * {@link #DEFAULT_BANDWIDTH}. + * @param robustnessIters This many robustness iterations are done.</br> + * A sensible value is usually 0 (just the initial fit without any + * robustness iterations) to 4, the default value is + * {@link #DEFAULT_ROBUSTNESS_ITERS}. + * @param accuracy If the median residual at a certain robustness iteration + * is less than this amount, no more iterations are done. + * @throws MathException if bandwidth does not lie in the interval [0,1] + * or if robustnessIters is negative. + * @see #LoessInterpolator(double, int) + * @since 2.1 + */ + public LoessInterpolator(double bandwidth, int robustnessIters, double accuracy) throws MathException { + if (bandwidth < 0 || bandwidth > 1) { + throw new MathException(LocalizedFormats.BANDWIDTH_OUT_OF_INTERVAL, + bandwidth); + } + this.bandwidth = bandwidth; + if (robustnessIters < 0) { + throw new MathException(LocalizedFormats.NEGATIVE_ROBUSTNESS_ITERATIONS, robustnessIters); + } + this.robustnessIters = robustnessIters; + this.accuracy = accuracy; + } + + /** + * Compute an interpolating function by performing a loess fit + * on the data at the original abscissae and then building a cubic spline + * with a + * {@link org.apache.commons.math.analysis.interpolation.SplineInterpolator} + * on the resulting fit. + * + * @param xval the arguments for the interpolation points + * @param yval the values for the interpolation points + * @return A cubic spline built upon a loess fit to the data at the original abscissae + * @throws MathException if some of the following conditions are false: + * <ul> + * <li> Arguments and values are of the same size that is greater than zero</li> + * <li> The arguments are in a strictly increasing order</li> + * <li> All arguments and values are finite real numbers</li> + * </ul> + */ + public final PolynomialSplineFunction interpolate( + final double[] xval, final double[] yval) throws MathException { + return new SplineInterpolator().interpolate(xval, smooth(xval, yval)); + } + + /** + * Compute a weighted loess fit on the data at the original abscissae. + * + * @param xval the arguments for the interpolation points + * @param yval the values for the interpolation points + * @param weights point weights: coefficients by which the robustness weight of a point is multiplied + * @return values of the loess fit at corresponding original abscissae + * @throws MathException if some of the following conditions are false: + * <ul> + * <li> Arguments and values are of the same size that is greater than zero</li> + * <li> The arguments are in a strictly increasing order</li> + * <li> All arguments and values are finite real numbers</li> + * </ul> + * @since 2.1 + */ + public final double[] smooth(final double[] xval, final double[] yval, final double[] weights) + throws MathException { + if (xval.length != yval.length) { + throw new MathException(LocalizedFormats.MISMATCHED_LOESS_ABSCISSA_ORDINATE_ARRAYS, + xval.length, yval.length); + } + + final int n = xval.length; + + if (n == 0) { + throw new MathException(LocalizedFormats.LOESS_EXPECTS_AT_LEAST_ONE_POINT); + } + + checkAllFiniteReal(xval, LocalizedFormats.NON_REAL_FINITE_ABSCISSA); + checkAllFiniteReal(yval, LocalizedFormats.NON_REAL_FINITE_ORDINATE); + checkAllFiniteReal(weights, LocalizedFormats.NON_REAL_FINITE_WEIGHT); + + checkStrictlyIncreasing(xval); + + if (n == 1) { + return new double[]{yval[0]}; + } + + if (n == 2) { + return new double[]{yval[0], yval[1]}; + } + + int bandwidthInPoints = (int) (bandwidth * n); + + if (bandwidthInPoints < 2) { + throw new MathException(LocalizedFormats.TOO_SMALL_BANDWIDTH, + n, 2.0 / n, bandwidth); + } + + final double[] res = new double[n]; + + final double[] residuals = new double[n]; + final double[] sortedResiduals = new double[n]; + + final double[] robustnessWeights = new double[n]; + + // Do an initial fit and 'robustnessIters' robustness iterations. + // This is equivalent to doing 'robustnessIters+1' robustness iterations + // starting with all robustness weights set to 1. + Arrays.fill(robustnessWeights, 1); + + for (int iter = 0; iter <= robustnessIters; ++iter) { + final int[] bandwidthInterval = {0, bandwidthInPoints - 1}; + // At each x, compute a local weighted linear regression + for (int i = 0; i < n; ++i) { + final double x = xval[i]; + + // Find out the interval of source points on which + // a regression is to be made. + if (i > 0) { + updateBandwidthInterval(xval, weights, i, bandwidthInterval); + } + + final int ileft = bandwidthInterval[0]; + final int iright = bandwidthInterval[1]; + + // Compute the point of the bandwidth interval that is + // farthest from x + final int edge; + if (xval[i] - xval[ileft] > xval[iright] - xval[i]) { + edge = ileft; + } else { + edge = iright; + } + + // Compute a least-squares linear fit weighted by + // the product of robustness weights and the tricube + // weight function. + // See http://en.wikipedia.org/wiki/Linear_regression + // (section "Univariate linear case") + // and http://en.wikipedia.org/wiki/Weighted_least_squares + // (section "Weighted least squares") + double sumWeights = 0; + double sumX = 0; + double sumXSquared = 0; + double sumY = 0; + double sumXY = 0; + double denom = FastMath.abs(1.0 / (xval[edge] - x)); + for (int k = ileft; k <= iright; ++k) { + final double xk = xval[k]; + final double yk = yval[k]; + final double dist = (k < i) ? x - xk : xk - x; + final double w = tricube(dist * denom) * robustnessWeights[k] * weights[k]; + final double xkw = xk * w; + sumWeights += w; + sumX += xkw; + sumXSquared += xk * xkw; + sumY += yk * w; + sumXY += yk * xkw; + } + + final double meanX = sumX / sumWeights; + final double meanY = sumY / sumWeights; + final double meanXY = sumXY / sumWeights; + final double meanXSquared = sumXSquared / sumWeights; + + final double beta; + if (FastMath.sqrt(FastMath.abs(meanXSquared - meanX * meanX)) < accuracy) { + beta = 0; + } else { + beta = (meanXY - meanX * meanY) / (meanXSquared - meanX * meanX); + } + + final double alpha = meanY - beta * meanX; + + res[i] = beta * x + alpha; + residuals[i] = FastMath.abs(yval[i] - res[i]); + } + + // No need to recompute the robustness weights at the last + // iteration, they won't be needed anymore + if (iter == robustnessIters) { + break; + } + + // Recompute the robustness weights. + + // Find the median residual. + // An arraycopy and a sort are completely tractable here, + // because the preceding loop is a lot more expensive + System.arraycopy(residuals, 0, sortedResiduals, 0, n); + Arrays.sort(sortedResiduals); + final double medianResidual = sortedResiduals[n / 2]; + + if (FastMath.abs(medianResidual) < accuracy) { + break; + } + + for (int i = 0; i < n; ++i) { + final double arg = residuals[i] / (6 * medianResidual); + if (arg >= 1) { + robustnessWeights[i] = 0; + } else { + final double w = 1 - arg * arg; + robustnessWeights[i] = w * w; + } + } + } + + return res; + } + + /** + * Compute a loess fit on the data at the original abscissae. + * + * @param xval the arguments for the interpolation points + * @param yval the values for the interpolation points + * @return values of the loess fit at corresponding original abscissae + * @throws MathException if some of the following conditions are false: + * <ul> + * <li> Arguments and values are of the same size that is greater than zero</li> + * <li> The arguments are in a strictly increasing order</li> + * <li> All arguments and values are finite real numbers</li> + * </ul> + */ + public final double[] smooth(final double[] xval, final double[] yval) + throws MathException { + if (xval.length != yval.length) { + throw new MathException(LocalizedFormats.MISMATCHED_LOESS_ABSCISSA_ORDINATE_ARRAYS, + xval.length, yval.length); + } + + final double[] unitWeights = new double[xval.length]; + Arrays.fill(unitWeights, 1.0); + + return smooth(xval, yval, unitWeights); + } + + /** + * Given an index interval into xval that embraces a certain number of + * points closest to xval[i-1], update the interval so that it embraces + * the same number of points closest to xval[i], ignoring zero weights. + * + * @param xval arguments array + * @param weights weights array + * @param i the index around which the new interval should be computed + * @param bandwidthInterval a two-element array {left, right} such that: <p/> + * <tt>(left==0 or xval[i] - xval[left-1] > xval[right] - xval[i])</tt> + * <p/> and also <p/> + * <tt>(right==xval.length-1 or xval[right+1] - xval[i] > xval[i] - xval[left])</tt>. + * The array will be updated. + */ + private static void updateBandwidthInterval(final double[] xval, final double[] weights, + final int i, + final int[] bandwidthInterval) { + final int left = bandwidthInterval[0]; + final int right = bandwidthInterval[1]; + + // The right edge should be adjusted if the next point to the right + // is closer to xval[i] than the leftmost point of the current interval + int nextRight = nextNonzero(weights, right); + if (nextRight < xval.length && xval[nextRight] - xval[i] < xval[i] - xval[left]) { + int nextLeft = nextNonzero(weights, bandwidthInterval[0]); + bandwidthInterval[0] = nextLeft; + bandwidthInterval[1] = nextRight; + } + } + + /** + * Returns the smallest index j such that j > i && (j==weights.length || weights[j] != 0) + * @param weights weights array + * @param i the index from which to start search; must be < weights.length + * @return the smallest index j such that j > i && (j==weights.length || weights[j] != 0) + */ + private static int nextNonzero(final double[] weights, final int i) { + int j = i + 1; + while(j < weights.length && weights[j] == 0) { + j++; + } + return j; + } + + /** + * Compute the + * <a href="http://en.wikipedia.org/wiki/Local_regression#Weight_function">tricube</a> + * weight function + * + * @param x the argument + * @return (1-|x|^3)^3 + */ + private static double tricube(final double x) { + final double tmp = 1 - x * x * x; + return tmp * tmp * tmp; + } + + /** + * Check that all elements of an array are finite real numbers. + * + * @param values the values array + * @param pattern pattern of the error message + * @throws MathException if one of the values is not a finite real number + */ + private static void checkAllFiniteReal(final double[] values, final Localizable pattern) + throws MathException { + for (int i = 0; i < values.length; i++) { + final double x = values[i]; + if (Double.isInfinite(x) || Double.isNaN(x)) { + throw new MathException(pattern, i, x); + } + } + } + + /** + * Check that elements of the abscissae array are in a strictly + * increasing order. + * + * @param xval the abscissae array + * @throws MathException if the abscissae array + * is not in a strictly increasing order + */ + private static void checkStrictlyIncreasing(final double[] xval) + throws MathException { + for (int i = 0; i < xval.length; ++i) { + if (i >= 1 && xval[i - 1] >= xval[i]) { + throw new MathException(LocalizedFormats.OUT_OF_ORDER_ABSCISSA_ARRAY, + i - 1, xval[i - 1], i, xval[i]); + } + } + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolatingFunction.java b/src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolatingFunction.java new file mode 100644 index 0000000..a710e82 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolatingFunction.java @@ -0,0 +1,244 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.math.DimensionMismatchException; +import org.apache.commons.math.analysis.MultivariateRealFunction; +import org.apache.commons.math.exception.NoDataException; +import org.apache.commons.math.linear.ArrayRealVector; +import org.apache.commons.math.linear.RealVector; +import org.apache.commons.math.random.UnitSphereRandomVectorGenerator; +import org.apache.commons.math.util.FastMath; + +/** + * Interpolating function that implements the + * <a href="http://www.dudziak.com/microsphere.php">Microsphere Projection</a>. + * + * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $ + */ +public class MicrosphereInterpolatingFunction + implements MultivariateRealFunction { + /** + * Space dimension. + */ + private final int dimension; + /** + * Internal accounting data for the interpolation algorithm. + * Each element of the list corresponds to one surface element of + * the microsphere. + */ + private final List<MicrosphereSurfaceElement> microsphere; + /** + * Exponent used in the power law that computes the weights of the + * sample data. + */ + private final double brightnessExponent; + /** + * Sample data. + */ + private final Map<RealVector, Double> samples; + + /** + * Class for storing the accounting data needed to perform the + * microsphere projection. + */ + private static class MicrosphereSurfaceElement { + + /** Normal vector characterizing a surface element. */ + private final RealVector normal; + + /** Illumination received from the brightest sample. */ + private double brightestIllumination; + + /** Brightest sample. */ + private Map.Entry<RealVector, Double> brightestSample; + + /** + * @param n Normal vector characterizing a surface element + * of the microsphere. + */ + MicrosphereSurfaceElement(double[] n) { + normal = new ArrayRealVector(n); + } + + /** + * Return the normal vector. + * @return the normal vector + */ + RealVector normal() { + return normal; + } + + /** + * Reset "illumination" and "sampleIndex". + */ + void reset() { + brightestIllumination = 0; + brightestSample = null; + } + + /** + * Store the illumination and index of the brightest sample. + * @param illuminationFromSample illumination received from sample + * @param sample current sample illuminating the element + */ + void store(final double illuminationFromSample, + final Map.Entry<RealVector, Double> sample) { + if (illuminationFromSample > this.brightestIllumination) { + this.brightestIllumination = illuminationFromSample; + this.brightestSample = sample; + } + } + + /** + * Get the illumination of the element. + * @return the illumination. + */ + double illumination() { + return brightestIllumination; + } + + /** + * Get the sample illuminating the element the most. + * @return the sample. + */ + Map.Entry<RealVector, Double> sample() { + return brightestSample; + } + } + + /** + * @param xval the arguments for the interpolation points. + * {@code xval[i][0]} is the first component of interpolation point + * {@code i}, {@code xval[i][1]} is the second component, and so on + * until {@code xval[i][d-1]}, the last component of that interpolation + * point (where {@code dimension} is thus the dimension of the sampled + * space). + * @param yval the values for the interpolation points + * @param brightnessExponent Brightness dimming factor. + * @param microsphereElements Number of surface elements of the + * microsphere. + * @param rand Unit vector generator for creating the microsphere. + * @throws DimensionMismatchException if the lengths of {@code yval} and + * {@code xval} (equal to {@code n}, the number of interpolation points) + * do not match, or the the arrays {@code xval[0]} ... {@code xval[n]}, + * have lengths different from {@code dimension}. + * @throws NoDataException if there are no data (xval null or zero length) + */ + public MicrosphereInterpolatingFunction(double[][] xval, + double[] yval, + int brightnessExponent, + int microsphereElements, + UnitSphereRandomVectorGenerator rand) + throws DimensionMismatchException, NoDataException { + if (xval.length == 0 || xval[0] == null) { + throw new NoDataException(); + } + + if (xval.length != yval.length) { + throw new DimensionMismatchException(xval.length, yval.length); + } + + dimension = xval[0].length; + this.brightnessExponent = brightnessExponent; + + // Copy data samples. + samples = new HashMap<RealVector, Double>(yval.length); + for (int i = 0; i < xval.length; ++i) { + final double[] xvalI = xval[i]; + if ( xvalI.length != dimension) { + throw new DimensionMismatchException(xvalI.length, dimension); + } + + samples.put(new ArrayRealVector(xvalI), yval[i]); + } + + microsphere = new ArrayList<MicrosphereSurfaceElement>(microsphereElements); + // Generate the microsphere, assuming that a fairly large number of + // randomly generated normals will represent a sphere. + for (int i = 0; i < microsphereElements; i++) { + microsphere.add(new MicrosphereSurfaceElement(rand.nextVector())); + } + + } + + /** + * @param point Interpolation point. + * @return the interpolated value. + */ + public double value(double[] point) { + + final RealVector p = new ArrayRealVector(point); + + // Reset. + for (MicrosphereSurfaceElement md : microsphere) { + md.reset(); + } + + // Compute contribution of each sample points to the microsphere elements illumination + for (Map.Entry<RealVector, Double> sd : samples.entrySet()) { + + // Vector between interpolation point and current sample point. + final RealVector diff = sd.getKey().subtract(p); + final double diffNorm = diff.getNorm(); + + if (FastMath.abs(diffNorm) < FastMath.ulp(1d)) { + // No need to interpolate, as the interpolation point is + // actually (very close to) one of the sampled points. + return sd.getValue(); + } + + for (MicrosphereSurfaceElement md : microsphere) { + final double w = FastMath.pow(diffNorm, -brightnessExponent); + md.store(cosAngle(diff, md.normal()) * w, sd); + } + + } + + // Interpolation calculation. + double value = 0; + double totalWeight = 0; + for (MicrosphereSurfaceElement md : microsphere) { + final double iV = md.illumination(); + final Map.Entry<RealVector, Double> sd = md.sample(); + if (sd != null) { + value += iV * sd.getValue(); + totalWeight += iV; + } + } + + return value / totalWeight; + + } + + /** + * Compute the cosine of the angle between 2 vectors. + * + * @param v Vector. + * @param w Vector. + * @return cosine of the angle + */ + private double cosAngle(final RealVector v, final RealVector w) { + return v.dotProduct(w) / (v.getNorm() * w.getNorm()); + } + +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolator.java new file mode 100644 index 0000000..c2a4009 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolator.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.MathException; +import org.apache.commons.math.analysis.MultivariateRealFunction; +import org.apache.commons.math.exception.NotPositiveException; +import org.apache.commons.math.exception.NotStrictlyPositiveException; +import org.apache.commons.math.random.UnitSphereRandomVectorGenerator; + +/** + * Interpolator that implements the algorithm described in + * <em>William Dudziak</em>'s + * <a href="http://www.dudziak.com/microsphere.pdf">MS thesis</a>. + * @since 2.1 + * + * @version $Revision: 980944 $ $Date: 2010-07-30 22:31:11 +0200 (ven. 30 juil. 2010) $ + */ +public class MicrosphereInterpolator + implements MultivariateRealInterpolator { + + /** + * Default number of surface elements that composes the microsphere. + */ + public static final int DEFAULT_MICROSPHERE_ELEMENTS = 2000; + + /** + * Default exponent used the weights calculation. + */ + public static final int DEFAULT_BRIGHTNESS_EXPONENT = 2; + + /** + * Number of surface elements of the microsphere. + */ + private int microsphereElements; + + /** + * Exponent used in the power law that computes the weights of the + * sample data. + */ + private int brightnessExponent; + + /** Create a microsphere interpolator with default settings. + * <p>Calling this constructor is equivalent to call {@link + * #MicrosphereInterpolator(int, int) + * MicrosphereInterpolator(MicrosphereInterpolator.DEFAULT_MICROSPHERE_ELEMENTS, + * MicrosphereInterpolator.DEFAULT_BRIGHTNESS_EXPONENT)}.</p> + */ + public MicrosphereInterpolator() { + this(DEFAULT_MICROSPHERE_ELEMENTS, DEFAULT_BRIGHTNESS_EXPONENT); + } + + /** Create a microsphere interpolator. + * @param microsphereElements number of surface elements of the microsphere. + * @param brightnessExponent exponent used in the power law that computes the + * weights of the sample data. + * @throws NotPositiveException if {@code microsphereElements <= 0} + * or {@code brightnessExponent < 0}. + */ + public MicrosphereInterpolator(final int microsphereElements, + final int brightnessExponent) { + setMicropshereElements(microsphereElements); + setBrightnessExponent(brightnessExponent); + } + + /** + * {@inheritDoc} + */ + public MultivariateRealFunction interpolate(final double[][] xval, + final double[] yval) + throws MathException, IllegalArgumentException { + final UnitSphereRandomVectorGenerator rand + = new UnitSphereRandomVectorGenerator(xval[0].length); + return new MicrosphereInterpolatingFunction(xval, yval, + brightnessExponent, + microsphereElements, + rand); + } + + /** + * Set the brightness exponent. + * @param exponent Exponent for computing the distance dimming + * factor. + * @throws NotPositiveException if {@code exponent < 0}. + */ + public void setBrightnessExponent(final int exponent) { + if (exponent < 0) { + throw new NotPositiveException(exponent); + } + brightnessExponent = exponent; + } + + /** + * Set the number of microsphere elements. + * @param elements Number of surface elements of the microsphere. + * @throws NotStrictlyPositiveException if {@code elements <= 0}. + */ + public void setMicropshereElements(final int elements) { + if (elements <= 0) { + throw new NotStrictlyPositiveException(elements); + } + microsphereElements = elements; + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/MultivariateRealInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/MultivariateRealInterpolator.java new file mode 100644 index 0000000..ed7690c --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/MultivariateRealInterpolator.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.MathException; +import org.apache.commons.math.analysis.MultivariateRealFunction; + +/** + * Interface representing a univariate real interpolating function. + * + * @since 2.1 + * @version $Revision: 924794 $ $Date: 2010-03-18 15:15:50 +0100 (jeu. 18 mars 2010) $ + */ +public interface MultivariateRealInterpolator { + + /** + * Computes an interpolating function for the data set. + * + * @param xval the arguments for the interpolation points. + * {@code xval[i][0]} is the first component of interpolation point + * {@code i}, {@code xval[i][1]} is the second component, and so on + * until {@code xval[i][d-1]}, the last component of that interpolation + * point (where {@code d} is thus the dimension of the space). + * @param yval the values for the interpolation points + * @return a function which interpolates the data set + * @throws MathException if arguments violate assumptions made by the + * interpolation algorithm or some dimension mismatch occurs + * @throws IllegalArgumentException if there are no data (xval null or zero length) + */ + MultivariateRealFunction interpolate(double[][] xval, double[] yval) + throws MathException, IllegalArgumentException; +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/NevilleInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/NevilleInterpolator.java new file mode 100644 index 0000000..27e89cd --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/NevilleInterpolator.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import java.io.Serializable; + +import org.apache.commons.math.MathException; +import org.apache.commons.math.analysis.polynomials.PolynomialFunctionLagrangeForm; + +/** + * Implements the <a href="http://mathworld.wolfram.com/NevillesAlgorithm.html"> + * Neville's Algorithm</a> for interpolation of real univariate functions. For + * reference, see <b>Introduction to Numerical Analysis</b>, ISBN 038795452X, + * chapter 2. + * <p> + * The actual code of Neville's evalution is in PolynomialFunctionLagrangeForm, + * this class provides an easy-to-use interface to it.</p> + * + * @version $Revision: 799857 $ $Date: 2009-08-01 15:07:12 +0200 (sam. 01 août 2009) $ + * @since 1.2 + */ +public class NevilleInterpolator implements UnivariateRealInterpolator, + Serializable { + + /** serializable version identifier */ + static final long serialVersionUID = 3003707660147873733L; + + /** + * Computes an interpolating function for the data set. + * + * @param x the interpolating points array + * @param y the interpolating values array + * @return a function which interpolates the data set + * @throws MathException if arguments are invalid + */ + public PolynomialFunctionLagrangeForm interpolate(double x[], double y[]) + throws MathException { + return new PolynomialFunctionLagrangeForm(x, y); + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingBicubicSplineInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingBicubicSplineInterpolator.java new file mode 100644 index 0000000..5514433 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingBicubicSplineInterpolator.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.DimensionMismatchException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.MathException; +import org.apache.commons.math.util.MathUtils; +import org.apache.commons.math.util.MathUtils.OrderDirection; +import org.apache.commons.math.analysis.BivariateRealFunction; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; +import org.apache.commons.math.exception.util.LocalizedFormats; + +/** + * Generates a bicubic interpolation function. + * Before interpolating, smoothing of the input data is performed using + * splines. + * See <b>Handbook on splines for the user</b>, ISBN 084939404X, + * chapter 2. + * + * @version $Revision: 1059400 $ $Date: 2011-01-15 20:35:27 +0100 (sam. 15 janv. 2011) $ + * @since 2.1 + * @deprecated This class does not perform smoothing; the name is thus misleading. + * Please use {@link org.apache.commons.math.analysis.interpolation.BicubicSplineInterpolator} + * instead. If smoothing is desired, a tentative implementation is provided in class + * {@link org.apache.commons.math.analysis.interpolation.SmoothingPolynomialBicubicSplineInterpolator}. + * This class will be removed in math 3.0. + */ +@Deprecated +public class SmoothingBicubicSplineInterpolator + implements BivariateRealGridInterpolator { + /** + * {@inheritDoc} + */ + public BivariateRealFunction interpolate(final double[] xval, + final double[] yval, + final double[][] zval) + throws MathException, IllegalArgumentException { + if (xval.length == 0 || yval.length == 0 || zval.length == 0) { + throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.NO_DATA); + } + if (xval.length != zval.length) { + throw new DimensionMismatchException(xval.length, zval.length); + } + + MathUtils.checkOrder(xval, OrderDirection.INCREASING, true); + MathUtils.checkOrder(yval, OrderDirection.INCREASING, true); + + final int xLen = xval.length; + final int yLen = yval.length; + + // Samples (first index is y-coordinate, i.e. subarray variable is x) + // 0 <= i < xval.length + // 0 <= j < yval.length + // zX[j][i] = f(xval[i], yval[j]) + final double[][] zX = new double[yLen][xLen]; + for (int i = 0; i < xLen; i++) { + if (zval[i].length != yLen) { + throw new DimensionMismatchException(zval[i].length, yLen); + } + + for (int j = 0; j < yLen; j++) { + zX[j][i] = zval[i][j]; + } + } + + final SplineInterpolator spInterpolator = new SplineInterpolator(); + + // For each line y[j] (0 <= j < yLen), construct a 1D spline with + // respect to variable x + final PolynomialSplineFunction[] ySplineX = new PolynomialSplineFunction[yLen]; + for (int j = 0; j < yLen; j++) { + ySplineX[j] = spInterpolator.interpolate(xval, zX[j]); + } + + // For every knot (xval[i], yval[j]) of the grid, calculate corrected + // values zY_1 + final double[][] zY_1 = new double[xLen][yLen]; + for (int j = 0; j < yLen; j++) { + final PolynomialSplineFunction f = ySplineX[j]; + for (int i = 0; i < xLen; i++) { + zY_1[i][j] = f.value(xval[i]); + } + } + + // For each line x[i] (0 <= i < xLen), construct a 1D spline with + // respect to variable y generated by array zY_1[i] + final PolynomialSplineFunction[] xSplineY = new PolynomialSplineFunction[xLen]; + for (int i = 0; i < xLen; i++) { + xSplineY[i] = spInterpolator.interpolate(yval, zY_1[i]); + } + + // For every knot (xval[i], yval[j]) of the grid, calculate corrected + // values zY_2 + final double[][] zY_2 = new double[xLen][yLen]; + for (int i = 0; i < xLen; i++) { + final PolynomialSplineFunction f = xSplineY[i]; + for (int j = 0; j < yLen; j++) { + zY_2[i][j] = f.value(yval[j]); + } + } + + // Partial derivatives with respect to x at the grid knots + final double[][] dZdX = new double[xLen][yLen]; + for (int j = 0; j < yLen; j++) { + final UnivariateRealFunction f = ySplineX[j].derivative(); + for (int i = 0; i < xLen; i++) { + dZdX[i][j] = f.value(xval[i]); + } + } + + // Partial derivatives with respect to y at the grid knots + final double[][] dZdY = new double[xLen][yLen]; + for (int i = 0; i < xLen; i++) { + final UnivariateRealFunction f = xSplineY[i].derivative(); + for (int j = 0; j < yLen; j++) { + dZdY[i][j] = f.value(yval[j]); + } + } + + // Cross partial derivatives + final double[][] dZdXdY = new double[xLen][yLen]; + for (int i = 0; i < xLen ; i++) { + final int nI = nextIndex(i, xLen); + final int pI = previousIndex(i); + for (int j = 0; j < yLen; j++) { + final int nJ = nextIndex(j, yLen); + final int pJ = previousIndex(j); + dZdXdY[i][j] = (zY_2[nI][nJ] - zY_2[nI][pJ] - + zY_2[pI][nJ] + zY_2[pI][pJ]) / + ((xval[nI] - xval[pI]) * (yval[nJ] - yval[pJ])); + } + } + + // Create the interpolating splines + return new BicubicSplineInterpolatingFunction(xval, yval, zY_2, + dZdX, dZdY, dZdXdY); + } + + /** + * Compute the next index of an array, clipping if necessary. + * It is assumed (but not checked) that {@code i} is larger than or equal to 0}. + * + * @param i Index + * @param max Upper limit of the array + * @return the next index + */ + private int nextIndex(int i, int max) { + final int index = i + 1; + return index < max ? index : index - 1; + } + /** + * Compute the previous index of an array, clipping if necessary. + * It is assumed (but not checked) that {@code i} is smaller than the size of the array. + * + * @param i Index + * @return the previous index + */ + private int previousIndex(int i) { + final int index = i - 1; + return index >= 0 ? index : 0; + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java new file mode 100644 index 0000000..406d603 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.exception.DimensionMismatchException; +import org.apache.commons.math.exception.NoDataException; +import org.apache.commons.math.MathException; +import org.apache.commons.math.util.MathUtils; +import org.apache.commons.math.optimization.general.GaussNewtonOptimizer; +import org.apache.commons.math.optimization.fitting.PolynomialFitter; +import org.apache.commons.math.analysis.polynomials.PolynomialFunction; + +/** + * Generates a bicubic interpolation function. + * Prior to generating the interpolating function, the input is smoothed using + * polynomial fitting. + * + * @version $Revision: 1003892 $ $Date: 2010-10-02 23:28:56 +0200 (sam. 02 oct. 2010) $ + * @since 2.2 + */ +public class SmoothingPolynomialBicubicSplineInterpolator + extends BicubicSplineInterpolator { + + /** Fitter for x. */ + private final PolynomialFitter xFitter; + + /** Fitter for y. */ + private final PolynomialFitter yFitter; + + /** + * Default constructor. The degree of the fitting polynomials is set to 3. + */ + public SmoothingPolynomialBicubicSplineInterpolator() { + this(3); + } + + /** + * @param degree Degree of the polynomial fitting functions. + */ + public SmoothingPolynomialBicubicSplineInterpolator(int degree) { + this(degree, degree); + } + + /** + * @param xDegree Degree of the polynomial fitting functions along the + * x-dimension. + * @param yDegree Degree of the polynomial fitting functions along the + * y-dimension. + */ + public SmoothingPolynomialBicubicSplineInterpolator(int xDegree, + int yDegree) { + xFitter = new PolynomialFitter(xDegree, new GaussNewtonOptimizer(false)); + yFitter = new PolynomialFitter(yDegree, new GaussNewtonOptimizer(false)); + } + + /** + * {@inheritDoc} + */ + @Override + public BicubicSplineInterpolatingFunction interpolate(final double[] xval, + final double[] yval, + final double[][] fval) + throws MathException { + if (xval.length == 0 || yval.length == 0 || fval.length == 0) { + throw new NoDataException(); + } + if (xval.length != fval.length) { + throw new DimensionMismatchException(xval.length, fval.length); + } + + final int xLen = xval.length; + final int yLen = yval.length; + + for (int i = 0; i < xLen; i++) { + if (fval[i].length != yLen) { + throw new DimensionMismatchException(fval[i].length, yLen); + } + } + + MathUtils.checkOrder(xval); + MathUtils.checkOrder(yval); + + // For each line y[j] (0 <= j < yLen), construct a polynomial, with + // respect to variable x, fitting array fval[][j] + final PolynomialFunction[] yPolyX = new PolynomialFunction[yLen]; + for (int j = 0; j < yLen; j++) { + xFitter.clearObservations(); + for (int i = 0; i < xLen; i++) { + xFitter.addObservedPoint(1, xval[i], fval[i][j]); + } + + yPolyX[j] = xFitter.fit(); + } + + // For every knot (xval[i], yval[j]) of the grid, calculate corrected + // values fval_1 + final double[][] fval_1 = new double[xLen][yLen]; + for (int j = 0; j < yLen; j++) { + final PolynomialFunction f = yPolyX[j]; + for (int i = 0; i < xLen; i++) { + fval_1[i][j] = f.value(xval[i]); + } + } + + // For each line x[i] (0 <= i < xLen), construct a polynomial, with + // respect to variable y, fitting array fval_1[i][] + final PolynomialFunction[] xPolyY = new PolynomialFunction[xLen]; + for (int i = 0; i < xLen; i++) { + yFitter.clearObservations(); + for (int j = 0; j < yLen; j++) { + yFitter.addObservedPoint(1, yval[j], fval_1[i][j]); + } + + xPolyY[i] = yFitter.fit(); + } + + // For every knot (xval[i], yval[j]) of the grid, calculate corrected + // values fval_2 + final double[][] fval_2 = new double[xLen][yLen]; + for (int i = 0; i < xLen; i++) { + final PolynomialFunction f = xPolyY[i]; + for (int j = 0; j < yLen; j++) { + fval_2[i][j] = f.value(yval[j]); + } + } + + return super.interpolate(xval, yval, fval_2); + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/SplineInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/SplineInterpolator.java new file mode 100644 index 0000000..f25ba83 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/SplineInterpolator.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.exception.DimensionMismatchException; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.exception.NumberIsTooSmallException; +import org.apache.commons.math.analysis.polynomials.PolynomialFunction; +import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; +import org.apache.commons.math.util.MathUtils; + +/** + * Computes a natural (also known as "free", "unclamped") cubic spline interpolation for the data set. + * <p> + * The {@link #interpolate(double[], double[])} method returns a {@link PolynomialSplineFunction} + * consisting of n cubic polynomials, defined over the subintervals determined by the x values, + * x[0] < x[i] ... < x[n]. The x values are referred to as "knot points."</p> + * <p> + * The value of the PolynomialSplineFunction at a point x that is greater than or equal to the smallest + * knot point and strictly less than the largest knot point is computed by finding the subinterval to which + * x belongs and computing the value of the corresponding polynomial at <code>x - x[i] </code> where + * <code>i</code> is the index of the subinterval. See {@link PolynomialSplineFunction} for more details. + * </p> + * <p> + * The interpolating polynomials satisfy: <ol> + * <li>The value of the PolynomialSplineFunction at each of the input x values equals the + * corresponding y value.</li> + * <li>Adjacent polynomials are equal through two derivatives at the knot points (i.e., adjacent polynomials + * "match up" at the knot points, as do their first and second derivatives).</li> + * </ol></p> + * <p> + * The cubic spline interpolation algorithm implemented is as described in R.L. Burden, J.D. Faires, + * <u>Numerical Analysis</u>, 4th Ed., 1989, PWS-Kent, ISBN 0-53491-585-X, pp 126-131. + * </p> + * + * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $ + * + */ +public class SplineInterpolator implements UnivariateRealInterpolator { + + /** + * Computes an interpolating function for the data set. + * @param x the arguments for the interpolation points + * @param y the values for the interpolation points + * @return a function which interpolates the data set + * @throws DimensionMismatchException if {@code x} and {@code y} + * have different sizes. + * @throws org.apache.commons.math.exception.NonMonotonousSequenceException + * if {@code x} is not sorted in strict increasing order. + * @throws NumberIsTooSmallException if the size of {@code x} is smaller + * than 3. + */ + public PolynomialSplineFunction interpolate(double x[], double y[]) { + if (x.length != y.length) { + throw new DimensionMismatchException(x.length, y.length); + } + + if (x.length < 3) { + throw new NumberIsTooSmallException(LocalizedFormats.NUMBER_OF_POINTS, + x.length, 3, true); + } + + // Number of intervals. The number of data points is n + 1. + int n = x.length - 1; + + MathUtils.checkOrder(x); + + // Differences between knot points + double h[] = new double[n]; + for (int i = 0; i < n; i++) { + h[i] = x[i + 1] - x[i]; + } + + double mu[] = new double[n]; + double z[] = new double[n + 1]; + mu[0] = 0d; + z[0] = 0d; + double g = 0; + for (int i = 1; i < n; i++) { + g = 2d * (x[i+1] - x[i - 1]) - h[i - 1] * mu[i -1]; + mu[i] = h[i] / g; + z[i] = (3d * (y[i + 1] * h[i - 1] - y[i] * (x[i + 1] - x[i - 1])+ y[i - 1] * h[i]) / + (h[i - 1] * h[i]) - h[i - 1] * z[i - 1]) / g; + } + + // cubic spline coefficients -- b is linear, c quadratic, d is cubic (original y's are constants) + double b[] = new double[n]; + double c[] = new double[n + 1]; + double d[] = new double[n]; + + z[n] = 0d; + c[n] = 0d; + + for (int j = n -1; j >=0; j--) { + c[j] = z[j] - mu[j] * c[j + 1]; + b[j] = (y[j + 1] - y[j]) / h[j] - h[j] * (c[j + 1] + 2d * c[j]) / 3d; + d[j] = (c[j + 1] - c[j]) / (3d * h[j]); + } + + PolynomialFunction polynomials[] = new PolynomialFunction[n]; + double coefficients[] = new double[4]; + for (int i = 0; i < n; i++) { + coefficients[0] = y[i]; + coefficients[1] = b[i]; + coefficients[2] = c[i]; + coefficients[3] = d[i]; + polynomials[i] = new PolynomialFunction(coefficients); + } + + return new PolynomialSplineFunction(x, polynomials); + } + +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatingFunction.java b/src/main/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatingFunction.java new file mode 100644 index 0000000..ea435a3 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatingFunction.java @@ -0,0 +1,483 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.analysis.TrivariateRealFunction; +import org.apache.commons.math.exception.DimensionMismatchException; +import org.apache.commons.math.exception.NoDataException; +import org.apache.commons.math.exception.OutOfRangeException; +import org.apache.commons.math.util.MathUtils; + +/** + * Function that implements the + * <a href="http://en.wikipedia.org/wiki/Tricubic_interpolation"> + * tricubic spline interpolation</a>, as proposed in + * <quote> + * Tricubic interpolation in three dimensions<br/> + * F. Lekien and J. Marsden<br/> + * <em>Int. J. Numer. Meth. Engng</em> 2005; <b>63</b>:455-471 + * </quote> + * + * @version $Revision$ $Date$ + * @since 2.2 + */ +public class TricubicSplineInterpolatingFunction + implements TrivariateRealFunction { + /** + * Matrix to compute the spline coefficients from the function values + * and function derivatives values + */ + private static final double[][] AINV = { + { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { -3,3,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 2,-2,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { -3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 9,-9,-9,9,0,0,0,0,6,3,-6,-3,0,0,0,0,6,-6,3,-3,0,0,0,0,0,0,0,0,0,0,0,0,4,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { -6,6,6,-6,0,0,0,0,-3,-3,3,3,0,0,0,0,-4,4,-2,2,0,0,0,0,0,0,0,0,0,0,0,0,-2,-2,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { -6,6,6,-6,0,0,0,0,-4,-2,4,2,0,0,0,0,-3,3,-3,3,0,0,0,0,0,0,0,0,0,0,0,0,-2,-1,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 4,-4,-4,4,0,0,0,0,2,2,-2,-2,0,0,0,0,2,-2,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,0,0,0,0,1,1,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,-9,-9,9,0,0,0,0,0,0,0,0,0,0,0,0,6,3,-6,-3,0,0,0,0,6,-6,3,-3,0,0,0,0,4,2,2,1,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,6,-6,0,0,0,0,0,0,0,0,0,0,0,0,-3,-3,3,3,0,0,0,0,-4,4,-2,2,0,0,0,0,-2,-2,-1,-1,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,6,-6,0,0,0,0,0,0,0,0,0,0,0,0,-4,-2,4,2,0,0,0,0,-3,3,-3,3,0,0,0,0,-2,-1,-2,-1,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,-4,-4,4,0,0,0,0,0,0,0,0,0,0,0,0,2,2,-2,-2,0,0,0,0,2,-2,2,-2,0,0,0,0,1,1,1,1,0,0,0,0 }, + {-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 9,-9,0,0,-9,9,0,0,6,3,0,0,-6,-3,0,0,0,0,0,0,0,0,0,0,6,-6,0,0,3,-3,0,0,0,0,0,0,0,0,0,0,4,2,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { -6,6,0,0,6,-6,0,0,-3,-3,0,0,3,3,0,0,0,0,0,0,0,0,0,0,-4,4,0,0,-2,2,0,0,0,0,0,0,0,0,0,0,-2,-2,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,-9,0,0,-9,9,0,0,0,0,0,0,0,0,0,0,6,3,0,0,-6,-3,0,0,0,0,0,0,0,0,0,0,6,-6,0,0,3,-3,0,0,4,2,0,0,2,1,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,0,0,6,-6,0,0,0,0,0,0,0,0,0,0,-3,-3,0,0,3,3,0,0,0,0,0,0,0,0,0,0,-4,4,0,0,-2,2,0,0,-2,-2,0,0,-1,-1,0,0 }, + { 9,0,-9,0,-9,0,9,0,0,0,0,0,0,0,0,0,6,0,3,0,-6,0,-3,0,6,0,-6,0,3,0,-3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,2,0,2,0,1,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,9,0,-9,0,-9,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,3,0,-6,0,-3,0,6,0,-6,0,3,0,-3,0,0,0,0,0,0,0,0,0,4,0,2,0,2,0,1,0 }, + { -27,27,27,-27,27,-27,-27,27,-18,-9,18,9,18,9,-18,-9,-18,18,-9,9,18,-18,9,-9,-18,18,18,-18,-9,9,9,-9,-12,-6,-6,-3,12,6,6,3,-12,-6,12,6,-6,-3,6,3,-12,12,-6,6,-6,6,-3,3,-8,-4,-4,-2,-4,-2,-2,-1 }, + { 18,-18,-18,18,-18,18,18,-18,9,9,-9,-9,-9,-9,9,9,12,-12,6,-6,-12,12,-6,6,12,-12,-12,12,6,-6,-6,6,6,6,3,3,-6,-6,-3,-3,6,6,-6,-6,3,3,-3,-3,8,-8,4,-4,4,-4,2,-2,4,4,2,2,2,2,1,1 }, + { -6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,-3,0,-3,0,3,0,3,0,-4,0,4,0,-2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-2,0,-1,0,-1,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,-6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,-3,0,3,0,3,0,-4,0,4,0,-2,0,2,0,0,0,0,0,0,0,0,0,-2,0,-2,0,-1,0,-1,0 }, + { 18,-18,-18,18,-18,18,18,-18,12,6,-12,-6,-12,-6,12,6,9,-9,9,-9,-9,9,-9,9,12,-12,-12,12,6,-6,-6,6,6,3,6,3,-6,-3,-6,-3,8,4,-8,-4,4,2,-4,-2,6,-6,6,-6,3,-3,3,-3,4,2,4,2,2,1,2,1 }, + { -12,12,12,-12,12,-12,-12,12,-6,-6,6,6,6,6,-6,-6,-6,6,-6,6,6,-6,6,-6,-8,8,8,-8,-4,4,4,-4,-3,-3,-3,-3,3,3,3,3,-4,-4,4,4,-2,-2,2,2,-4,4,-4,4,-2,2,-2,2,-2,-2,-2,-2,-1,-1,-1,-1 }, + { 2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { -6,6,0,0,6,-6,0,0,-4,-2,0,0,4,2,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,-3,3,0,0,0,0,0,0,0,0,0,0,-2,-1,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 4,-4,0,0,-4,4,0,0,2,2,0,0,-2,-2,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,2,-2,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,0,0,6,-6,0,0,0,0,0,0,0,0,0,0,-4,-2,0,0,4,2,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,-3,3,0,0,-2,-1,0,0,-2,-1,0,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,-4,0,0,-4,4,0,0,0,0,0,0,0,0,0,0,2,2,0,0,-2,-2,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,2,-2,0,0,1,1,0,0,1,1,0,0 }, + { -6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,-4,0,-2,0,4,0,2,0,-3,0,3,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,-2,0,-1,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,-6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-4,0,-2,0,4,0,2,0,-3,0,3,0,-3,0,3,0,0,0,0,0,0,0,0,0,-2,0,-1,0,-2,0,-1,0 }, + { 18,-18,-18,18,-18,18,18,-18,12,6,-12,-6,-12,-6,12,6,12,-12,6,-6,-12,12,-6,6,9,-9,-9,9,9,-9,-9,9,8,4,4,2,-8,-4,-4,-2,6,3,-6,-3,6,3,-6,-3,6,-6,3,-3,6,-6,3,-3,4,2,2,1,4,2,2,1 }, + { -12,12,12,-12,12,-12,-12,12,-6,-6,6,6,6,6,-6,-6,-8,8,-4,4,8,-8,4,-4,-6,6,6,-6,-6,6,6,-6,-4,-4,-2,-2,4,4,2,2,-3,-3,3,3,-3,-3,3,3,-4,4,-2,2,-4,4,-2,2,-2,-2,-1,-1,-2,-2,-1,-1 }, + { 4,0,-4,0,-4,0,4,0,0,0,0,0,0,0,0,0,2,0,2,0,-2,0,-2,0,2,0,-2,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0 }, + { 0,0,0,0,0,0,0,0,4,0,-4,0,-4,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,-2,0,-2,0,2,0,-2,0,2,0,-2,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0 }, + { -12,12,12,-12,12,-12,-12,12,-8,-4,8,4,8,4,-8,-4,-6,6,-6,6,6,-6,6,-6,-6,6,6,-6,-6,6,6,-6,-4,-2,-4,-2,4,2,4,2,-4,-2,4,2,-4,-2,4,2,-3,3,-3,3,-3,3,-3,3,-2,-1,-2,-1,-2,-1,-2,-1 }, + { 8,-8,-8,8,-8,8,8,-8,4,4,-4,-4,-4,-4,4,4,4,-4,4,-4,-4,4,-4,4,4,-4,-4,4,4,-4,-4,4,2,2,2,2,-2,-2,-2,-2,2,2,-2,-2,2,2,-2,-2,2,-2,2,-2,2,-2,2,-2,1,1,1,1,1,1,1,1 } + }; + + /** Samples x-coordinates */ + private final double[] xval; + /** Samples y-coordinates */ + private final double[] yval; + /** Samples z-coordinates */ + private final double[] zval; + /** Set of cubic splines pacthing the whole data grid */ + private final TricubicSplineFunction[][][] splines; + + /** + * @param x Sample values of the x-coordinate, in increasing order. + * @param y Sample values of the y-coordinate, in increasing order. + * @param z Sample values of the y-coordinate, in increasing order. + * @param f Values of the function on every grid point. + * @param dFdX Values of the partial derivative of function with respect + * to x on every grid point. + * @param dFdY Values of the partial derivative of function with respect + * to y on every grid point. + * @param dFdZ Values of the partial derivative of function with respect + * to z on every grid point. + * @param d2FdXdY Values of the cross partial derivative of function on + * every grid point. + * @param d2FdXdZ Values of the cross partial derivative of function on + * every grid point. + * @param d2FdYdZ Values of the cross partial derivative of function on + * every grid point. + * @param d3FdXdYdZ Values of the cross partial derivative of function on + * every grid point. + * @throws NoDataException if any of the arrays has zero length. + * @throws DimensionMismatchException if the various arrays do not contain + * the expected number of elements. + * @throws IllegalArgumentException if {@code x}, {@code y} or {@code z} + * are not strictly increasing. + */ + public TricubicSplineInterpolatingFunction(double[] x, + double[] y, + double[] z, + double[][][] f, + double[][][] dFdX, + double[][][] dFdY, + double[][][] dFdZ, + double[][][] d2FdXdY, + double[][][] d2FdXdZ, + double[][][] d2FdYdZ, + double[][][] d3FdXdYdZ) { + final int xLen = x.length; + final int yLen = y.length; + final int zLen = z.length; + + if (xLen == 0 || yLen == 0 || z.length == 0 || f.length == 0 || f[0].length == 0) { + throw new NoDataException(); + } + if (xLen != f.length) { + throw new DimensionMismatchException(xLen, f.length); + } + if (xLen != dFdX.length) { + throw new DimensionMismatchException(xLen, dFdX.length); + } + if (xLen != dFdY.length) { + throw new DimensionMismatchException(xLen, dFdY.length); + } + if (xLen != dFdZ.length) { + throw new DimensionMismatchException(xLen, dFdZ.length); + } + if (xLen != d2FdXdY.length) { + throw new DimensionMismatchException(xLen, d2FdXdY.length); + } + if (xLen != d2FdXdZ.length) { + throw new DimensionMismatchException(xLen, d2FdXdZ.length); + } + if (xLen != d2FdYdZ.length) { + throw new DimensionMismatchException(xLen, d2FdYdZ.length); + } + if (xLen != d3FdXdYdZ.length) { + throw new DimensionMismatchException(xLen, d3FdXdYdZ.length); + } + + MathUtils.checkOrder(x); + MathUtils.checkOrder(y); + MathUtils.checkOrder(z); + + xval = x.clone(); + yval = y.clone(); + zval = z.clone(); + + final int lastI = xLen - 1; + final int lastJ = yLen - 1; + final int lastK = zLen - 1; + splines = new TricubicSplineFunction[lastI][lastJ][lastK]; + + for (int i = 0; i < lastI; i++) { + if (f[i].length != yLen) { + throw new DimensionMismatchException(f[i].length, yLen); + } + if (dFdX[i].length != yLen) { + throw new DimensionMismatchException(dFdX[i].length, yLen); + } + if (dFdY[i].length != yLen) { + throw new DimensionMismatchException(dFdY[i].length, yLen); + } + if (dFdZ[i].length != yLen) { + throw new DimensionMismatchException(dFdZ[i].length, yLen); + } + if (d2FdXdY[i].length != yLen) { + throw new DimensionMismatchException(d2FdXdY[i].length, yLen); + } + if (d2FdXdZ[i].length != yLen) { + throw new DimensionMismatchException(d2FdXdZ[i].length, yLen); + } + if (d2FdYdZ[i].length != yLen) { + throw new DimensionMismatchException(d2FdYdZ[i].length, yLen); + } + if (d3FdXdYdZ[i].length != yLen) { + throw new DimensionMismatchException(d3FdXdYdZ[i].length, yLen); + } + + final int ip1 = i + 1; + for (int j = 0; j < lastJ; j++) { + if (f[i][j].length != zLen) { + throw new DimensionMismatchException(f[i][j].length, zLen); + } + if (dFdX[i][j].length != zLen) { + throw new DimensionMismatchException(dFdX[i][j].length, zLen); + } + if (dFdY[i][j].length != zLen) { + throw new DimensionMismatchException(dFdY[i][j].length, zLen); + } + if (dFdZ[i][j].length != zLen) { + throw new DimensionMismatchException(dFdZ[i][j].length, zLen); + } + if (d2FdXdY[i][j].length != zLen) { + throw new DimensionMismatchException(d2FdXdY[i][j].length, zLen); + } + if (d2FdXdZ[i][j].length != zLen) { + throw new DimensionMismatchException(d2FdXdZ[i][j].length, zLen); + } + if (d2FdYdZ[i][j].length != zLen) { + throw new DimensionMismatchException(d2FdYdZ[i][j].length, zLen); + } + if (d3FdXdYdZ[i][j].length != zLen) { + throw new DimensionMismatchException(d3FdXdYdZ[i][j].length, zLen); + } + + final int jp1 = j + 1; + for (int k = 0; k < lastK; k++) { + final int kp1 = k + 1; + + final double[] beta = new double[] { + f[i][j][k], f[ip1][j][k], + f[i][jp1][k], f[ip1][jp1][k], + f[i][j][kp1], f[ip1][j][kp1], + f[i][jp1][kp1], f[ip1][jp1][kp1], + + dFdX[i][j][k], dFdX[ip1][j][k], + dFdX[i][jp1][k], dFdX[ip1][jp1][k], + dFdX[i][j][kp1], dFdX[ip1][j][kp1], + dFdX[i][jp1][kp1], dFdX[ip1][jp1][kp1], + + dFdY[i][j][k], dFdY[ip1][j][k], + dFdY[i][jp1][k], dFdY[ip1][jp1][k], + dFdY[i][j][kp1], dFdY[ip1][j][kp1], + dFdY[i][jp1][kp1], dFdY[ip1][jp1][kp1], + + dFdZ[i][j][k], dFdZ[ip1][j][k], + dFdZ[i][jp1][k], dFdZ[ip1][jp1][k], + dFdZ[i][j][kp1], dFdZ[ip1][j][kp1], + dFdZ[i][jp1][kp1], dFdZ[ip1][jp1][kp1], + + d2FdXdY[i][j][k], d2FdXdY[ip1][j][k], + d2FdXdY[i][jp1][k], d2FdXdY[ip1][jp1][k], + d2FdXdY[i][j][kp1], d2FdXdY[ip1][j][kp1], + d2FdXdY[i][jp1][kp1], d2FdXdY[ip1][jp1][kp1], + + d2FdXdZ[i][j][k], d2FdXdZ[ip1][j][k], + d2FdXdZ[i][jp1][k], d2FdXdZ[ip1][jp1][k], + d2FdXdZ[i][j][kp1], d2FdXdZ[ip1][j][kp1], + d2FdXdZ[i][jp1][kp1], d2FdXdZ[ip1][jp1][kp1], + + d2FdYdZ[i][j][k], d2FdYdZ[ip1][j][k], + d2FdYdZ[i][jp1][k], d2FdYdZ[ip1][jp1][k], + d2FdYdZ[i][j][kp1], d2FdYdZ[ip1][j][kp1], + d2FdYdZ[i][jp1][kp1], d2FdYdZ[ip1][jp1][kp1], + + d3FdXdYdZ[i][j][k], d3FdXdYdZ[ip1][j][k], + d3FdXdYdZ[i][jp1][k], d3FdXdYdZ[ip1][jp1][k], + d3FdXdYdZ[i][j][kp1], d3FdXdYdZ[ip1][j][kp1], + d3FdXdYdZ[i][jp1][kp1], d3FdXdYdZ[ip1][jp1][kp1], + }; + + splines[i][j][k] = new TricubicSplineFunction(computeSplineCoefficients(beta)); + } + } + } + } + + /** + * {@inheritDoc} + */ + public double value(double x, double y, double z) { + final int i = searchIndex(x, xval); + if (i == -1) { + throw new OutOfRangeException(x, xval[0], xval[xval.length - 1]); + } + final int j = searchIndex(y, yval); + if (j == -1) { + throw new OutOfRangeException(y, yval[0], yval[yval.length - 1]); + } + final int k = searchIndex(z, zval); + if (k == -1) { + throw new OutOfRangeException(z, zval[0], zval[zval.length - 1]); + } + + final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]); + final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]); + final double zN = (z - zval[k]) / (zval[k + 1] - zval[k]); + + return splines[i][j][k].value(xN, yN, zN); + } + + /** + * @param c Coordinate. + * @param val Coordinate samples. + * @return the index in {@code val} corresponding to the interval + * containing {@code c}, or {@code -1} if {@code c} is out of the + * range defined by the end values of {@code val}. + */ + private int searchIndex(double c, double[] val) { + if (c < val[0]) { + return -1; + } + + final int max = val.length; + for (int i = 1; i < max; i++) { + if (c <= val[i]) { + return i - 1; + } + } + + return -1; + } + + /** + * Compute the spline coefficients from the list of function values and + * function partial derivatives values at the four corners of a grid + * element. They must be specified in the following order: + * <ul> + * <li>f(0,0,0)</li> + * <li>f(1,0,0)</li> + * <li>f(0,1,0)</li> + * <li>f(1,1,0)</li> + * <li>f(0,0,1)</li> + * <li>f(1,0,1)</li> + * <li>f(0,1,1)</li> + * <li>f(1,1,1)</li> + * + * <li>f<sub>x</sub>(0,0,0)</li> + * <li>... <em>(same order as above)</em></li> + * <li>f<sub>x</sub>(1,1,1)</li> + * + * <li>f<sub>y</sub>(0,0,0)</li> + * <li>... <em>(same order as above)</em></li> + * <li>f<sub>y</sub>(1,1,1)</li> + * + * <li>f<sub>z</sub>(0,0,0)</li> + * <li>... <em>(same order as above)</em></li> + * <li>f<sub>z</sub>(1,1,1)</li> + * + * <li>f<sub>xy</sub>(0,0,0)</li> + * <li>... <em>(same order as above)</em></li> + * <li>f<sub>xy</sub>(1,1,1)</li> + * + * <li>f<sub>xz</sub>(0,0,0)</li> + * <li>... <em>(same order as above)</em></li> + * <li>f<sub>xz</sub>(1,1,1)</li> + * + * <li>f<sub>yz</sub>(0,0,0)</li> + * <li>... <em>(same order as above)</em></li> + * <li>f<sub>yz</sub>(1,1,1)</li> + * + * <li>f<sub>xyz</sub>(0,0,0)</li> + * <li>... <em>(same order as above)</em></li> + * <li>f<sub>xyz</sub>(1,1,1)</li> + * </ul> + * where the subscripts indicate the partial derivative with respect to + * the corresponding variable(s). + * + * @param beta List of function values and function partial derivatives + * values. + * @return the spline coefficients. + */ + private double[] computeSplineCoefficients(double[] beta) { + final int sz = 64; + final double[] a = new double[sz]; + + for (int i = 0; i < sz; i++) { + double result = 0; + final double[] row = AINV[i]; + for (int j = 0; j < sz; j++) { + result += row[j] * beta[j]; + } + a[i] = result; + } + + return a; + } +} + +/** + * 3D-spline function. + * + * @version $Revision$ $Date$ + */ +class TricubicSplineFunction + implements TrivariateRealFunction { + /** Number of points. */ + private static final short N = 4; + /** Coefficients */ + private final double[][][] a = new double[N][N][N]; + + /** + * @param aV List of spline coefficients. + */ + public TricubicSplineFunction(double[] aV) { + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + for (int k = 0; k < N; k++) { + a[i][j][k] = aV[i + N * (j + N * k)]; + } + } + } + } + + /** + * @param x x-coordinate of the interpolation point. + * @param y y-coordinate of the interpolation point. + * @param z z-coordinate of the interpolation point. + * @return the interpolated value. + */ + public double value(double x, double y, double z) { + if (x < 0 || x > 1) { + throw new OutOfRangeException(x, 0, 1); + } + if (y < 0 || y > 1) { + throw new OutOfRangeException(y, 0, 1); + } + if (z < 0 || z > 1) { + throw new OutOfRangeException(z, 0, 1); + } + + final double x2 = x * x; + final double x3 = x2 * x; + final double[] pX = { 1, x, x2, x3 }; + + final double y2 = y * y; + final double y3 = y2 * y; + final double[] pY = { 1, y, y2, y3 }; + + final double z2 = z * z; + final double z3 = z2 * z; + final double[] pZ = { 1, z, z2, z3 }; + + double result = 0; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + for (int k = 0; k < N; k++) { + result += a[i][j][k] * pX[i] * pY[j] * pZ[k]; + } + } + } + + return result; + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolator.java new file mode 100644 index 0000000..dac7f26 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolator.java @@ -0,0 +1,198 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.exception.DimensionMismatchException; +import org.apache.commons.math.exception.NoDataException; +import org.apache.commons.math.MathException; +import org.apache.commons.math.util.MathUtils; + +/** + * Generates a tricubic interpolating function. + * + * @version $Revision$ $Date$ + * @since 2.2 + */ +public class TricubicSplineInterpolator + implements TrivariateRealGridInterpolator { + /** + * {@inheritDoc} + */ + public TricubicSplineInterpolatingFunction interpolate(final double[] xval, + final double[] yval, + final double[] zval, + final double[][][] fval) + throws MathException { + if (xval.length == 0 || yval.length == 0 || zval.length == 0 || fval.length == 0) { + throw new NoDataException(); + } + if (xval.length != fval.length) { + throw new DimensionMismatchException(xval.length, fval.length); + } + + MathUtils.checkOrder(xval); + MathUtils.checkOrder(yval); + MathUtils.checkOrder(zval); + + final int xLen = xval.length; + final int yLen = yval.length; + final int zLen = zval.length; + + // Samples, re-ordered as (z, x, y) and (y, z, x) tuplets + // fvalXY[k][i][j] = f(xval[i], yval[j], zval[k]) + // fvalZX[j][k][i] = f(xval[i], yval[j], zval[k]) + final double[][][] fvalXY = new double[zLen][xLen][yLen]; + final double[][][] fvalZX = new double[yLen][zLen][xLen]; + for (int i = 0; i < xLen; i++) { + if (fval[i].length != yLen) { + throw new DimensionMismatchException(fval[i].length, yLen); + } + + for (int j = 0; j < yLen; j++) { + if (fval[i][j].length != zLen) { + throw new DimensionMismatchException(fval[i][j].length, zLen); + } + + for (int k = 0; k < zLen; k++) { + final double v = fval[i][j][k]; + fvalXY[k][i][j] = v; + fvalZX[j][k][i] = v; + } + } + } + + final BicubicSplineInterpolator bsi = new BicubicSplineInterpolator(); + + // For each line x[i] (0 <= i < xLen), construct a 2D spline in y and z + final BicubicSplineInterpolatingFunction[] xSplineYZ + = new BicubicSplineInterpolatingFunction[xLen]; + for (int i = 0; i < xLen; i++) { + xSplineYZ[i] = bsi.interpolate(yval, zval, fval[i]); + } + + // For each line y[j] (0 <= j < yLen), construct a 2D spline in z and x + final BicubicSplineInterpolatingFunction[] ySplineZX + = new BicubicSplineInterpolatingFunction[yLen]; + for (int j = 0; j < yLen; j++) { + ySplineZX[j] = bsi.interpolate(zval, xval, fvalZX[j]); + } + + // For each line z[k] (0 <= k < zLen), construct a 2D spline in x and y + final BicubicSplineInterpolatingFunction[] zSplineXY + = new BicubicSplineInterpolatingFunction[zLen]; + for (int k = 0; k < zLen; k++) { + zSplineXY[k] = bsi.interpolate(xval, yval, fvalXY[k]); + } + + // Partial derivatives wrt x and wrt y + final double[][][] dFdX = new double[xLen][yLen][zLen]; + final double[][][] dFdY = new double[xLen][yLen][zLen]; + final double[][][] d2FdXdY = new double[xLen][yLen][zLen]; + for (int k = 0; k < zLen; k++) { + final BicubicSplineInterpolatingFunction f = zSplineXY[k]; + for (int i = 0; i < xLen; i++) { + final double x = xval[i]; + for (int j = 0; j < yLen; j++) { + final double y = yval[j]; + dFdX[i][j][k] = f.partialDerivativeX(x, y); + dFdY[i][j][k] = f.partialDerivativeY(x, y); + d2FdXdY[i][j][k] = f.partialDerivativeXY(x, y); + } + } + } + + // Partial derivatives wrt y and wrt z + final double[][][] dFdZ = new double[xLen][yLen][zLen]; + final double[][][] d2FdYdZ = new double[xLen][yLen][zLen]; + for (int i = 0; i < xLen; i++) { + final BicubicSplineInterpolatingFunction f = xSplineYZ[i]; + for (int j = 0; j < yLen; j++) { + final double y = yval[j]; + for (int k = 0; k < zLen; k++) { + final double z = zval[k]; + dFdZ[i][j][k] = f.partialDerivativeY(y, z); + d2FdYdZ[i][j][k] = f.partialDerivativeXY(y, z); + } + } + } + + // Partial derivatives wrt x and wrt z + final double[][][] d2FdZdX = new double[xLen][yLen][zLen]; + for (int j = 0; j < yLen; j++) { + final BicubicSplineInterpolatingFunction f = ySplineZX[j]; + for (int k = 0; k < zLen; k++) { + final double z = zval[k]; + for (int i = 0; i < xLen; i++) { + final double x = xval[i]; + d2FdZdX[i][j][k] = f.partialDerivativeXY(z, x); + } + } + } + + // Third partial cross-derivatives + final double[][][] d3FdXdYdZ = new double[xLen][yLen][zLen]; + for (int i = 0; i < xLen ; i++) { + final int nI = nextIndex(i, xLen); + final int pI = previousIndex(i); + for (int j = 0; j < yLen; j++) { + final int nJ = nextIndex(j, yLen); + final int pJ = previousIndex(j); + for (int k = 0; k < zLen; k++) { + final int nK = nextIndex(k, zLen); + final int pK = previousIndex(k); + + // XXX Not sure about this formula + d3FdXdYdZ[i][j][k] = (fval[nI][nJ][nK] - fval[nI][pJ][nK] - + fval[pI][nJ][nK] + fval[pI][pJ][nK] - + fval[nI][nJ][pK] + fval[nI][pJ][pK] + + fval[pI][nJ][pK] - fval[pI][pJ][pK]) / + ((xval[nI] - xval[pI]) * (yval[nJ] - yval[pJ]) * (zval[nK] - zval[pK])) ; + } + } + } + + // Create the interpolating splines + return new TricubicSplineInterpolatingFunction(xval, yval, zval, fval, + dFdX, dFdY, dFdZ, + d2FdXdY, d2FdZdX, d2FdYdZ, + d3FdXdYdZ); + } + + /** + * Compute the next index of an array, clipping if necessary. + * It is assumed (but not checked) that {@code i} is larger than or equal to 0}. + * + * @param i Index + * @param max Upper limit of the array + * @return the next index + */ + private int nextIndex(int i, int max) { + final int index = i + 1; + return index < max ? index : index - 1; + } + /** + * Compute the previous index of an array, clipping if necessary. + * It is assumed (but not checked) that {@code i} is smaller than the size of the array. + * + * @param i Index + * @return the previous index + */ + private int previousIndex(int i) { + final int index = i - 1; + return index >= 0 ? index : 0; + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/TrivariateRealGridInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/TrivariateRealGridInterpolator.java new file mode 100644 index 0000000..c42d7aa --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/TrivariateRealGridInterpolator.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.MathException; +import org.apache.commons.math.analysis.TrivariateRealFunction; + +/** + * Interface representing a trivariate real interpolating function where the + * sample points must be specified on a regular grid. + * + * @version $Revision$ $Date$ + * @since 2.2 + */ +public interface TrivariateRealGridInterpolator { + /** + * Computes an interpolating function for the data set. + * + * @param xval All the x-coordinates of the interpolation points, sorted + * in increasing order. + * @param yval All the y-coordinates of the interpolation points, sorted + * in increasing order. + * @param zval All the z-coordinates of the interpolation points, sorted + * in increasing order. + * @param fval the values of the interpolation points on all the grid knots: + * {@code fval[i][j][k] = f(xval[i], yval[j], zval[k])}. + * @return a function that interpolates the data set. + * @throws org.apache.commons.math.exception.NoDataException if any of the arrays has zero length. + * @throws org.apache.commons.math.exception.DimensionMismatchException if the array lengths are inconsistent. + * @throws MathException if arguments violate assumptions made by the + * interpolation algorithm. + */ + TrivariateRealFunction interpolate(double[] xval, double[] yval, double[] zval, double[][][] fval) + throws MathException; +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/UnivariateRealInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/UnivariateRealInterpolator.java new file mode 100644 index 0000000..09f5487 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/UnivariateRealInterpolator.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.interpolation; + +import org.apache.commons.math.MathException; +import org.apache.commons.math.analysis.UnivariateRealFunction; + +/** + * Interface representing a univariate real interpolating function. + * + * @version $Revision: 821626 $ $Date: 2009-10-04 23:57:30 +0200 (dim. 04 oct. 2009) $ + */ +public interface UnivariateRealInterpolator { + + /** + * Computes an interpolating function for the data set. + * @param xval the arguments for the interpolation points + * @param yval the values for the interpolation points + * @return a function which interpolates the data set + * @throws MathException if arguments violate assumptions made by the + * interpolation algorithm + */ + UnivariateRealFunction interpolate(double xval[], double yval[]) + throws MathException; +} diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/package.html b/src/main/java/org/apache/commons/math/analysis/interpolation/package.html new file mode 100644 index 0000000..136c576 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/interpolation/package.html @@ -0,0 +1,22 @@ +<html> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ --> + <body> + Univariate real functions interpolation algorithms. + </body> +</html> diff --git a/src/main/java/org/apache/commons/math/analysis/package.html b/src/main/java/org/apache/commons/math/analysis/package.html new file mode 100644 index 0000000..63b64a8 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/package.html @@ -0,0 +1,33 @@ +<html> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ --> + <body> + <p> + Parent package for common numerical analysis procedures, including root finding, + function interpolation and integration. Note that the optimization (i.e. minimization + and maximization) is a huge separate top package, despite it also operate on functions + as defined by this top-level package. + </p> + <p> + Functions interfaces are intended to be implemented by user code to represent their + domain problems. The algorithms provided by the library will then operate on these + function to find their roots, or integrate them, or ... Functions can be multivariate + or univariate, real vectorial or matrix valued, and they can be differentiable or not. + </p> + </body> +</html> diff --git a/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunction.java b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunction.java new file mode 100644 index 0000000..ec528b4 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunction.java @@ -0,0 +1,350 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.polynomials; + +import java.io.Serializable; +import java.util.Arrays; + +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.exception.NoDataException; +import org.apache.commons.math.analysis.DifferentiableUnivariateRealFunction; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.util.FastMath; + +/** + * Immutable representation of a real polynomial function with real coefficients. + * <p> + * <a href="http://mathworld.wolfram.com/HornersMethod.html">Horner's Method</a> + * is used to evaluate the function.</p> + * + * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 déc. 2010) $ + */ +public class PolynomialFunction implements DifferentiableUnivariateRealFunction, Serializable { + + /** + * Serialization identifier + */ + private static final long serialVersionUID = -7726511984200295583L; + + /** + * The coefficients of the polynomial, ordered by degree -- i.e., + * coefficients[0] is the constant term and coefficients[n] is the + * coefficient of x^n where n is the degree of the polynomial. + */ + private final double coefficients[]; + + /** + * Construct a polynomial with the given coefficients. The first element + * of the coefficients array is the constant term. Higher degree + * coefficients follow in sequence. The degree of the resulting polynomial + * is the index of the last non-null element of the array, or 0 if all elements + * are null. + * <p> + * The constructor makes a copy of the input array and assigns the copy to + * the coefficients property.</p> + * + * @param c polynomial coefficients + * @throws NullPointerException if c is null + * @throws NoDataException if c is empty + */ + public PolynomialFunction(double c[]) { + super(); + int n = c.length; + if (n == 0) { + throw new NoDataException(LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY); + } + while ((n > 1) && (c[n - 1] == 0)) { + --n; + } + this.coefficients = new double[n]; + System.arraycopy(c, 0, this.coefficients, 0, n); + } + + /** + * Compute the value of the function for the given argument. + * <p> + * The value returned is <br> + * <code>coefficients[n] * x^n + ... + coefficients[1] * x + coefficients[0]</code> + * </p> + * + * @param x the argument for which the function value should be computed + * @return the value of the polynomial at the given point + * @see UnivariateRealFunction#value(double) + */ + public double value(double x) { + return evaluate(coefficients, x); + } + + + /** + * Returns the degree of the polynomial + * + * @return the degree of the polynomial + */ + public int degree() { + return coefficients.length - 1; + } + + /** + * Returns a copy of the coefficients array. + * <p> + * Changes made to the returned copy will not affect the coefficients of + * the polynomial.</p> + * + * @return a fresh copy of the coefficients array + */ + public double[] getCoefficients() { + return coefficients.clone(); + } + + /** + * Uses Horner's Method to evaluate the polynomial with the given coefficients at + * the argument. + * + * @param coefficients the coefficients of the polynomial to evaluate + * @param argument the input value + * @return the value of the polynomial + * @throws NoDataException if coefficients is empty + * @throws NullPointerException if coefficients is null + */ + protected static double evaluate(double[] coefficients, double argument) { + int n = coefficients.length; + if (n == 0) { + throw new NoDataException(LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY); + } + double result = coefficients[n - 1]; + for (int j = n -2; j >=0; j--) { + result = argument * result + coefficients[j]; + } + return result; + } + + /** + * Add a polynomial to the instance. + * @param p polynomial to add + * @return a new polynomial which is the sum of the instance and p + */ + public PolynomialFunction add(final PolynomialFunction p) { + + // identify the lowest degree polynomial + final int lowLength = FastMath.min(coefficients.length, p.coefficients.length); + final int highLength = FastMath.max(coefficients.length, p.coefficients.length); + + // build the coefficients array + double[] newCoefficients = new double[highLength]; + for (int i = 0; i < lowLength; ++i) { + newCoefficients[i] = coefficients[i] + p.coefficients[i]; + } + System.arraycopy((coefficients.length < p.coefficients.length) ? + p.coefficients : coefficients, + lowLength, + newCoefficients, lowLength, + highLength - lowLength); + + return new PolynomialFunction(newCoefficients); + + } + + /** + * Subtract a polynomial from the instance. + * @param p polynomial to subtract + * @return a new polynomial which is the difference the instance minus p + */ + public PolynomialFunction subtract(final PolynomialFunction p) { + + // identify the lowest degree polynomial + int lowLength = FastMath.min(coefficients.length, p.coefficients.length); + int highLength = FastMath.max(coefficients.length, p.coefficients.length); + + // build the coefficients array + double[] newCoefficients = new double[highLength]; + for (int i = 0; i < lowLength; ++i) { + newCoefficients[i] = coefficients[i] - p.coefficients[i]; + } + if (coefficients.length < p.coefficients.length) { + for (int i = lowLength; i < highLength; ++i) { + newCoefficients[i] = -p.coefficients[i]; + } + } else { + System.arraycopy(coefficients, lowLength, newCoefficients, lowLength, + highLength - lowLength); + } + + return new PolynomialFunction(newCoefficients); + + } + + /** + * Negate the instance. + * @return a new polynomial + */ + public PolynomialFunction negate() { + double[] newCoefficients = new double[coefficients.length]; + for (int i = 0; i < coefficients.length; ++i) { + newCoefficients[i] = -coefficients[i]; + } + return new PolynomialFunction(newCoefficients); + } + + /** + * Multiply the instance by a polynomial. + * @param p polynomial to multiply by + * @return a new polynomial + */ + public PolynomialFunction multiply(final PolynomialFunction p) { + + double[] newCoefficients = new double[coefficients.length + p.coefficients.length - 1]; + + for (int i = 0; i < newCoefficients.length; ++i) { + newCoefficients[i] = 0.0; + for (int j = FastMath.max(0, i + 1 - p.coefficients.length); + j < FastMath.min(coefficients.length, i + 1); + ++j) { + newCoefficients[i] += coefficients[j] * p.coefficients[i-j]; + } + } + + return new PolynomialFunction(newCoefficients); + + } + + /** + * Returns the coefficients of the derivative of the polynomial with the given coefficients. + * + * @param coefficients the coefficients of the polynomial to differentiate + * @return the coefficients of the derivative or null if coefficients has length 1. + * @throws NoDataException if coefficients is empty + * @throws NullPointerException if coefficients is null + */ + protected static double[] differentiate(double[] coefficients) { + int n = coefficients.length; + if (n == 0) { + throw new NoDataException(LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY); + } + if (n == 1) { + return new double[]{0}; + } + double[] result = new double[n - 1]; + for (int i = n - 1; i > 0; i--) { + result[i - 1] = i * coefficients[i]; + } + return result; + } + + /** + * Returns the derivative as a PolynomialRealFunction + * + * @return the derivative polynomial + */ + public PolynomialFunction polynomialDerivative() { + return new PolynomialFunction(differentiate(coefficients)); + } + + /** + * Returns the derivative as a UnivariateRealFunction + * + * @return the derivative function + */ + public UnivariateRealFunction derivative() { + return polynomialDerivative(); + } + + /** Returns a string representation of the polynomial. + + * <p>The representation is user oriented. Terms are displayed lowest + * degrees first. The multiplications signs, coefficients equals to + * one and null terms are not displayed (except if the polynomial is 0, + * in which case the 0 constant term is displayed). Addition of terms + * with negative coefficients are replaced by subtraction of terms + * with positive coefficients except for the first displayed term + * (i.e. we display <code>-3</code> for a constant negative polynomial, + * but <code>1 - 3 x + x^2</code> if the negative coefficient is not + * the first one displayed).</p> + + * @return a string representation of the polynomial + + */ + @Override + public String toString() { + + StringBuilder s = new StringBuilder(); + if (coefficients[0] == 0.0) { + if (coefficients.length == 1) { + return "0"; + } + } else { + s.append(Double.toString(coefficients[0])); + } + + for (int i = 1; i < coefficients.length; ++i) { + + if (coefficients[i] != 0) { + + if (s.length() > 0) { + if (coefficients[i] < 0) { + s.append(" - "); + } else { + s.append(" + "); + } + } else { + if (coefficients[i] < 0) { + s.append("-"); + } + } + + double absAi = FastMath.abs(coefficients[i]); + if ((absAi - 1) != 0) { + s.append(Double.toString(absAi)); + s.append(' '); + } + + s.append("x"); + if (i > 1) { + s.append('^'); + s.append(Integer.toString(i)); + } + } + + } + + return s.toString(); + + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(coefficients); + return result; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof PolynomialFunction)) + return false; + PolynomialFunction other = (PolynomialFunction) obj; + if (!Arrays.equals(coefficients, other.coefficients)) + return false; + return true; + } + +} diff --git a/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionLagrangeForm.java b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionLagrangeForm.java new file mode 100644 index 0000000..b40bf60 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionLagrangeForm.java @@ -0,0 +1,305 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.polynomials; + +import org.apache.commons.math.DuplicateSampleAbscissaException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.util.FastMath; + +/** + * Implements the representation of a real polynomial function in + * <a href="http://mathworld.wolfram.com/LagrangeInterpolatingPolynomial.html"> + * Lagrange Form</a>. For reference, see <b>Introduction to Numerical + * Analysis</b>, ISBN 038795452X, chapter 2. + * <p> + * The approximated function should be smooth enough for Lagrange polynomial + * to work well. Otherwise, consider using splines instead.</p> + * + * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $ + * @since 1.2 + */ +public class PolynomialFunctionLagrangeForm implements UnivariateRealFunction { + + /** + * The coefficients of the polynomial, ordered by degree -- i.e. + * coefficients[0] is the constant term and coefficients[n] is the + * coefficient of x^n where n is the degree of the polynomial. + */ + private double coefficients[]; + + /** + * Interpolating points (abscissas). + */ + private final double x[]; + + /** + * Function values at interpolating points. + */ + private final double y[]; + + /** + * Whether the polynomial coefficients are available. + */ + private boolean coefficientsComputed; + + /** + * Construct a Lagrange polynomial with the given abscissas and function + * values. The order of interpolating points are not important. + * <p> + * The constructor makes copy of the input arrays and assigns them.</p> + * + * @param x interpolating points + * @param y function values at interpolating points + * @throws IllegalArgumentException if input arrays are not valid + */ + public PolynomialFunctionLagrangeForm(double x[], double y[]) + throws IllegalArgumentException { + + verifyInterpolationArray(x, y); + this.x = new double[x.length]; + this.y = new double[y.length]; + System.arraycopy(x, 0, this.x, 0, x.length); + System.arraycopy(y, 0, this.y, 0, y.length); + coefficientsComputed = false; + } + + /** {@inheritDoc} */ + public double value(double z) throws FunctionEvaluationException { + try { + return evaluate(x, y, z); + } catch (DuplicateSampleAbscissaException e) { + throw new FunctionEvaluationException(z, e.getSpecificPattern(), e.getGeneralPattern(), e.getArguments()); + } + } + + /** + * Returns the degree of the polynomial. + * + * @return the degree of the polynomial + */ + public int degree() { + return x.length - 1; + } + + /** + * Returns a copy of the interpolating points array. + * <p> + * Changes made to the returned copy will not affect the polynomial.</p> + * + * @return a fresh copy of the interpolating points array + */ + public double[] getInterpolatingPoints() { + double[] out = new double[x.length]; + System.arraycopy(x, 0, out, 0, x.length); + return out; + } + + /** + * Returns a copy of the interpolating values array. + * <p> + * Changes made to the returned copy will not affect the polynomial.</p> + * + * @return a fresh copy of the interpolating values array + */ + public double[] getInterpolatingValues() { + double[] out = new double[y.length]; + System.arraycopy(y, 0, out, 0, y.length); + return out; + } + + /** + * Returns a copy of the coefficients array. + * <p> + * Changes made to the returned copy will not affect the polynomial.</p> + * <p> + * Note that coefficients computation can be ill-conditioned. Use with caution + * and only when it is necessary.</p> + * + * @return a fresh copy of the coefficients array + */ + public double[] getCoefficients() { + if (!coefficientsComputed) { + computeCoefficients(); + } + double[] out = new double[coefficients.length]; + System.arraycopy(coefficients, 0, out, 0, coefficients.length); + return out; + } + + /** + * Evaluate the Lagrange polynomial using + * <a href="http://mathworld.wolfram.com/NevillesAlgorithm.html"> + * Neville's Algorithm</a>. It takes O(N^2) time. + * <p> + * This function is made public static so that users can call it directly + * without instantiating PolynomialFunctionLagrangeForm object.</p> + * + * @param x the interpolating points array + * @param y the interpolating values array + * @param z the point at which the function value is to be computed + * @return the function value + * @throws DuplicateSampleAbscissaException if the sample has duplicate abscissas + * @throws IllegalArgumentException if inputs are not valid + */ + public static double evaluate(double x[], double y[], double z) throws + DuplicateSampleAbscissaException, IllegalArgumentException { + + verifyInterpolationArray(x, y); + + int nearest = 0; + final int n = x.length; + final double[] c = new double[n]; + final double[] d = new double[n]; + double min_dist = Double.POSITIVE_INFINITY; + for (int i = 0; i < n; i++) { + // initialize the difference arrays + c[i] = y[i]; + d[i] = y[i]; + // find out the abscissa closest to z + final double dist = FastMath.abs(z - x[i]); + if (dist < min_dist) { + nearest = i; + min_dist = dist; + } + } + + // initial approximation to the function value at z + double value = y[nearest]; + + for (int i = 1; i < n; i++) { + for (int j = 0; j < n-i; j++) { + final double tc = x[j] - z; + final double td = x[i+j] - z; + final double divider = x[j] - x[i+j]; + if (divider == 0.0) { + // This happens only when two abscissas are identical. + throw new DuplicateSampleAbscissaException(x[i], i, i+j); + } + // update the difference arrays + final double w = (c[j+1] - d[j]) / divider; + c[j] = tc * w; + d[j] = td * w; + } + // sum up the difference terms to get the final value + if (nearest < 0.5*(n-i+1)) { + value += c[nearest]; // fork down + } else { + nearest--; + value += d[nearest]; // fork up + } + } + + return value; + } + + /** + * Calculate the coefficients of Lagrange polynomial from the + * interpolation data. It takes O(N^2) time. + * <p> + * Note this computation can be ill-conditioned. Use with caution + * and only when it is necessary.</p> + * + * @throws ArithmeticException if any abscissas coincide + */ + protected void computeCoefficients() throws ArithmeticException { + + final int n = degree() + 1; + coefficients = new double[n]; + for (int i = 0; i < n; i++) { + coefficients[i] = 0.0; + } + + // c[] are the coefficients of P(x) = (x-x[0])(x-x[1])...(x-x[n-1]) + final double[] c = new double[n+1]; + c[0] = 1.0; + for (int i = 0; i < n; i++) { + for (int j = i; j > 0; j--) { + c[j] = c[j-1] - c[j] * x[i]; + } + c[0] *= -x[i]; + c[i+1] = 1; + } + + final double[] tc = new double[n]; + for (int i = 0; i < n; i++) { + // d = (x[i]-x[0])...(x[i]-x[i-1])(x[i]-x[i+1])...(x[i]-x[n-1]) + double d = 1; + for (int j = 0; j < n; j++) { + if (i != j) { + d *= x[i] - x[j]; + } + } + if (d == 0.0) { + // This happens only when two abscissas are identical. + for (int k = 0; k < n; ++k) { + if ((i != k) && (x[i] == x[k])) { + throw MathRuntimeException.createArithmeticException( + LocalizedFormats.IDENTICAL_ABSCISSAS_DIVISION_BY_ZERO, + i, k, x[i]); + } + } + } + final double t = y[i] / d; + // Lagrange polynomial is the sum of n terms, each of which is a + // polynomial of degree n-1. tc[] are the coefficients of the i-th + // numerator Pi(x) = (x-x[0])...(x-x[i-1])(x-x[i+1])...(x-x[n-1]). + tc[n-1] = c[n]; // actually c[n] = 1 + coefficients[n-1] += t * tc[n-1]; + for (int j = n-2; j >= 0; j--) { + tc[j] = c[j+1] + tc[j+1] * x[i]; + coefficients[j] += t * tc[j]; + } + } + + coefficientsComputed = true; + } + + /** + * Verifies that the interpolation arrays are valid. + * <p> + * The arrays features checked by this method are that both arrays have the + * same length and this length is at least 2. + * </p> + * <p> + * The interpolating points must be distinct. However it is not + * verified here, it is checked in evaluate() and computeCoefficients(). + * </p> + * + * @param x the interpolating points array + * @param y the interpolating values array + * @throws IllegalArgumentException if not valid + * @see #evaluate(double[], double[], double) + * @see #computeCoefficients() + */ + public static void verifyInterpolationArray(double x[], double y[]) + throws IllegalArgumentException { + + if (x.length != y.length) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, x.length, y.length); + } + + if (x.length < 2) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.WRONG_NUMBER_OF_POINTS, 2, x.length); + } + + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionNewtonForm.java b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionNewtonForm.java new file mode 100644 index 0000000..5ee3224 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionNewtonForm.java @@ -0,0 +1,221 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.polynomials; + +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.exception.util.LocalizedFormats; + +/** + * Implements the representation of a real polynomial function in + * Newton Form. For reference, see <b>Elementary Numerical Analysis</b>, + * ISBN 0070124477, chapter 2. + * <p> + * The formula of polynomial in Newton form is + * p(x) = a[0] + a[1](x-c[0]) + a[2](x-c[0])(x-c[1]) + ... + + * a[n](x-c[0])(x-c[1])...(x-c[n-1]) + * Note that the length of a[] is one more than the length of c[]</p> + * + * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $ + * @since 1.2 + */ +public class PolynomialFunctionNewtonForm implements UnivariateRealFunction { + + /** + * The coefficients of the polynomial, ordered by degree -- i.e. + * coefficients[0] is the constant term and coefficients[n] is the + * coefficient of x^n where n is the degree of the polynomial. + */ + private double coefficients[]; + + /** + * Centers of the Newton polynomial. + */ + private final double c[]; + + /** + * When all c[i] = 0, a[] becomes normal polynomial coefficients, + * i.e. a[i] = coefficients[i]. + */ + private final double a[]; + + /** + * Whether the polynomial coefficients are available. + */ + private boolean coefficientsComputed; + + /** + * Construct a Newton polynomial with the given a[] and c[]. The order of + * centers are important in that if c[] shuffle, then values of a[] would + * completely change, not just a permutation of old a[]. + * <p> + * The constructor makes copy of the input arrays and assigns them.</p> + * + * @param a the coefficients in Newton form formula + * @param c the centers + * @throws IllegalArgumentException if input arrays are not valid + */ + public PolynomialFunctionNewtonForm(double a[], double c[]) + throws IllegalArgumentException { + + verifyInputArray(a, c); + this.a = new double[a.length]; + this.c = new double[c.length]; + System.arraycopy(a, 0, this.a, 0, a.length); + System.arraycopy(c, 0, this.c, 0, c.length); + coefficientsComputed = false; + } + + /** + * Calculate the function value at the given point. + * + * @param z the point at which the function value is to be computed + * @return the function value + * @throws FunctionEvaluationException if a runtime error occurs + * @see UnivariateRealFunction#value(double) + */ + public double value(double z) throws FunctionEvaluationException { + return evaluate(a, c, z); + } + + /** + * Returns the degree of the polynomial. + * + * @return the degree of the polynomial + */ + public int degree() { + return c.length; + } + + /** + * Returns a copy of coefficients in Newton form formula. + * <p> + * Changes made to the returned copy will not affect the polynomial.</p> + * + * @return a fresh copy of coefficients in Newton form formula + */ + public double[] getNewtonCoefficients() { + double[] out = new double[a.length]; + System.arraycopy(a, 0, out, 0, a.length); + return out; + } + + /** + * Returns a copy of the centers array. + * <p> + * Changes made to the returned copy will not affect the polynomial.</p> + * + * @return a fresh copy of the centers array + */ + public double[] getCenters() { + double[] out = new double[c.length]; + System.arraycopy(c, 0, out, 0, c.length); + return out; + } + + /** + * Returns a copy of the coefficients array. + * <p> + * Changes made to the returned copy will not affect the polynomial.</p> + * + * @return a fresh copy of the coefficients array + */ + public double[] getCoefficients() { + if (!coefficientsComputed) { + computeCoefficients(); + } + double[] out = new double[coefficients.length]; + System.arraycopy(coefficients, 0, out, 0, coefficients.length); + return out; + } + + /** + * Evaluate the Newton polynomial using nested multiplication. It is + * also called <a href="http://mathworld.wolfram.com/HornersRule.html"> + * Horner's Rule</a> and takes O(N) time. + * + * @param a the coefficients in Newton form formula + * @param c the centers + * @param z the point at which the function value is to be computed + * @return the function value + * @throws FunctionEvaluationException if a runtime error occurs + * @throws IllegalArgumentException if inputs are not valid + */ + public static double evaluate(double a[], double c[], double z) + throws FunctionEvaluationException, IllegalArgumentException { + + verifyInputArray(a, c); + + int n = c.length; + double value = a[n]; + for (int i = n-1; i >= 0; i--) { + value = a[i] + (z - c[i]) * value; + } + + return value; + } + + /** + * Calculate the normal polynomial coefficients given the Newton form. + * It also uses nested multiplication but takes O(N^2) time. + */ + protected void computeCoefficients() { + final int n = degree(); + + coefficients = new double[n+1]; + for (int i = 0; i <= n; i++) { + coefficients[i] = 0.0; + } + + coefficients[0] = a[n]; + for (int i = n-1; i >= 0; i--) { + for (int j = n-i; j > 0; j--) { + coefficients[j] = coefficients[j-1] - c[i] * coefficients[j]; + } + coefficients[0] = a[i] - c[i] * coefficients[0]; + } + + coefficientsComputed = true; + } + + /** + * Verifies that the input arrays are valid. + * <p> + * The centers must be distinct for interpolation purposes, but not + * for general use. Thus it is not verified here.</p> + * + * @param a the coefficients in Newton form formula + * @param c the centers + * @throws IllegalArgumentException if not valid + * @see org.apache.commons.math.analysis.interpolation.DividedDifferenceInterpolator#computeDividedDifference(double[], + * double[]) + */ + protected static void verifyInputArray(double a[], double c[]) throws + IllegalArgumentException { + + if (a.length < 1 || c.length < 1) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY); + } + if (a.length != c.length + 1) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.ARRAY_SIZES_SHOULD_HAVE_DIFFERENCE_1, + a.length, c.length); + } + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialSplineFunction.java b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialSplineFunction.java new file mode 100644 index 0000000..a0e1e01 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialSplineFunction.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.polynomials; + +import java.util.Arrays; + +import org.apache.commons.math.ArgumentOutsideDomainException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.analysis.DifferentiableUnivariateRealFunction; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.exception.util.LocalizedFormats; + +/** + * Represents a polynomial spline function. + * <p> + * A <strong>polynomial spline function</strong> consists of a set of + * <i>interpolating polynomials</i> and an ascending array of domain + * <i>knot points</i>, determining the intervals over which the spline function + * is defined by the constituent polynomials. The polynomials are assumed to + * have been computed to match the values of another function at the knot + * points. The value consistency constraints are not currently enforced by + * <code>PolynomialSplineFunction</code> itself, but are assumed to hold among + * the polynomials and knot points passed to the constructor.</p> + * <p> + * N.B.: The polynomials in the <code>polynomials</code> property must be + * centered on the knot points to compute the spline function values. + * See below.</p> + * <p> + * The domain of the polynomial spline function is + * <code>[smallest knot, largest knot]</code>. Attempts to evaluate the + * function at values outside of this range generate IllegalArgumentExceptions. + * </p> + * <p> + * The value of the polynomial spline function for an argument <code>x</code> + * is computed as follows: + * <ol> + * <li>The knot array is searched to find the segment to which <code>x</code> + * belongs. If <code>x</code> is less than the smallest knot point or greater + * than the largest one, an <code>IllegalArgumentException</code> + * is thrown.</li> + * <li> Let <code>j</code> be the index of the largest knot point that is less + * than or equal to <code>x</code>. The value returned is <br> + * <code>polynomials[j](x - knot[j])</code></li></ol></p> + * + * @version $Revision: 1037327 $ $Date: 2010-11-20 21:57:37 +0100 (sam. 20 nov. 2010) $ + */ +public class PolynomialSplineFunction + implements DifferentiableUnivariateRealFunction { + + /** Spline segment interval delimiters (knots). Size is n+1 for n segments. */ + private final double knots[]; + + /** + * The polynomial functions that make up the spline. The first element + * determines the value of the spline over the first subinterval, the + * second over the second, etc. Spline function values are determined by + * evaluating these functions at <code>(x - knot[i])</code> where i is the + * knot segment to which x belongs. + */ + private final PolynomialFunction polynomials[]; + + /** + * Number of spline segments = number of polynomials + * = number of partition points - 1 + */ + private final int n; + + + /** + * Construct a polynomial spline function with the given segment delimiters + * and interpolating polynomials. + * <p> + * The constructor copies both arrays and assigns the copies to the knots + * and polynomials properties, respectively.</p> + * + * @param knots spline segment interval delimiters + * @param polynomials polynomial functions that make up the spline + * @throws NullPointerException if either of the input arrays is null + * @throws IllegalArgumentException if knots has length less than 2, + * <code>polynomials.length != knots.length - 1 </code>, or the knots array + * is not strictly increasing. + * + */ + public PolynomialSplineFunction(double knots[], PolynomialFunction polynomials[]) { + if (knots.length < 2) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.NOT_ENOUGH_POINTS_IN_SPLINE_PARTITION, + 2, knots.length); + } + if (knots.length - 1 != polynomials.length) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.POLYNOMIAL_INTERPOLANTS_MISMATCH_SEGMENTS, + polynomials.length, knots.length); + } + if (!isStrictlyIncreasing(knots)) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.NOT_STRICTLY_INCREASING_KNOT_VALUES); + } + + this.n = knots.length -1; + this.knots = new double[n + 1]; + System.arraycopy(knots, 0, this.knots, 0, n + 1); + this.polynomials = new PolynomialFunction[n]; + System.arraycopy(polynomials, 0, this.polynomials, 0, n); + } + + /** + * Compute the value for the function. + * See {@link PolynomialSplineFunction} for details on the algorithm for + * computing the value of the function.</p> + * + * @param v the point for which the function value should be computed + * @return the value + * @throws ArgumentOutsideDomainException if v is outside of the domain of + * of the spline function (less than the smallest knot point or greater + * than the largest knot point) + */ + public double value(double v) throws ArgumentOutsideDomainException { + if (v < knots[0] || v > knots[n]) { + throw new ArgumentOutsideDomainException(v, knots[0], knots[n]); + } + int i = Arrays.binarySearch(knots, v); + if (i < 0) { + i = -i - 2; + } + //This will handle the case where v is the last knot value + //There are only n-1 polynomials, so if v is the last knot + //then we will use the last polynomial to calculate the value. + if ( i >= polynomials.length ) { + i--; + } + return polynomials[i].value(v - knots[i]); + } + + /** + * Returns the derivative of the polynomial spline function as a UnivariateRealFunction + * @return the derivative function + */ + public UnivariateRealFunction derivative() { + return polynomialSplineDerivative(); + } + + /** + * Returns the derivative of the polynomial spline function as a PolynomialSplineFunction + * + * @return the derivative function + */ + public PolynomialSplineFunction polynomialSplineDerivative() { + PolynomialFunction derivativePolynomials[] = new PolynomialFunction[n]; + for (int i = 0; i < n; i++) { + derivativePolynomials[i] = polynomials[i].polynomialDerivative(); + } + return new PolynomialSplineFunction(knots, derivativePolynomials); + } + + /** + * Returns the number of spline segments = the number of polynomials + * = the number of knot points - 1. + * + * @return the number of spline segments + */ + public int getN() { + return n; + } + + /** + * Returns a copy of the interpolating polynomials array. + * <p> + * Returns a fresh copy of the array. Changes made to the copy will + * not affect the polynomials property.</p> + * + * @return the interpolating polynomials + */ + public PolynomialFunction[] getPolynomials() { + PolynomialFunction p[] = new PolynomialFunction[n]; + System.arraycopy(polynomials, 0, p, 0, n); + return p; + } + + /** + * Returns an array copy of the knot points. + * <p> + * Returns a fresh copy of the array. Changes made to the copy + * will not affect the knots property.</p> + * + * @return the knot points + */ + public double[] getKnots() { + double out[] = new double[n + 1]; + System.arraycopy(knots, 0, out, 0, n + 1); + return out; + } + + /** + * Determines if the given array is ordered in a strictly increasing + * fashion. + * + * @param x the array to examine. + * @return <code>true</code> if the elements in <code>x</code> are ordered + * in a stricly increasing manner. <code>false</code>, otherwise. + */ + private static boolean isStrictlyIncreasing(double[] x) { + for (int i = 1; i < x.length; ++i) { + if (x[i - 1] >= x[i]) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialsUtils.java b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialsUtils.java new file mode 100644 index 0000000..5e939c7 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialsUtils.java @@ -0,0 +1,281 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.polynomials; + +import java.util.ArrayList; + +import org.apache.commons.math.fraction.BigFraction; +import org.apache.commons.math.util.FastMath; + +/** + * A collection of static methods that operate on or return polynomials. + * + * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $ + * @since 2.0 + */ +public class PolynomialsUtils { + + /** Coefficients for Chebyshev polynomials. */ + private static final ArrayList<BigFraction> CHEBYSHEV_COEFFICIENTS; + + /** Coefficients for Hermite polynomials. */ + private static final ArrayList<BigFraction> HERMITE_COEFFICIENTS; + + /** Coefficients for Laguerre polynomials. */ + private static final ArrayList<BigFraction> LAGUERRE_COEFFICIENTS; + + /** Coefficients for Legendre polynomials. */ + private static final ArrayList<BigFraction> LEGENDRE_COEFFICIENTS; + + static { + + // initialize recurrence for Chebyshev polynomials + // T0(X) = 1, T1(X) = 0 + 1 * X + CHEBYSHEV_COEFFICIENTS = new ArrayList<BigFraction>(); + CHEBYSHEV_COEFFICIENTS.add(BigFraction.ONE); + CHEBYSHEV_COEFFICIENTS.add(BigFraction.ZERO); + CHEBYSHEV_COEFFICIENTS.add(BigFraction.ONE); + + // initialize recurrence for Hermite polynomials + // H0(X) = 1, H1(X) = 0 + 2 * X + HERMITE_COEFFICIENTS = new ArrayList<BigFraction>(); + HERMITE_COEFFICIENTS.add(BigFraction.ONE); + HERMITE_COEFFICIENTS.add(BigFraction.ZERO); + HERMITE_COEFFICIENTS.add(BigFraction.TWO); + + // initialize recurrence for Laguerre polynomials + // L0(X) = 1, L1(X) = 1 - 1 * X + LAGUERRE_COEFFICIENTS = new ArrayList<BigFraction>(); + LAGUERRE_COEFFICIENTS.add(BigFraction.ONE); + LAGUERRE_COEFFICIENTS.add(BigFraction.ONE); + LAGUERRE_COEFFICIENTS.add(BigFraction.MINUS_ONE); + + // initialize recurrence for Legendre polynomials + // P0(X) = 1, P1(X) = 0 + 1 * X + LEGENDRE_COEFFICIENTS = new ArrayList<BigFraction>(); + LEGENDRE_COEFFICIENTS.add(BigFraction.ONE); + LEGENDRE_COEFFICIENTS.add(BigFraction.ZERO); + LEGENDRE_COEFFICIENTS.add(BigFraction.ONE); + + } + + /** + * Private constructor, to prevent instantiation. + */ + private PolynomialsUtils() { + } + + /** + * Create a Chebyshev polynomial of the first kind. + * <p><a href="http://mathworld.wolfram.com/ChebyshevPolynomialoftheFirstKind.html">Chebyshev + * polynomials of the first kind</a> are orthogonal polynomials. + * They can be defined by the following recurrence relations: + * <pre> + * T<sub>0</sub>(X) = 1 + * T<sub>1</sub>(X) = X + * T<sub>k+1</sub>(X) = 2X T<sub>k</sub>(X) - T<sub>k-1</sub>(X) + * </pre></p> + * @param degree degree of the polynomial + * @return Chebyshev polynomial of specified degree + */ + public static PolynomialFunction createChebyshevPolynomial(final int degree) { + return buildPolynomial(degree, CHEBYSHEV_COEFFICIENTS, + new RecurrenceCoefficientsGenerator() { + private final BigFraction[] coeffs = { BigFraction.ZERO, BigFraction.TWO, BigFraction.ONE }; + /** {@inheritDoc} */ + public BigFraction[] generate(int k) { + return coeffs; + } + }); + } + + /** + * Create a Hermite polynomial. + * <p><a href="http://mathworld.wolfram.com/HermitePolynomial.html">Hermite + * polynomials</a> are orthogonal polynomials. + * They can be defined by the following recurrence relations: + * <pre> + * H<sub>0</sub>(X) = 1 + * H<sub>1</sub>(X) = 2X + * H<sub>k+1</sub>(X) = 2X H<sub>k</sub>(X) - 2k H<sub>k-1</sub>(X) + * </pre></p> + + * @param degree degree of the polynomial + * @return Hermite polynomial of specified degree + */ + public static PolynomialFunction createHermitePolynomial(final int degree) { + return buildPolynomial(degree, HERMITE_COEFFICIENTS, + new RecurrenceCoefficientsGenerator() { + /** {@inheritDoc} */ + public BigFraction[] generate(int k) { + return new BigFraction[] { + BigFraction.ZERO, + BigFraction.TWO, + new BigFraction(2 * k)}; + } + }); + } + + /** + * Create a Laguerre polynomial. + * <p><a href="http://mathworld.wolfram.com/LaguerrePolynomial.html">Laguerre + * polynomials</a> are orthogonal polynomials. + * They can be defined by the following recurrence relations: + * <pre> + * L<sub>0</sub>(X) = 1 + * L<sub>1</sub>(X) = 1 - X + * (k+1) L<sub>k+1</sub>(X) = (2k + 1 - X) L<sub>k</sub>(X) - k L<sub>k-1</sub>(X) + * </pre></p> + * @param degree degree of the polynomial + * @return Laguerre polynomial of specified degree + */ + public static PolynomialFunction createLaguerrePolynomial(final int degree) { + return buildPolynomial(degree, LAGUERRE_COEFFICIENTS, + new RecurrenceCoefficientsGenerator() { + /** {@inheritDoc} */ + public BigFraction[] generate(int k) { + final int kP1 = k + 1; + return new BigFraction[] { + new BigFraction(2 * k + 1, kP1), + new BigFraction(-1, kP1), + new BigFraction(k, kP1)}; + } + }); + } + + /** + * Create a Legendre polynomial. + * <p><a href="http://mathworld.wolfram.com/LegendrePolynomial.html">Legendre + * polynomials</a> are orthogonal polynomials. + * They can be defined by the following recurrence relations: + * <pre> + * P<sub>0</sub>(X) = 1 + * P<sub>1</sub>(X) = X + * (k+1) P<sub>k+1</sub>(X) = (2k+1) X P<sub>k</sub>(X) - k P<sub>k-1</sub>(X) + * </pre></p> + * @param degree degree of the polynomial + * @return Legendre polynomial of specified degree + */ + public static PolynomialFunction createLegendrePolynomial(final int degree) { + return buildPolynomial(degree, LEGENDRE_COEFFICIENTS, + new RecurrenceCoefficientsGenerator() { + /** {@inheritDoc} */ + public BigFraction[] generate(int k) { + final int kP1 = k + 1; + return new BigFraction[] { + BigFraction.ZERO, + new BigFraction(k + kP1, kP1), + new BigFraction(k, kP1)}; + } + }); + } + + /** Get the coefficients array for a given degree. + * @param degree degree of the polynomial + * @param coefficients list where the computed coefficients are stored + * @param generator recurrence coefficients generator + * @return coefficients array + */ + private static PolynomialFunction buildPolynomial(final int degree, + final ArrayList<BigFraction> coefficients, + final RecurrenceCoefficientsGenerator generator) { + + final int maxDegree = (int) FastMath.floor(FastMath.sqrt(2 * coefficients.size())) - 1; + synchronized (PolynomialsUtils.class) { + if (degree > maxDegree) { + computeUpToDegree(degree, maxDegree, generator, coefficients); + } + } + + // coefficient for polynomial 0 is l [0] + // coefficients for polynomial 1 are l [1] ... l [2] (degrees 0 ... 1) + // coefficients for polynomial 2 are l [3] ... l [5] (degrees 0 ... 2) + // coefficients for polynomial 3 are l [6] ... l [9] (degrees 0 ... 3) + // coefficients for polynomial 4 are l[10] ... l[14] (degrees 0 ... 4) + // coefficients for polynomial 5 are l[15] ... l[20] (degrees 0 ... 5) + // coefficients for polynomial 6 are l[21] ... l[27] (degrees 0 ... 6) + // ... + final int start = degree * (degree + 1) / 2; + + final double[] a = new double[degree + 1]; + for (int i = 0; i <= degree; ++i) { + a[i] = coefficients.get(start + i).doubleValue(); + } + + // build the polynomial + return new PolynomialFunction(a); + + } + + /** Compute polynomial coefficients up to a given degree. + * @param degree maximal degree + * @param maxDegree current maximal degree + * @param generator recurrence coefficients generator + * @param coefficients list where the computed coefficients should be appended + */ + private static void computeUpToDegree(final int degree, final int maxDegree, + final RecurrenceCoefficientsGenerator generator, + final ArrayList<BigFraction> coefficients) { + + int startK = (maxDegree - 1) * maxDegree / 2; + for (int k = maxDegree; k < degree; ++k) { + + // start indices of two previous polynomials Pk(X) and Pk-1(X) + int startKm1 = startK; + startK += k; + + // Pk+1(X) = (a[0] + a[1] X) Pk(X) - a[2] Pk-1(X) + BigFraction[] ai = generator.generate(k); + + BigFraction ck = coefficients.get(startK); + BigFraction ckm1 = coefficients.get(startKm1); + + // degree 0 coefficient + coefficients.add(ck.multiply(ai[0]).subtract(ckm1.multiply(ai[2]))); + + // degree 1 to degree k-1 coefficients + for (int i = 1; i < k; ++i) { + final BigFraction ckPrev = ck; + ck = coefficients.get(startK + i); + ckm1 = coefficients.get(startKm1 + i); + coefficients.add(ck.multiply(ai[0]).add(ckPrev.multiply(ai[1])).subtract(ckm1.multiply(ai[2]))); + } + + // degree k coefficient + final BigFraction ckPrev = ck; + ck = coefficients.get(startK + k); + coefficients.add(ck.multiply(ai[0]).add(ckPrev.multiply(ai[1]))); + + // degree k+1 coefficient + coefficients.add(ck.multiply(ai[1])); + + } + + } + + /** Interface for recurrence coefficients generation. */ + private static interface RecurrenceCoefficientsGenerator { + /** + * Generate recurrence coefficients. + * @param k highest degree of the polynomials used in the recurrence + * @return an array of three coefficients such that + * P<sub>k+1</sub>(X) = (a[0] + a[1] X) P<sub>k</sub>(X) - a[2] P<sub>k-1</sub>(X) + */ + BigFraction[] generate(int k); + } + +} diff --git a/src/main/java/org/apache/commons/math/analysis/polynomials/package.html b/src/main/java/org/apache/commons/math/analysis/polynomials/package.html new file mode 100644 index 0000000..63ca96a --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/polynomials/package.html @@ -0,0 +1,23 @@ +<html> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ --> + <body> + Univariate real polynomials implementations, seen as differentiable + univariate real functions. + </body> +</html> diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/BisectionSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/BisectionSolver.java new file mode 100644 index 0000000..c9d3a63 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/BisectionSolver.java @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.solvers; + +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MaxIterationsExceededException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.util.FastMath; + +/** + * Implements the <a href="http://mathworld.wolfram.com/Bisection.html"> + * bisection algorithm</a> for finding zeros of univariate real functions. + * <p> + * The function should be continuous but not necessarily smooth.</p> + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + */ +public class BisectionSolver extends UnivariateRealSolverImpl { + + /** + * Construct a solver for the given function. + * + * @param f function to solve. + * @deprecated as of 2.0 the function to solve is passed as an argument + * to the {@link #solve(UnivariateRealFunction, double, double)} or + * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)} + * method. + */ + @Deprecated + public BisectionSolver(UnivariateRealFunction f) { + super(f, 100, 1E-6); + } + + /** + * Construct a solver. + * + */ + public BisectionSolver() { + super(100, 1E-6); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(double min, double max, double initial) + throws MaxIterationsExceededException, FunctionEvaluationException { + return solve(f, min, max); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(double min, double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + return solve(f, min, max); + } + + /** + * {@inheritDoc} + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, double min, double max, double initial) + throws MaxIterationsExceededException, FunctionEvaluationException { + return solve(f, min, max); + } + + /** {@inheritDoc} */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, double min, double max, double initial) + throws MaxIterationsExceededException, FunctionEvaluationException { + return solve(maxEval, f, min, max); + } + + /** {@inheritDoc} */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, double min, double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max); + } + + /** + * {@inheritDoc} + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, double min, double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + + clearResult(); + verifyInterval(min,max); + double m; + double fm; + double fmin; + + int i = 0; + while (i < maximalIterationCount) { + m = UnivariateRealSolverUtils.midpoint(min, max); + fmin = f.value(min); + fm = f.value(m); + + if (fm * fmin > 0.0) { + // max and m bracket the root. + min = m; + } else { + // min and m bracket the root. + max = m; + } + + if (FastMath.abs(max - min) <= absoluteAccuracy) { + m = UnivariateRealSolverUtils.midpoint(min, max); + setResult(m, i); + return m; + } + ++i; + } + + throw new MaxIterationsExceededException(maximalIterationCount); + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/BrentSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/BrentSolver.java new file mode 100644 index 0000000..5aa2447 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/BrentSolver.java @@ -0,0 +1,399 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.solvers; + + +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.MaxIterationsExceededException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.util.FastMath; + +/** + * Implements the <a href="http://mathworld.wolfram.com/BrentsMethod.html"> + * Brent algorithm</a> for finding zeros of real univariate functions. + * <p> + * The function should be continuous but not necessarily smooth.</p> + * + * @version $Revision:670469 $ $Date:2008-06-23 10:01:38 +0200 (lun., 23 juin 2008) $ + */ +public class BrentSolver extends UnivariateRealSolverImpl { + + /** + * Default absolute accuracy + * @since 2.1 + */ + public static final double DEFAULT_ABSOLUTE_ACCURACY = 1E-6; + + /** Default maximum number of iterations + * @since 2.1 + */ + public static final int DEFAULT_MAXIMUM_ITERATIONS = 100; + + /** Serializable version identifier */ + private static final long serialVersionUID = 7694577816772532779L; + + /** + * Construct a solver for the given function. + * + * @param f function to solve. + * @deprecated as of 2.0 the function to solve is passed as an argument + * to the {@link #solve(UnivariateRealFunction, double, double)} or + * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)} + * method. + */ + @Deprecated + public BrentSolver(UnivariateRealFunction f) { + super(f, DEFAULT_MAXIMUM_ITERATIONS, DEFAULT_ABSOLUTE_ACCURACY); + } + + /** + * Construct a solver with default properties. + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public BrentSolver() { + super(DEFAULT_MAXIMUM_ITERATIONS, DEFAULT_ABSOLUTE_ACCURACY); + } + + /** + * Construct a solver with the given absolute accuracy. + * + * @param absoluteAccuracy lower bound for absolute accuracy of solutions returned by the solver + * @since 2.1 + */ + public BrentSolver(double absoluteAccuracy) { + super(DEFAULT_MAXIMUM_ITERATIONS, absoluteAccuracy); + } + + /** + * Contstruct a solver with the given maximum iterations and absolute accuracy. + * + * @param maximumIterations maximum number of iterations + * @param absoluteAccuracy lower bound for absolute accuracy of solutions returned by the solver + * @since 2.1 + */ + public BrentSolver(int maximumIterations, double absoluteAccuracy) { + super(maximumIterations, absoluteAccuracy); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(double min, double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + return solve(f, min, max); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(double min, double max, double initial) + throws MaxIterationsExceededException, FunctionEvaluationException { + return solve(f, min, max, initial); + } + + /** + * Find a zero in the given interval with an initial guess. + * <p>Throws <code>IllegalArgumentException</code> if the values of the + * function at the three points have the same sign (note that it is + * allowed to have endpoints with the same sign if the initial point has + * opposite sign function-wise).</p> + * + * @param f function to solve. + * @param min the lower bound for the interval. + * @param max the upper bound for the interval. + * @param initial the start value to use (must be set to min if no + * initial point is known). + * @return the value where the function is zero + * @throws MaxIterationsExceededException the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if initial is not between min and max + * (even if it <em>is</em> a root) + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, + final double min, final double max, final double initial) + throws MaxIterationsExceededException, FunctionEvaluationException { + + clearResult(); + if ((initial < min) || (initial > max)) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.INVALID_INTERVAL_INITIAL_VALUE_PARAMETERS, + min, initial, max); + } + + // return the initial guess if it is good enough + double yInitial = f.value(initial); + if (FastMath.abs(yInitial) <= functionValueAccuracy) { + setResult(initial, 0); + return result; + } + + // return the first endpoint if it is good enough + double yMin = f.value(min); + if (FastMath.abs(yMin) <= functionValueAccuracy) { + setResult(min, 0); + return result; + } + + // reduce interval if min and initial bracket the root + if (yInitial * yMin < 0) { + return solve(f, min, yMin, initial, yInitial, min, yMin); + } + + // return the second endpoint if it is good enough + double yMax = f.value(max); + if (FastMath.abs(yMax) <= functionValueAccuracy) { + setResult(max, 0); + return result; + } + + // reduce interval if initial and max bracket the root + if (yInitial * yMax < 0) { + return solve(f, initial, yInitial, max, yMax, initial, yInitial); + } + + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.SAME_SIGN_AT_ENDPOINTS, min, max, yMin, yMax); + + } + + /** + * Find a zero in the given interval with an initial guess. + * <p>Throws <code>IllegalArgumentException</code> if the values of the + * function at the three points have the same sign (note that it is + * allowed to have endpoints with the same sign if the initial point has + * opposite sign function-wise).</p> + * + * @param f function to solve. + * @param min the lower bound for the interval. + * @param max the upper bound for the interval. + * @param initial the start value to use (must be set to min if no + * initial point is known). + * @param maxEval Maximum number of evaluations. + * @return the value where the function is zero + * @throws MaxIterationsExceededException the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if initial is not between min and max + * (even if it <em>is</em> a root) + */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, + final double min, final double max, final double initial) + throws MaxIterationsExceededException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max, initial); + } + + /** + * Find a zero in the given interval. + * <p> + * Requires that the values of the function at the endpoints have opposite + * signs. An <code>IllegalArgumentException</code> is thrown if this is not + * the case.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval. + * @param max the upper bound for the interval. + * @return the value where the function is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min is not less than max or the + * signs of the values of the function at the endpoints are not opposites + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, + final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + + clearResult(); + verifyInterval(min, max); + + double ret = Double.NaN; + + double yMin = f.value(min); + double yMax = f.value(max); + + // Verify bracketing + double sign = yMin * yMax; + if (sign > 0) { + // check if either value is close to a zero + if (FastMath.abs(yMin) <= functionValueAccuracy) { + setResult(min, 0); + ret = min; + } else if (FastMath.abs(yMax) <= functionValueAccuracy) { + setResult(max, 0); + ret = max; + } else { + // neither value is close to zero and min and max do not bracket root. + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.SAME_SIGN_AT_ENDPOINTS, min, max, yMin, yMax); + } + } else if (sign < 0){ + // solve using only the first endpoint as initial guess + ret = solve(f, min, yMin, max, yMax, min, yMin); + } else { + // either min or max is a root + if (yMin == 0.0) { + ret = min; + } else { + ret = max; + } + } + + return ret; + } + + /** + * Find a zero in the given interval. + * <p> + * Requires that the values of the function at the endpoints have opposite + * signs. An <code>IllegalArgumentException</code> is thrown if this is not + * the case.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval. + * @param max the upper bound for the interval. + * @param maxEval Maximum number of evaluations. + * @return the value where the function is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min is not less than max or the + * signs of the values of the function at the endpoints are not opposites + */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, + final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max); + } + + /** + * Find a zero starting search according to the three provided points. + * @param f the function to solve + * @param x0 old approximation for the root + * @param y0 function value at the approximation for the root + * @param x1 last calculated approximation for the root + * @param y1 function value at the last calculated approximation + * for the root + * @param x2 bracket point (must be set to x0 if no bracket point is + * known, this will force starting with linear interpolation) + * @param y2 function value at the bracket point. + * @return the value where the function is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + */ + private double solve(final UnivariateRealFunction f, + double x0, double y0, + double x1, double y1, + double x2, double y2) + throws MaxIterationsExceededException, FunctionEvaluationException { + + double delta = x1 - x0; + double oldDelta = delta; + + int i = 0; + while (i < maximalIterationCount) { + if (FastMath.abs(y2) < FastMath.abs(y1)) { + // use the bracket point if is better than last approximation + x0 = x1; + x1 = x2; + x2 = x0; + y0 = y1; + y1 = y2; + y2 = y0; + } + if (FastMath.abs(y1) <= functionValueAccuracy) { + // Avoid division by very small values. Assume + // the iteration has converged (the problem may + // still be ill conditioned) + setResult(x1, i); + return result; + } + double dx = x2 - x1; + double tolerance = + FastMath.max(relativeAccuracy * FastMath.abs(x1), absoluteAccuracy); + if (FastMath.abs(dx) <= tolerance) { + setResult(x1, i); + return result; + } + if ((FastMath.abs(oldDelta) < tolerance) || + (FastMath.abs(y0) <= FastMath.abs(y1))) { + // Force bisection. + delta = 0.5 * dx; + oldDelta = delta; + } else { + double r3 = y1 / y0; + double p; + double p1; + // the equality test (x0 == x2) is intentional, + // it is part of the original Brent's method, + // it should NOT be replaced by proximity test + if (x0 == x2) { + // Linear interpolation. + p = dx * r3; + p1 = 1.0 - r3; + } else { + // Inverse quadratic interpolation. + double r1 = y0 / y2; + double r2 = y1 / y2; + p = r3 * (dx * r1 * (r1 - r2) - (x1 - x0) * (r2 - 1.0)); + p1 = (r1 - 1.0) * (r2 - 1.0) * (r3 - 1.0); + } + if (p > 0.0) { + p1 = -p1; + } else { + p = -p; + } + if (2.0 * p >= 1.5 * dx * p1 - FastMath.abs(tolerance * p1) || + p >= FastMath.abs(0.5 * oldDelta * p1)) { + // Inverse quadratic interpolation gives a value + // in the wrong direction, or progress is slow. + // Fall back to bisection. + delta = 0.5 * dx; + oldDelta = delta; + } else { + oldDelta = delta; + delta = p / p1; + } + } + // Save old X1, Y1 + x0 = x1; + y0 = y1; + // Compute new X1, Y1 + if (FastMath.abs(delta) > tolerance) { + x1 = x1 + delta; + } else if (dx > 0.0) { + x1 = x1 + 0.5 * tolerance; + } else if (dx <= 0.0) { + x1 = x1 - 0.5 * tolerance; + } + y1 = f.value(x1); + if ((y1 > 0) == (y2 > 0)) { + x2 = x0; + y2 = y0; + delta = x1 - x0; + oldDelta = delta; + } + i++; + } + throw new MaxIterationsExceededException(maximalIterationCount); + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/LaguerreSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/LaguerreSolver.java new file mode 100644 index 0000000..e6d8bd8 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/LaguerreSolver.java @@ -0,0 +1,434 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.solvers; + +import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.MaxIterationsExceededException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.analysis.polynomials.PolynomialFunction; +import org.apache.commons.math.complex.Complex; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.util.FastMath; + +/** + * Implements the <a href="http://mathworld.wolfram.com/LaguerresMethod.html"> + * Laguerre's Method</a> for root finding of real coefficient polynomials. + * For reference, see <b>A First Course in Numerical Analysis</b>, + * ISBN 048641454X, chapter 8. + * <p> + * Laguerre's method is global in the sense that it can start with any initial + * approximation and be able to solve all roots from that point.</p> + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + * @since 1.2 + */ +public class LaguerreSolver extends UnivariateRealSolverImpl { + + /** polynomial function to solve. + * @deprecated as of 2.0 the function is not stored anymore in the instance + */ + @Deprecated + private final PolynomialFunction p; + + /** + * Construct a solver for the given function. + * + * @param f function to solve + * @throws IllegalArgumentException if function is not polynomial + * @deprecated as of 2.0 the function to solve is passed as an argument + * to the {@link #solve(UnivariateRealFunction, double, double)} or + * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)} + * method. + */ + @Deprecated + public LaguerreSolver(UnivariateRealFunction f) throws IllegalArgumentException { + super(f, 100, 1E-6); + if (f instanceof PolynomialFunction) { + p = (PolynomialFunction) f; + } else { + throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.FUNCTION_NOT_POLYNOMIAL); + } + } + + /** + * Construct a solver. + * @deprecated in 2.2 (to be removed in 3.0) + */ + @Deprecated + public LaguerreSolver() { + super(100, 1E-6); + p = null; + } + + /** + * Returns a copy of the polynomial function. + * + * @return a fresh copy of the polynomial function + * @deprecated as of 2.0 the function is not stored anymore within the instance. + */ + @Deprecated + public PolynomialFunction getPolynomialFunction() { + return new PolynomialFunction(p.getCoefficients()); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(final double min, final double max) + throws ConvergenceException, FunctionEvaluationException { + return solve(p, min, max); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(final double min, final double max, final double initial) + throws ConvergenceException, FunctionEvaluationException { + return solve(p, min, max, initial); + } + + /** + * Find a real root in the given interval with initial value. + * <p> + * Requires bracketing condition.</p> + * + * @param f function to solve (must be polynomial) + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param initial the start value to use + * @param maxEval Maximum number of evaluations. + * @return the point at which the function value is zero + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, + final double min, final double max, final double initial) + throws ConvergenceException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max, initial); + } + + /** + * Find a real root in the given interval with initial value. + * <p> + * Requires bracketing condition.</p> + * + * @param f function to solve (must be polynomial) + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param initial the start value to use + * @return the point at which the function value is zero + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, + final double min, final double max, final double initial) + throws ConvergenceException, FunctionEvaluationException { + + // check for zeros before verifying bracketing + if (f.value(min) == 0.0) { + return min; + } + if (f.value(max) == 0.0) { + return max; + } + if (f.value(initial) == 0.0) { + return initial; + } + + verifyBracketing(min, max, f); + verifySequence(min, initial, max); + if (isBracketing(min, initial, f)) { + return solve(f, min, initial); + } else { + return solve(f, initial, max); + } + + } + + /** + * Find a real root in the given interval. + * <p> + * Despite the bracketing condition, the root returned by solve(Complex[], + * Complex) may not be a real zero inside [min, max]. For example, + * p(x) = x^3 + 1, min = -2, max = 2, initial = 0. We can either try + * another initial value, or, as we did here, call solveAll() to obtain + * all roots and pick up the one that we're looking for.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param maxEval Maximum number of evaluations. + * @return the point at which the function value is zero + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, + final double min, final double max) + throws ConvergenceException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max); + } + + /** + * Find a real root in the given interval. + * <p> + * Despite the bracketing condition, the root returned by solve(Complex[], + * Complex) may not be a real zero inside [min, max]. For example, + * p(x) = x^3 + 1, min = -2, max = 2, initial = 0. We can either try + * another initial value, or, as we did here, call solveAll() to obtain + * all roots and pick up the one that we're looking for.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @return the point at which the function value is zero + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, + final double min, final double max) + throws ConvergenceException, FunctionEvaluationException { + + // check function type + if (!(f instanceof PolynomialFunction)) { + throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.FUNCTION_NOT_POLYNOMIAL); + } + + // check for zeros before verifying bracketing + if (f.value(min) == 0.0) { return min; } + if (f.value(max) == 0.0) { return max; } + verifyBracketing(min, max, f); + + double coefficients[] = ((PolynomialFunction) f).getCoefficients(); + Complex c[] = new Complex[coefficients.length]; + for (int i = 0; i < coefficients.length; i++) { + c[i] = new Complex(coefficients[i], 0.0); + } + Complex initial = new Complex(0.5 * (min + max), 0.0); + Complex z = solve(c, initial); + if (isRootOK(min, max, z)) { + setResult(z.getReal(), iterationCount); + return result; + } + + // solve all roots and select the one we're seeking + Complex[] root = solveAll(c, initial); + for (int i = 0; i < root.length; i++) { + if (isRootOK(min, max, root[i])) { + setResult(root[i].getReal(), iterationCount); + return result; + } + } + + // should never happen + throw new ConvergenceException(); + } + + /** + * Returns true iff the given complex root is actually a real zero + * in the given interval, within the solver tolerance level. + * + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param z the complex root + * @return true iff z is the sought-after real zero + */ + protected boolean isRootOK(double min, double max, Complex z) { + double tolerance = FastMath.max(relativeAccuracy * z.abs(), absoluteAccuracy); + return (isSequence(min, z.getReal(), max)) && + (FastMath.abs(z.getImaginary()) <= tolerance || + z.abs() <= functionValueAccuracy); + } + + /** + * Find all complex roots for the polynomial with the given coefficients, + * starting from the given initial value. + * + * @param coefficients the polynomial coefficients array + * @param initial the start value to use + * @return the point at which the function value is zero + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + * @deprecated in 2.2. + */ + @Deprecated + public Complex[] solveAll(double coefficients[], double initial) throws + ConvergenceException, FunctionEvaluationException { + + Complex c[] = new Complex[coefficients.length]; + Complex z = new Complex(initial, 0.0); + for (int i = 0; i < c.length; i++) { + c[i] = new Complex(coefficients[i], 0.0); + } + return solveAll(c, z); + } + + /** + * Find all complex roots for the polynomial with the given coefficients, + * starting from the given initial value. + * + * @param coefficients the polynomial coefficients array + * @param initial the start value to use + * @return the point at which the function value is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + * @deprecated in 2.2. + */ + @Deprecated + public Complex[] solveAll(Complex coefficients[], Complex initial) throws + MaxIterationsExceededException, FunctionEvaluationException { + + int n = coefficients.length - 1; + int iterationCount = 0; + if (n < 1) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.NON_POSITIVE_POLYNOMIAL_DEGREE, n); + } + Complex c[] = new Complex[n+1]; // coefficients for deflated polynomial + for (int i = 0; i <= n; i++) { + c[i] = coefficients[i]; + } + + // solve individual root successively + Complex root[] = new Complex[n]; + for (int i = 0; i < n; i++) { + Complex subarray[] = new Complex[n-i+1]; + System.arraycopy(c, 0, subarray, 0, subarray.length); + root[i] = solve(subarray, initial); + // polynomial deflation using synthetic division + Complex newc = c[n-i]; + Complex oldc = null; + for (int j = n-i-1; j >= 0; j--) { + oldc = c[j]; + c[j] = newc; + newc = oldc.add(newc.multiply(root[i])); + } + iterationCount += this.iterationCount; + } + + resultComputed = true; + this.iterationCount = iterationCount; + return root; + } + + /** + * Find a complex root for the polynomial with the given coefficients, + * starting from the given initial value. + * + * @param coefficients the polynomial coefficients array + * @param initial the start value to use + * @return the point at which the function value is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + * @deprecated in 2.2. + */ + @Deprecated + public Complex solve(Complex coefficients[], Complex initial) throws + MaxIterationsExceededException, FunctionEvaluationException { + + int n = coefficients.length - 1; + if (n < 1) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.NON_POSITIVE_POLYNOMIAL_DEGREE, n); + } + Complex N = new Complex(n, 0.0); + Complex N1 = new Complex(n - 1, 0.0); + + int i = 1; + Complex pv = null; + Complex dv = null; + Complex d2v = null; + Complex G = null; + Complex G2 = null; + Complex H = null; + Complex delta = null; + Complex denominator = null; + Complex z = initial; + Complex oldz = new Complex(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); + while (i <= maximalIterationCount) { + // Compute pv (polynomial value), dv (derivative value), and + // d2v (second derivative value) simultaneously. + pv = coefficients[n]; + dv = Complex.ZERO; + d2v = Complex.ZERO; + for (int j = n-1; j >= 0; j--) { + d2v = dv.add(z.multiply(d2v)); + dv = pv.add(z.multiply(dv)); + pv = coefficients[j].add(z.multiply(pv)); + } + d2v = d2v.multiply(new Complex(2.0, 0.0)); + + // check for convergence + double tolerance = FastMath.max(relativeAccuracy * z.abs(), + absoluteAccuracy); + if ((z.subtract(oldz)).abs() <= tolerance) { + resultComputed = true; + iterationCount = i; + return z; + } + if (pv.abs() <= functionValueAccuracy) { + resultComputed = true; + iterationCount = i; + return z; + } + + // now pv != 0, calculate the new approximation + G = dv.divide(pv); + G2 = G.multiply(G); + H = G2.subtract(d2v.divide(pv)); + delta = N1.multiply((N.multiply(H)).subtract(G2)); + // choose a denominator larger in magnitude + Complex deltaSqrt = delta.sqrt(); + Complex dplus = G.add(deltaSqrt); + Complex dminus = G.subtract(deltaSqrt); + denominator = dplus.abs() > dminus.abs() ? dplus : dminus; + // Perturb z if denominator is zero, for instance, + // p(x) = x^3 + 1, z = 0. + if (denominator.equals(new Complex(0.0, 0.0))) { + z = z.add(new Complex(absoluteAccuracy, absoluteAccuracy)); + oldz = new Complex(Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY); + } else { + oldz = z; + z = z.subtract(N.divide(denominator)); + } + i++; + } + throw new MaxIterationsExceededException(maximalIterationCount); + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/MullerSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/MullerSolver.java new file mode 100644 index 0000000..a6d03bc --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/MullerSolver.java @@ -0,0 +1,415 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.solvers; + +import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MaxIterationsExceededException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.util.FastMath; +import org.apache.commons.math.util.MathUtils; + +/** + * Implements the <a href="http://mathworld.wolfram.com/MullersMethod.html"> + * Muller's Method</a> for root finding of real univariate functions. For + * reference, see <b>Elementary Numerical Analysis</b>, ISBN 0070124477, + * chapter 3. + * <p> + * Muller's method applies to both real and complex functions, but here we + * restrict ourselves to real functions. Methods solve() and solve2() find + * real zeros, using different ways to bypass complex arithmetics.</p> + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + * @since 1.2 + */ +public class MullerSolver extends UnivariateRealSolverImpl { + + /** + * Construct a solver for the given function. + * + * @param f function to solve + * @deprecated as of 2.0 the function to solve is passed as an argument + * to the {@link #solve(UnivariateRealFunction, double, double)} or + * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)} + * method. + */ + @Deprecated + public MullerSolver(UnivariateRealFunction f) { + super(f, 100, 1E-6); + } + + /** + * Construct a solver. + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public MullerSolver() { + super(100, 1E-6); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(final double min, final double max) + throws ConvergenceException, FunctionEvaluationException { + return solve(f, min, max); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(final double min, final double max, final double initial) + throws ConvergenceException, FunctionEvaluationException { + return solve(f, min, max, initial); + } + + /** + * Find a real root in the given interval with initial value. + * <p> + * Requires bracketing condition.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param initial the start value to use + * @param maxEval Maximum number of evaluations. + * @return the point at which the function value is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, + final double min, final double max, final double initial) + throws MaxIterationsExceededException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max, initial); + } + + /** + * Find a real root in the given interval with initial value. + * <p> + * Requires bracketing condition.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param initial the start value to use + * @return the point at which the function value is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, + final double min, final double max, final double initial) + throws MaxIterationsExceededException, FunctionEvaluationException { + + // check for zeros before verifying bracketing + if (f.value(min) == 0.0) { return min; } + if (f.value(max) == 0.0) { return max; } + if (f.value(initial) == 0.0) { return initial; } + + verifyBracketing(min, max, f); + verifySequence(min, initial, max); + if (isBracketing(min, initial, f)) { + return solve(f, min, initial); + } else { + return solve(f, initial, max); + } + } + + /** + * Find a real root in the given interval. + * <p> + * Original Muller's method would have function evaluation at complex point. + * Since our f(x) is real, we have to find ways to avoid that. Bracketing + * condition is one way to go: by requiring bracketing in every iteration, + * the newly computed approximation is guaranteed to be real.</p> + * <p> + * Normally Muller's method converges quadratically in the vicinity of a + * zero, however it may be very slow in regions far away from zeros. For + * example, f(x) = exp(x) - 1, min = -50, max = 100. In such case we use + * bisection as a safety backup if it performs very poorly.</p> + * <p> + * The formulas here use divided differences directly.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param maxEval Maximum number of evaluations. + * @return the point at which the function value is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, + final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max); + } + + /** + * Find a real root in the given interval. + * <p> + * Original Muller's method would have function evaluation at complex point. + * Since our f(x) is real, we have to find ways to avoid that. Bracketing + * condition is one way to go: by requiring bracketing in every iteration, + * the newly computed approximation is guaranteed to be real.</p> + * <p> + * Normally Muller's method converges quadratically in the vicinity of a + * zero, however it may be very slow in regions far away from zeros. For + * example, f(x) = exp(x) - 1, min = -50, max = 100. In such case we use + * bisection as a safety backup if it performs very poorly.</p> + * <p> + * The formulas here use divided differences directly.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @return the point at which the function value is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, + final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + + // [x0, x2] is the bracketing interval in each iteration + // x1 is the last approximation and an interpolation point in (x0, x2) + // x is the new root approximation and new x1 for next round + // d01, d12, d012 are divided differences + + double x0 = min; + double y0 = f.value(x0); + double x2 = max; + double y2 = f.value(x2); + double x1 = 0.5 * (x0 + x2); + double y1 = f.value(x1); + + // check for zeros before verifying bracketing + if (y0 == 0.0) { + return min; + } + if (y2 == 0.0) { + return max; + } + verifyBracketing(min, max, f); + + double oldx = Double.POSITIVE_INFINITY; + for (int i = 1; i <= maximalIterationCount; ++i) { + // Muller's method employs quadratic interpolation through + // x0, x1, x2 and x is the zero of the interpolating parabola. + // Due to bracketing condition, this parabola must have two + // real roots and we choose one in [x0, x2] to be x. + final double d01 = (y1 - y0) / (x1 - x0); + final double d12 = (y2 - y1) / (x2 - x1); + final double d012 = (d12 - d01) / (x2 - x0); + final double c1 = d01 + (x1 - x0) * d012; + final double delta = c1 * c1 - 4 * y1 * d012; + final double xplus = x1 + (-2.0 * y1) / (c1 + FastMath.sqrt(delta)); + final double xminus = x1 + (-2.0 * y1) / (c1 - FastMath.sqrt(delta)); + // xplus and xminus are two roots of parabola and at least + // one of them should lie in (x0, x2) + final double x = isSequence(x0, xplus, x2) ? xplus : xminus; + final double y = f.value(x); + + // check for convergence + final double tolerance = FastMath.max(relativeAccuracy * FastMath.abs(x), absoluteAccuracy); + if (FastMath.abs(x - oldx) <= tolerance) { + setResult(x, i); + return result; + } + if (FastMath.abs(y) <= functionValueAccuracy) { + setResult(x, i); + return result; + } + + // Bisect if convergence is too slow. Bisection would waste + // our calculation of x, hopefully it won't happen often. + // the real number equality test x == x1 is intentional and + // completes the proximity tests above it + boolean bisect = (x < x1 && (x1 - x0) > 0.95 * (x2 - x0)) || + (x > x1 && (x2 - x1) > 0.95 * (x2 - x0)) || + (x == x1); + // prepare the new bracketing interval for next iteration + if (!bisect) { + x0 = x < x1 ? x0 : x1; + y0 = x < x1 ? y0 : y1; + x2 = x > x1 ? x2 : x1; + y2 = x > x1 ? y2 : y1; + x1 = x; y1 = y; + oldx = x; + } else { + double xm = 0.5 * (x0 + x2); + double ym = f.value(xm); + if (MathUtils.sign(y0) + MathUtils.sign(ym) == 0.0) { + x2 = xm; y2 = ym; + } else { + x0 = xm; y0 = ym; + } + x1 = 0.5 * (x0 + x2); + y1 = f.value(x1); + oldx = Double.POSITIVE_INFINITY; + } + } + throw new MaxIterationsExceededException(maximalIterationCount); + } + + /** + * Find a real root in the given interval. + * <p> + * solve2() differs from solve() in the way it avoids complex operations. + * Except for the initial [min, max], solve2() does not require bracketing + * condition, e.g. f(x0), f(x1), f(x2) can have the same sign. If complex + * number arises in the computation, we simply use its modulus as real + * approximation.</p> + * <p> + * Because the interval may not be bracketing, bisection alternative is + * not applicable here. However in practice our treatment usually works + * well, especially near real zeros where the imaginary part of complex + * approximation is often negligible.</p> + * <p> + * The formulas here do not use divided differences directly.</p> + * + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @return the point at which the function value is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + * @deprecated replaced by {@link #solve2(UnivariateRealFunction, double, double)} + * since 2.0 + */ + @Deprecated + public double solve2(final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + return solve2(f, min, max); + } + + /** + * Find a real root in the given interval. + * <p> + * solve2() differs from solve() in the way it avoids complex operations. + * Except for the initial [min, max], solve2() does not require bracketing + * condition, e.g. f(x0), f(x1), f(x2) can have the same sign. If complex + * number arises in the computation, we simply use its modulus as real + * approximation.</p> + * <p> + * Because the interval may not be bracketing, bisection alternative is + * not applicable here. However in practice our treatment usually works + * well, especially near real zeros where the imaginary part of complex + * approximation is often negligible.</p> + * <p> + * The formulas here do not use divided differences directly.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @return the point at which the function value is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve2(final UnivariateRealFunction f, + final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + + // x2 is the last root approximation + // x is the new approximation and new x2 for next round + // x0 < x1 < x2 does not hold here + + double x0 = min; + double y0 = f.value(x0); + double x1 = max; + double y1 = f.value(x1); + double x2 = 0.5 * (x0 + x1); + double y2 = f.value(x2); + + // check for zeros before verifying bracketing + if (y0 == 0.0) { return min; } + if (y1 == 0.0) { return max; } + verifyBracketing(min, max, f); + + double oldx = Double.POSITIVE_INFINITY; + for (int i = 1; i <= maximalIterationCount; ++i) { + // quadratic interpolation through x0, x1, x2 + final double q = (x2 - x1) / (x1 - x0); + final double a = q * (y2 - (1 + q) * y1 + q * y0); + final double b = (2 * q + 1) * y2 - (1 + q) * (1 + q) * y1 + q * q * y0; + final double c = (1 + q) * y2; + final double delta = b * b - 4 * a * c; + double x; + final double denominator; + if (delta >= 0.0) { + // choose a denominator larger in magnitude + double dplus = b + FastMath.sqrt(delta); + double dminus = b - FastMath.sqrt(delta); + denominator = FastMath.abs(dplus) > FastMath.abs(dminus) ? dplus : dminus; + } else { + // take the modulus of (B +/- FastMath.sqrt(delta)) + denominator = FastMath.sqrt(b * b - delta); + } + if (denominator != 0) { + x = x2 - 2.0 * c * (x2 - x1) / denominator; + // perturb x if it exactly coincides with x1 or x2 + // the equality tests here are intentional + while (x == x1 || x == x2) { + x += absoluteAccuracy; + } + } else { + // extremely rare case, get a random number to skip it + x = min + FastMath.random() * (max - min); + oldx = Double.POSITIVE_INFINITY; + } + final double y = f.value(x); + + // check for convergence + final double tolerance = FastMath.max(relativeAccuracy * FastMath.abs(x), absoluteAccuracy); + if (FastMath.abs(x - oldx) <= tolerance) { + setResult(x, i); + return result; + } + if (FastMath.abs(y) <= functionValueAccuracy) { + setResult(x, i); + return result; + } + + // prepare the next iteration + x0 = x1; + y0 = y1; + x1 = x2; + y1 = y2; + x2 = x; + y2 = y; + oldx = x; + } + throw new MaxIterationsExceededException(maximalIterationCount); + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/NewtonSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/NewtonSolver.java new file mode 100644 index 0000000..a2cffff --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/NewtonSolver.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math.analysis.solvers; + +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.MaxIterationsExceededException; +import org.apache.commons.math.analysis.DifferentiableUnivariateRealFunction; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.util.FastMath; + +/** + * Implements <a href="http://mathworld.wolfram.com/NewtonsMethod.html"> + * Newton's Method</a> for finding zeros of real univariate functions. + * <p> + * The function should be continuous but not necessarily smooth.</p> + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + */ +public class NewtonSolver extends UnivariateRealSolverImpl { + + /** + * Construct a solver for the given function. + * @param f function to solve. + * @deprecated as of 2.0 the function to solve is passed as an argument + * to the {@link #solve(UnivariateRealFunction, double, double)} or + * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)} + * method. + */ + @Deprecated + public NewtonSolver(DifferentiableUnivariateRealFunction f) { + super(f, 100, 1E-6); + } + + /** + * Construct a solver. + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public NewtonSolver() { + super(100, 1E-6); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + return solve(f, min, max); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(final double min, final double max, final double startValue) + throws MaxIterationsExceededException, FunctionEvaluationException { + return solve(f, min, max, startValue); + } + + /** + * Find a zero near the midpoint of <code>min</code> and <code>max</code>. + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param maxEval Maximum number of evaluations. + * @return the value where the function is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function or derivative + * @throws IllegalArgumentException if min is not less than max + */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, + final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max); + } + + /** + * Find a zero near the midpoint of <code>min</code> and <code>max</code>. + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @return the value where the function is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function or derivative + * @throws IllegalArgumentException if min is not less than max + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, + final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + return solve(f, min, max, UnivariateRealSolverUtils.midpoint(min, max)); + } + + /** + * Find a zero near the value <code>startValue</code>. + * + * @param f the function to solve + * @param min the lower bound for the interval (ignored). + * @param max the upper bound for the interval (ignored). + * @param startValue the start value to use. + * @param maxEval Maximum number of evaluations. + * @return the value where the function is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function or derivative + * @throws IllegalArgumentException if startValue is not between min and max or + * if function is not a {@link DifferentiableUnivariateRealFunction} instance + */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, + final double min, final double max, final double startValue) + throws MaxIterationsExceededException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max, startValue); + } + + /** + * Find a zero near the value <code>startValue</code>. + * + * @param f the function to solve + * @param min the lower bound for the interval (ignored). + * @param max the upper bound for the interval (ignored). + * @param startValue the start value to use. + * @return the value where the function is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function or derivative + * @throws IllegalArgumentException if startValue is not between min and max or + * if function is not a {@link DifferentiableUnivariateRealFunction} instance + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, + final double min, final double max, final double startValue) + throws MaxIterationsExceededException, FunctionEvaluationException { + + try { + + final UnivariateRealFunction derivative = + ((DifferentiableUnivariateRealFunction) f).derivative(); + clearResult(); + verifySequence(min, startValue, max); + + double x0 = startValue; + double x1; + + int i = 0; + while (i < maximalIterationCount) { + + x1 = x0 - (f.value(x0) / derivative.value(x0)); + if (FastMath.abs(x1 - x0) <= absoluteAccuracy) { + setResult(x1, i); + return x1; + } + + x0 = x1; + ++i; + } + + throw new MaxIterationsExceededException(maximalIterationCount); + } catch (ClassCastException cce) { + throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.FUNCTION_NOT_DIFFERENTIABLE); + } + } + +} diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/RiddersSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/RiddersSolver.java new file mode 100644 index 0000000..4f316c7 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/RiddersSolver.java @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.solvers; + +import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MaxIterationsExceededException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.util.FastMath; +import org.apache.commons.math.util.MathUtils; + +/** + * Implements the <a href="http://mathworld.wolfram.com/RiddersMethod.html"> + * Ridders' Method</a> for root finding of real univariate functions. For + * reference, see C. Ridders, <i>A new algorithm for computing a single root + * of a real continuous function </i>, IEEE Transactions on Circuits and + * Systems, 26 (1979), 979 - 980. + * <p> + * The function should be continuous but not necessarily smooth.</p> + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + * @since 1.2 + */ +public class RiddersSolver extends UnivariateRealSolverImpl { + + /** + * Construct a solver for the given function. + * + * @param f function to solve + * @deprecated as of 2.0 the function to solve is passed as an argument + * to the {@link #solve(UnivariateRealFunction, double, double)} or + * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)} + * method. + */ + @Deprecated + public RiddersSolver(UnivariateRealFunction f) { + super(f, 100, 1E-6); + } + + /** + * Construct a solver. + * @deprecated in 2.2 + */ + @Deprecated + public RiddersSolver() { + super(100, 1E-6); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(final double min, final double max) + throws ConvergenceException, FunctionEvaluationException { + return solve(f, min, max); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(final double min, final double max, final double initial) + throws ConvergenceException, FunctionEvaluationException { + return solve(f, min, max, initial); + } + + /** + * Find a root in the given interval with initial value. + * <p> + * Requires bracketing condition.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param initial the start value to use + * @param maxEval Maximum number of evaluations. + * @return the point at which the function value is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, + final double min, final double max, final double initial) + throws MaxIterationsExceededException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max, initial); + } + + /** + * Find a root in the given interval with initial value. + * <p> + * Requires bracketing condition.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param initial the start value to use + * @return the point at which the function value is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, + final double min, final double max, final double initial) + throws MaxIterationsExceededException, FunctionEvaluationException { + + // check for zeros before verifying bracketing + if (f.value(min) == 0.0) { return min; } + if (f.value(max) == 0.0) { return max; } + if (f.value(initial) == 0.0) { return initial; } + + verifyBracketing(min, max, f); + verifySequence(min, initial, max); + if (isBracketing(min, initial, f)) { + return solve(f, min, initial); + } else { + return solve(f, initial, max); + } + } + + /** + * Find a root in the given interval. + * <p> + * Requires bracketing condition.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param maxEval Maximum number of evaluations. + * @return the point at which the function value is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, + final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max); + } + + /** + * Find a root in the given interval. + * <p> + * Requires bracketing condition.</p> + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @return the point at which the function value is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if any parameters are invalid + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, + final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + + // [x1, x2] is the bracketing interval in each iteration + // x3 is the midpoint of [x1, x2] + // x is the new root approximation and an endpoint of the new interval + double x1 = min; + double y1 = f.value(x1); + double x2 = max; + double y2 = f.value(x2); + + // check for zeros before verifying bracketing + if (y1 == 0.0) { + return min; + } + if (y2 == 0.0) { + return max; + } + verifyBracketing(min, max, f); + + int i = 1; + double oldx = Double.POSITIVE_INFINITY; + while (i <= maximalIterationCount) { + // calculate the new root approximation + final double x3 = 0.5 * (x1 + x2); + final double y3 = f.value(x3); + if (FastMath.abs(y3) <= functionValueAccuracy) { + setResult(x3, i); + return result; + } + final double delta = 1 - (y1 * y2) / (y3 * y3); // delta > 1 due to bracketing + final double correction = (MathUtils.sign(y2) * MathUtils.sign(y3)) * + (x3 - x1) / FastMath.sqrt(delta); + final double x = x3 - correction; // correction != 0 + final double y = f.value(x); + + // check for convergence + final double tolerance = FastMath.max(relativeAccuracy * FastMath.abs(x), absoluteAccuracy); + if (FastMath.abs(x - oldx) <= tolerance) { + setResult(x, i); + return result; + } + if (FastMath.abs(y) <= functionValueAccuracy) { + setResult(x, i); + return result; + } + + // prepare the new interval for next iteration + // Ridders' method guarantees x1 < x < x2 + if (correction > 0.0) { // x1 < x < x3 + if (MathUtils.sign(y1) + MathUtils.sign(y) == 0.0) { + x2 = x; + y2 = y; + } else { + x1 = x; + x2 = x3; + y1 = y; + y2 = y3; + } + } else { // x3 < x < x2 + if (MathUtils.sign(y2) + MathUtils.sign(y) == 0.0) { + x1 = x; + y1 = y; + } else { + x1 = x3; + x2 = x; + y1 = y3; + y2 = y; + } + } + oldx = x; + i++; + } + throw new MaxIterationsExceededException(maximalIterationCount); + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/SecantSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/SecantSolver.java new file mode 100644 index 0000000..325b662 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/SecantSolver.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.solvers; + +import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.MaxIterationsExceededException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.util.FastMath; + + +/** + * Implements a modified version of the + * <a href="http://mathworld.wolfram.com/SecantMethod.html">secant method</a> + * for approximating a zero of a real univariate function. + * <p> + * The algorithm is modified to maintain bracketing of a root by successive + * approximations. Because of forced bracketing, convergence may be slower than + * the unrestricted secant algorithm. However, this implementation should in + * general outperform the + * <a href="http://mathworld.wolfram.com/MethodofFalsePosition.html"> + * regula falsi method.</a></p> + * <p> + * The function is assumed to be continuous but not necessarily smooth.</p> + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + */ +public class SecantSolver extends UnivariateRealSolverImpl { + + /** + * Construct a solver for the given function. + * @param f function to solve. + * @deprecated as of 2.0 the function to solve is passed as an argument + * to the {@link #solve(UnivariateRealFunction, double, double)} or + * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)} + * method. + */ + @Deprecated + public SecantSolver(UnivariateRealFunction f) { + super(f, 100, 1E-6); + } + + /** + * Construct a solver. + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public SecantSolver() { + super(100, 1E-6); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(final double min, final double max) + throws ConvergenceException, FunctionEvaluationException { + return solve(f, min, max); + } + + /** {@inheritDoc} */ + @Deprecated + public double solve(final double min, final double max, final double initial) + throws ConvergenceException, FunctionEvaluationException { + return solve(f, min, max, initial); + } + + /** + * Find a zero in the given interval. + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param initial the start value to use (ignored) + * @param maxEval Maximum number of evaluations. + * @return the value where the function is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min is not less than max or the + * signs of the values of the function at the endpoints are not opposites + */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, + final double min, final double max, final double initial) + throws MaxIterationsExceededException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max, initial); + } + + /** + * Find a zero in the given interval. + * + * @param f the function to solve + * @param min the lower bound for the interval + * @param max the upper bound for the interval + * @param initial the start value to use (ignored) + * @return the value where the function is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min is not less than max or the + * signs of the values of the function at the endpoints are not opposites + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, + final double min, final double max, final double initial) + throws MaxIterationsExceededException, FunctionEvaluationException { + return solve(f, min, max); + } + + /** + * Find a zero in the given interval. + * @param f the function to solve + * @param min the lower bound for the interval. + * @param max the upper bound for the interval. + * @param maxEval Maximum number of evaluations. + * @return the value where the function is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min is not less than max or the + * signs of the values of the function at the endpoints are not opposites + */ + @Override + public double solve(int maxEval, final UnivariateRealFunction f, + final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + setMaximalIterationCount(maxEval); + return solve(f, min, max); + } + + /** + * Find a zero in the given interval. + * @param f the function to solve + * @param min the lower bound for the interval. + * @param max the upper bound for the interval. + * @return the value where the function is zero + * @throws MaxIterationsExceededException if the maximum iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min is not less than max or the + * signs of the values of the function at the endpoints are not opposites + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + public double solve(final UnivariateRealFunction f, + final double min, final double max) + throws MaxIterationsExceededException, FunctionEvaluationException { + + clearResult(); + verifyInterval(min, max); + + // Index 0 is the old approximation for the root. + // Index 1 is the last calculated approximation for the root. + // Index 2 is a bracket for the root with respect to x0. + // OldDelta is the length of the bracketing interval of the last + // iteration. + double x0 = min; + double x1 = max; + double y0 = f.value(x0); + double y1 = f.value(x1); + + // Verify bracketing + if (y0 * y1 >= 0) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.SAME_SIGN_AT_ENDPOINTS, min, max, y0, y1); + } + + double x2 = x0; + double y2 = y0; + double oldDelta = x2 - x1; + int i = 0; + while (i < maximalIterationCount) { + if (FastMath.abs(y2) < FastMath.abs(y1)) { + x0 = x1; + x1 = x2; + x2 = x0; + y0 = y1; + y1 = y2; + y2 = y0; + } + if (FastMath.abs(y1) <= functionValueAccuracy) { + setResult(x1, i); + return result; + } + if (FastMath.abs(oldDelta) < + FastMath.max(relativeAccuracy * FastMath.abs(x1), absoluteAccuracy)) { + setResult(x1, i); + return result; + } + double delta; + if (FastMath.abs(y1) > FastMath.abs(y0)) { + // Function value increased in last iteration. Force bisection. + delta = 0.5 * oldDelta; + } else { + delta = (x0 - x1) / (1 - y0 / y1); + if (delta / oldDelta > 1) { + // New approximation falls outside bracket. + // Fall back to bisection. + delta = 0.5 * oldDelta; + } + } + x0 = x1; + y0 = y1; + x1 = x1 + delta; + y1 = f.value(x1); + if ((y1 > 0) == (y2 > 0)) { + // New bracket is (x0,x1). + x2 = x0; + y2 = y0; + } + oldDelta = x2 - x1; + i++; + } + throw new MaxIterationsExceededException(maximalIterationCount); + } + +} diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolver.java new file mode 100644 index 0000000..6540f67 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolver.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.solvers; + +import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.ConvergingAlgorithm; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.analysis.UnivariateRealFunction; + + +/** + * Interface for (univariate real) rootfinding algorithms. + * <p> + * Implementations will search for only one zero in the given interval.</p> + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + */ +public interface UnivariateRealSolver extends ConvergingAlgorithm { + + /** + * Set the function value accuracy. + * <p> + * This is used to determine when an evaluated function value or some other + * value which is used as divisor is zero.</p> + * <p> + * This is a safety guard and it shouldn't be necessary to change this in + * general.</p> + * + * @param accuracy the accuracy. + * @throws IllegalArgumentException if the accuracy can't be achieved by + * the solver or is otherwise deemed unreasonable. + */ + void setFunctionValueAccuracy(double accuracy); + + /** + * Get the actual function value accuracy. + * @return the accuracy + */ + double getFunctionValueAccuracy(); + + /** + * Reset the actual function accuracy to the default. + * The default value is provided by the solver implementation. + */ + void resetFunctionValueAccuracy(); + + /** + * Solve for a zero root in the given interval. + * <p>A solver may require that the interval brackets a single zero root. + * Solvers that do require bracketing should be able to handle the case + * where one of the endpoints is itself a root.</p> + * + * @param min the lower bound for the interval. + * @param max the upper bound for the interval. + * @return a value where the function is zero + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise. + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min > max or the endpoints do not + * satisfy the requirements specified by the solver + * @deprecated replaced by {@link #solve(UnivariateRealFunction, double, double)} + * since 2.0 + */ + @Deprecated + double solve(double min, double max) throws ConvergenceException, FunctionEvaluationException; + + /** + * Solve for a zero root in the given interval. + * <p>A solver may require that the interval brackets a single zero root. + * Solvers that do require bracketing should be able to handle the case + * where one of the endpoints is itself a root.</p> + * + * @param f the function to solve. + * @param min the lower bound for the interval. + * @param max the upper bound for the interval. + * @return a value where the function is zero + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise. + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min > max or the endpoints do not + * satisfy the requirements specified by the solver + * @since 2.0 + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + double solve(UnivariateRealFunction f, double min, double max) + throws ConvergenceException, FunctionEvaluationException; + + /** + * Solve for a zero in the given interval, start at startValue. + * <p>A solver may require that the interval brackets a single zero root. + * Solvers that do require bracketing should be able to handle the case + * where one of the endpoints is itself a root.</p> + * + * @param min the lower bound for the interval. + * @param max the upper bound for the interval. + * @param startValue the start value to use + * @return a value where the function is zero + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise. + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min > max or the arguments do not + * satisfy the requirements specified by the solver + * @deprecated replaced by {@link #solve(UnivariateRealFunction, double, double, double)} + * since 2.0 + */ + @Deprecated + double solve(double min, double max, double startValue) + throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException; + + /** + * Solve for a zero in the given interval, start at startValue. + * <p>A solver may require that the interval brackets a single zero root. + * Solvers that do require bracketing should be able to handle the case + * where one of the endpoints is itself a root.</p> + * + * @param f the function to solve. + * @param min the lower bound for the interval. + * @param max the upper bound for the interval. + * @param startValue the start value to use + * @return a value where the function is zero + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise. + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min > max or the arguments do not + * satisfy the requirements specified by the solver + * @since 2.0 + * @deprecated in 2.2 (to be removed in 3.0). + */ + @Deprecated + double solve(UnivariateRealFunction f, double min, double max, double startValue) + throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException; + + /** + * Get the result of the last run of the solver. + * + * @return the last result. + * @throws IllegalStateException if there is no result available, either + * because no result was yet computed or the last attempt failed. + */ + double getResult(); + + /** + * Get the result of the last run of the solver. + * + * @return the value of the function at the last result. + * @throws IllegalStateException if there is no result available, either + * because no result was yet computed or the last attempt failed. + */ + double getFunctionValue(); +} diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactory.java b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactory.java new file mode 100644 index 0000000..46b7234 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactory.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.solvers; + +/** + * Abstract factory class used to create {@link UnivariateRealSolver} instances. + * <p> + * Solvers implementing the following algorithms are supported: + * <ul> + * <li>Bisection</li> + * <li>Brent's method</li> + * <li>Secant method</li> + * </ul> + * Concrete factories extending this class also specify a default solver, instances of which + * are returned by <code>newDefaultSolver()</code>.</p> + * <p> + * Common usage:<pre> + * SolverFactory factory = UnivariateRealSolverFactory.newInstance();</p> + * + * // create a Brent solver to use + * BrentSolver solver = factory.newBrentSolver(); + * </pre> + * + * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ + */ +public abstract class UnivariateRealSolverFactory { + /** + * Default constructor. + */ + protected UnivariateRealSolverFactory() { + } + + /** + * Create a new factory. + * @return a new factory. + */ + public static UnivariateRealSolverFactory newInstance() { + return new UnivariateRealSolverFactoryImpl(); + } + + /** + * Create a new {@link UnivariateRealSolver}. The + * actual solver returned is determined by the underlying factory. + * @return the new solver. + */ + public abstract UnivariateRealSolver newDefaultSolver(); + + /** + * Create a new {@link UnivariateRealSolver}. The + * solver is an implementation of the bisection method. + * @return the new solver. + */ + public abstract UnivariateRealSolver newBisectionSolver(); + + /** + * Create a new {@link UnivariateRealSolver}. The + * solver is an implementation of the Brent method. + * @return the new solver. + */ + public abstract UnivariateRealSolver newBrentSolver(); + + /** + * Create a new {@link UnivariateRealSolver}. The + * solver is an implementation of Newton's Method. + * @return the new solver. + */ + public abstract UnivariateRealSolver newNewtonSolver(); + + /** + * Create a new {@link UnivariateRealSolver}. The + * solver is an implementation of the secant method. + * @return the new solver. + */ + public abstract UnivariateRealSolver newSecantSolver(); + +} diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactoryImpl.java b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactoryImpl.java new file mode 100644 index 0000000..cb4c6b2 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactoryImpl.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.solvers; + +/** + * A concrete {@link UnivariateRealSolverFactory}. This is the default solver factory + * used by commons-math. + * <p> + * The default solver returned by this factory is a {@link BrentSolver}.</p> + * + * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ + */ +public class UnivariateRealSolverFactoryImpl extends UnivariateRealSolverFactory { + + /** + * Default constructor. + */ + public UnivariateRealSolverFactoryImpl() { + } + + /** {@inheritDoc} */ + @Override + public UnivariateRealSolver newDefaultSolver() { + return newBrentSolver(); + } + + /** {@inheritDoc} */ + @Override + public UnivariateRealSolver newBisectionSolver() { + return new BisectionSolver(); + } + + /** {@inheritDoc} */ + @Override + public UnivariateRealSolver newBrentSolver() { + return new BrentSolver(); + } + + /** {@inheritDoc} */ + @Override + public UnivariateRealSolver newNewtonSolver() { + return new NewtonSolver(); + } + + /** {@inheritDoc} */ + @Override + public UnivariateRealSolver newSecantSolver() { + return new SecantSolver(); + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverImpl.java b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverImpl.java new file mode 100644 index 0000000..557c767 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverImpl.java @@ -0,0 +1,304 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math.analysis.solvers; + +import org.apache.commons.math.ConvergingAlgorithmImpl; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.exception.NullArgumentException; + +/** + * Provide a default implementation for several functions useful to generic + * solvers. + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + * @deprecated in 2.2 (to be removed in 3.0). + */ +@Deprecated +public abstract class UnivariateRealSolverImpl + extends ConvergingAlgorithmImpl implements UnivariateRealSolver { + + /** Maximum error of function. */ + protected double functionValueAccuracy; + + /** Default maximum error of function. */ + protected double defaultFunctionValueAccuracy; + + /** Indicates where a root has been computed. */ + protected boolean resultComputed = false; + + /** The last computed root. */ + protected double result; + + /** Value of the function at the last computed result. */ + protected double functionValue; + + /** The function to solve. + * @deprecated as of 2.0 the function to solve is passed as an argument + * to the {@link #solve(UnivariateRealFunction, double, double)} or + * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)} + * method. */ + @Deprecated + protected UnivariateRealFunction f; + + /** + * Construct a solver with given iteration count and accuracy. + * + * @param f the function to solve. + * @param defaultAbsoluteAccuracy maximum absolute error + * @param defaultMaximalIterationCount maximum number of iterations + * @throws IllegalArgumentException if f is null or the + * defaultAbsoluteAccuracy is not valid + * @deprecated as of 2.0 the function to solve is passed as an argument + * to the {@link #solve(UnivariateRealFunction, double, double)} or + * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)} + * method. + */ + @Deprecated + protected UnivariateRealSolverImpl(final UnivariateRealFunction f, + final int defaultMaximalIterationCount, + final double defaultAbsoluteAccuracy) { + super(defaultMaximalIterationCount, defaultAbsoluteAccuracy); + if (f == null) { + throw new NullArgumentException(LocalizedFormats.FUNCTION); + } + this.f = f; + this.defaultFunctionValueAccuracy = 1.0e-15; + this.functionValueAccuracy = defaultFunctionValueAccuracy; + } + + /** + * Construct a solver with given iteration count and accuracy. + * + * @param defaultAbsoluteAccuracy maximum absolute error + * @param defaultMaximalIterationCount maximum number of iterations + * @throws IllegalArgumentException if f is null or the + * defaultAbsoluteAccuracy is not valid + */ + protected UnivariateRealSolverImpl(final int defaultMaximalIterationCount, + final double defaultAbsoluteAccuracy) { + super(defaultMaximalIterationCount, defaultAbsoluteAccuracy); + this.defaultFunctionValueAccuracy = 1.0e-15; + this.functionValueAccuracy = defaultFunctionValueAccuracy; + } + + /** Check if a result has been computed. + * @exception IllegalStateException if no result has been computed + */ + protected void checkResultComputed() throws IllegalStateException { + if (!resultComputed) { + throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_RESULT_AVAILABLE); + } + } + + /** {@inheritDoc} */ + public double getResult() { + checkResultComputed(); + return result; + } + + /** {@inheritDoc} */ + public double getFunctionValue() { + checkResultComputed(); + return functionValue; + } + + /** {@inheritDoc} */ + public void setFunctionValueAccuracy(final double accuracy) { + functionValueAccuracy = accuracy; + } + + /** {@inheritDoc} */ + public double getFunctionValueAccuracy() { + return functionValueAccuracy; + } + + /** {@inheritDoc} */ + public void resetFunctionValueAccuracy() { + functionValueAccuracy = defaultFunctionValueAccuracy; + } + + /** + * Solve for a zero root in the given interval. + * <p>A solver may require that the interval brackets a single zero root. + * Solvers that do require bracketing should be able to handle the case + * where one of the endpoints is itself a root.</p> + * + * @param function the function to solve. + * @param min the lower bound for the interval. + * @param max the upper bound for the interval. + * @param maxEval Maximum number of evaluations. + * @return a value where the function is zero + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise. + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min > max or the endpoints do not + * satisfy the requirements specified by the solver + * @since 2.2 + */ + public double solve(int maxEval, UnivariateRealFunction function, double min, double max) + throws ConvergenceException, FunctionEvaluationException { + throw MathRuntimeException.createUnsupportedOperationException(LocalizedFormats.NOT_OVERRIDEN); + } + + /** + * Solve for a zero in the given interval, start at startValue. + * <p>A solver may require that the interval brackets a single zero root. + * Solvers that do require bracketing should be able to handle the case + * where one of the endpoints is itself a root.</p> + * + * @param function the function to solve. + * @param min the lower bound for the interval. + * @param max the upper bound for the interval. + * @param startValue the start value to use + * @param maxEval Maximum number of evaluations. + * @return a value where the function is zero + * @throws ConvergenceException if the maximum iteration count is exceeded + * or the solver detects convergence problems otherwise. + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if min > max or the arguments do not + * satisfy the requirements specified by the solver + * @since 2.2 + */ + public double solve(int maxEval, UnivariateRealFunction function, double min, double max, double startValue) + throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException { + throw MathRuntimeException.createUnsupportedOperationException(LocalizedFormats.NOT_OVERRIDEN); + } + + /** + * Convenience function for implementations. + * + * @param newResult the result to set + * @param iterationCount the iteration count to set + */ + protected final void setResult(final double newResult, final int iterationCount) { + this.result = newResult; + this.iterationCount = iterationCount; + this.resultComputed = true; + } + + /** + * Convenience function for implementations. + * + * @param x the result to set + * @param fx the result to set + * @param iterationCount the iteration count to set + */ + protected final void setResult(final double x, final double fx, + final int iterationCount) { + this.result = x; + this.functionValue = fx; + this.iterationCount = iterationCount; + this.resultComputed = true; + } + + /** + * Convenience function for implementations. + */ + protected final void clearResult() { + this.iterationCount = 0; + this.resultComputed = false; + } + + /** + * Returns true iff the function takes opposite signs at the endpoints. + * + * @param lower the lower endpoint + * @param upper the upper endpoint + * @param function the function + * @return true if f(lower) * f(upper) < 0 + * @throws FunctionEvaluationException if an error occurs evaluating the function at the endpoints + */ + protected boolean isBracketing(final double lower, final double upper, + final UnivariateRealFunction function) + throws FunctionEvaluationException { + final double f1 = function.value(lower); + final double f2 = function.value(upper); + return (f1 > 0 && f2 < 0) || (f1 < 0 && f2 > 0); + } + + /** + * Returns true if the arguments form a (strictly) increasing sequence + * + * @param start first number + * @param mid second number + * @param end third number + * @return true if the arguments form an increasing sequence + */ + protected boolean isSequence(final double start, final double mid, final double end) { + return (start < mid) && (mid < end); + } + + /** + * Verifies that the endpoints specify an interval, + * throws IllegalArgumentException if not + * + * @param lower lower endpoint + * @param upper upper endpoint + * @throws IllegalArgumentException + */ + protected void verifyInterval(final double lower, final double upper) { + if (lower >= upper) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.ENDPOINTS_NOT_AN_INTERVAL, + lower, upper); + } + } + + /** + * Verifies that <code>lower < initial < upper</code> + * throws IllegalArgumentException if not + * + * @param lower lower endpoint + * @param initial initial value + * @param upper upper endpoint + * @throws IllegalArgumentException + */ + protected void verifySequence(final double lower, final double initial, final double upper) { + if (!isSequence(lower, initial, upper)) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.INVALID_INTERVAL_INITIAL_VALUE_PARAMETERS, + lower, initial, upper); + } + } + + /** + * Verifies that the endpoints specify an interval and the function takes + * opposite signs at the endpoints, throws IllegalArgumentException if not + * + * @param lower lower endpoint + * @param upper upper endpoint + * @param function function + * @throws IllegalArgumentException + * @throws FunctionEvaluationException if an error occurs evaluating the function at the endpoints + */ + protected void verifyBracketing(final double lower, final double upper, + final UnivariateRealFunction function) + throws FunctionEvaluationException { + + verifyInterval(lower, upper); + if (!isBracketing(lower, upper, function)) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.SAME_SIGN_AT_ENDPOINTS, + lower, upper, function.value(lower), function.value(upper)); + } + } +} diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverUtils.java b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverUtils.java new file mode 100644 index 0000000..3186d6a --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverUtils.java @@ -0,0 +1,240 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math.analysis.solvers; + +import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.exception.util.LocalizedFormats; +import org.apache.commons.math.exception.NullArgumentException; +import org.apache.commons.math.util.FastMath; + +/** + * Utility routines for {@link UnivariateRealSolver} objects. + * + * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $ + */ +public class UnivariateRealSolverUtils { + + /** + * Default constructor. + */ + private UnivariateRealSolverUtils() { + super(); + } + + /** + * Convenience method to find a zero of a univariate real function. A default + * solver is used. + * + * @param f the function. + * @param x0 the lower bound for the interval. + * @param x1 the upper bound for the interval. + * @return a value where the function is zero. + * @throws ConvergenceException if the iteration count was exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if f is null or the endpoints do not + * specify a valid interval + */ + public static double solve(UnivariateRealFunction f, double x0, double x1) + throws ConvergenceException, FunctionEvaluationException { + setup(f); + return LazyHolder.FACTORY.newDefaultSolver().solve(f, x0, x1); + } + + /** + * Convenience method to find a zero of a univariate real function. A default + * solver is used. + * + * @param f the function + * @param x0 the lower bound for the interval + * @param x1 the upper bound for the interval + * @param absoluteAccuracy the accuracy to be used by the solver + * @return a value where the function is zero + * @throws ConvergenceException if the iteration count is exceeded + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if f is null, the endpoints do not + * specify a valid interval, or the absoluteAccuracy is not valid for the + * default solver + */ + public static double solve(UnivariateRealFunction f, double x0, double x1, + double absoluteAccuracy) throws ConvergenceException, + FunctionEvaluationException { + + setup(f); + UnivariateRealSolver solver = LazyHolder.FACTORY.newDefaultSolver(); + solver.setAbsoluteAccuracy(absoluteAccuracy); + return solver.solve(f, x0, x1); + } + + /** + * This method attempts to find two values a and b satisfying <ul> + * <li> <code> lowerBound <= a < initial < b <= upperBound</code> </li> + * <li> <code> f(a) * f(b) < 0 </code></li> + * </ul> + * If f is continuous on <code>[a,b],</code> this means that <code>a</code> + * and <code>b</code> bracket a root of f. + * <p> + * The algorithm starts by setting + * <code>a := initial -1; b := initial +1,</code> examines the value of the + * function at <code>a</code> and <code>b</code> and keeps moving + * the endpoints out by one unit each time through a loop that terminates + * when one of the following happens: <ul> + * <li> <code> f(a) * f(b) < 0 </code> -- success!</li> + * <li> <code> a = lower </code> and <code> b = upper</code> + * -- ConvergenceException </li> + * <li> <code> Integer.MAX_VALUE</code> iterations elapse + * -- ConvergenceException </li> + * </ul></p> + * <p> + * <strong>Note: </strong> this method can take + * <code>Integer.MAX_VALUE</code> iterations to throw a + * <code>ConvergenceException.</code> Unless you are confident that there + * is a root between <code>lowerBound</code> and <code>upperBound</code> + * near <code>initial,</code> it is better to use + * {@link #bracket(UnivariateRealFunction, double, double, double, int)}, + * explicitly specifying the maximum number of iterations.</p> + * + * @param function the function + * @param initial initial midpoint of interval being expanded to + * bracket a root + * @param lowerBound lower bound (a is never lower than this value) + * @param upperBound upper bound (b never is greater than this + * value) + * @return a two element array holding {a, b} + * @throws ConvergenceException if a root can not be bracketted + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if function is null, maximumIterations + * is not positive, or initial is not between lowerBound and upperBound + */ + public static double[] bracket(UnivariateRealFunction function, + double initial, double lowerBound, double upperBound) + throws ConvergenceException, FunctionEvaluationException { + return bracket( function, initial, lowerBound, upperBound, + Integer.MAX_VALUE ) ; + } + + /** + * This method attempts to find two values a and b satisfying <ul> + * <li> <code> lowerBound <= a < initial < b <= upperBound</code> </li> + * <li> <code> f(a) * f(b) <= 0 </code> </li> + * </ul> + * If f is continuous on <code>[a,b],</code> this means that <code>a</code> + * and <code>b</code> bracket a root of f. + * <p> + * The algorithm starts by setting + * <code>a := initial -1; b := initial +1,</code> examines the value of the + * function at <code>a</code> and <code>b</code> and keeps moving + * the endpoints out by one unit each time through a loop that terminates + * when one of the following happens: <ul> + * <li> <code> f(a) * f(b) <= 0 </code> -- success!</li> + * <li> <code> a = lower </code> and <code> b = upper</code> + * -- ConvergenceException </li> + * <li> <code> maximumIterations</code> iterations elapse + * -- ConvergenceException </li></ul></p> + * + * @param function the function + * @param initial initial midpoint of interval being expanded to + * bracket a root + * @param lowerBound lower bound (a is never lower than this value) + * @param upperBound upper bound (b never is greater than this + * value) + * @param maximumIterations maximum number of iterations to perform + * @return a two element array holding {a, b}. + * @throws ConvergenceException if the algorithm fails to find a and b + * satisfying the desired conditions + * @throws FunctionEvaluationException if an error occurs evaluating the function + * @throws IllegalArgumentException if function is null, maximumIterations + * is not positive, or initial is not between lowerBound and upperBound + */ + public static double[] bracket(UnivariateRealFunction function, + double initial, double lowerBound, double upperBound, + int maximumIterations) throws ConvergenceException, + FunctionEvaluationException { + + if (function == null) { + throw new NullArgumentException(LocalizedFormats.FUNCTION); + } + if (maximumIterations <= 0) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.INVALID_MAX_ITERATIONS, maximumIterations); + } + if (initial < lowerBound || initial > upperBound || lowerBound >= upperBound) { + throw MathRuntimeException.createIllegalArgumentException( + LocalizedFormats.INVALID_BRACKETING_PARAMETERS, + lowerBound, initial, upperBound); + } + double a = initial; + double b = initial; + double fa; + double fb; + int numIterations = 0 ; + + do { + a = FastMath.max(a - 1.0, lowerBound); + b = FastMath.min(b + 1.0, upperBound); + fa = function.value(a); + + fb = function.value(b); + numIterations++ ; + } while ((fa * fb > 0.0) && (numIterations < maximumIterations) && + ((a > lowerBound) || (b < upperBound))); + + if (fa * fb > 0.0 ) { + throw new ConvergenceException( + LocalizedFormats.FAILED_BRACKETING, + numIterations, maximumIterations, initial, + lowerBound, upperBound, a, b, fa, fb); + } + + return new double[]{a, b}; + } + + /** + * Compute the midpoint of two values. + * + * @param a first value. + * @param b second value. + * @return the midpoint. + */ + public static double midpoint(double a, double b) { + return (a + b) * .5; + } + + /** + * Checks to see if f is null, throwing IllegalArgumentException if so. + * @param f input function + * @throws IllegalArgumentException if f is null + */ + private static void setup(UnivariateRealFunction f) { + if (f == null) { + throw new NullArgumentException(LocalizedFormats.FUNCTION); + } + } + + // CHECKSTYLE: stop HideUtilityClassConstructor + /** Holder for the factory. + * <p>We use here the Initialization On Demand Holder Idiom.</p> + */ + private static class LazyHolder { + /** Cached solver factory */ + private static final UnivariateRealSolverFactory FACTORY = UnivariateRealSolverFactory.newInstance(); + } + // CHECKSTYLE: resume HideUtilityClassConstructor + +} diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/package.html b/src/main/java/org/apache/commons/math/analysis/solvers/package.html new file mode 100644 index 0000000..bbb49d1 --- /dev/null +++ b/src/main/java/org/apache/commons/math/analysis/solvers/package.html @@ -0,0 +1,22 @@ +<html> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ --> + <body> + Root finding algorithms, for univariate real functions. + </body> +</html> |