diff --git a/NOTICE.txt b/NOTICE.txt index 624e79105..938083a8b 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -8,6 +8,21 @@ The Apache Software Foundation (http://www.apache.org/). This product includes software translated from the lmder, lmpar and qrsolv Fortran routines from the Minpack package and distributed under the following disclaimer: + http://www.netlib.org/minpack/disclaimer + +This product includes software translated from the odex Fortran routine +developed by E. Hairer and G. Wanner and distributed under the following +license: + http://www.unige.ch/~hairer/prog/licence.txt + +This product includes software translated from some LAPACK Fortran routines +and distributed under the following license: +http://www.netlib.org/lapack/LICENSE + +For convenience, the text of these licenses and disclaimers as available at +time of code inclusion are provided below. + + ---------- http://www.netlib.org/minpack/disclaimer ---------- Minpack Copyright Notice (1999) University of Chicago. All rights reserved @@ -65,10 +80,6 @@ POSSIBILITY OF SUCH LOSS OR DAMAGES. -This product includes software translated from the odex Fortran routine -developed by E. Hairer and G. Wanner and distributed under the following -license: - ---------- http://www.unige.ch/~hairer/prog/licence.txt ---------- Copyright (c) 2004, Ernst Hairer @@ -95,3 +106,43 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------- http://www.unige.ch/~hairer/prog/licence.txt ---------- + + + +---------- http://www.netlib.org/lapack/LICENSE ---------- +Copyright (c) 1992-2008 The University of Tennessee. All rights reserved. + +$COPYRIGHT$ + +Additional copyrights may follow + +$HEADER$ + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer listed + in this license in the documentation and/or other materials + provided with the distribution. + +- Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +---------- http://www.netlib.org/lapack/LICENSE ---------- diff --git a/build.xml b/build.xml index 1e37d7988..232903a19 100644 --- a/build.xml +++ b/build.xml @@ -1,46 +1,32 @@ - + - - - + - + - - - + - + - + - + - + - + - - - - - - - - - - - + - - - - + + + + @@ -53,25 +39,13 @@ - - - - - - - - - - - - - + @@ -80,11 +54,9 @@ - + - - @@ -115,7 +87,7 @@ - + @@ -131,7 +103,7 @@ - + @@ -150,7 +122,7 @@ - + @@ -161,7 +133,7 @@ - + @@ -178,42 +150,16 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + Proxy used : @@ -222,6 +168,10 @@ Proxy user [${proxy.username}] + + + + Proxy not used. diff --git a/checkstyle.xml b/checkstyle.xml index 3e3d05fe3..db1e638ef 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -66,9 +66,21 @@ + + + + + + + + + + + + diff --git a/findbugs-exclude-filter.xml b/findbugs-exclude-filter.xml index f6c0af18c..2b962083f 100644 --- a/findbugs-exclude-filter.xml +++ b/findbugs-exclude-filter.xml @@ -49,11 +49,29 @@ + + + + + + + + + + + + + + + + + + @@ -64,6 +82,11 @@ + + + + + diff --git a/maven.xml b/maven.xml index 691cc46d3..750576b6a 100644 --- a/maven.xml +++ b/maven.xml @@ -51,12 +51,6 @@ --> - - diff --git a/pom.xml b/pom.xml index 470ddd0a7..a26d7e43a 100644 --- a/pom.xml +++ b/pom.xml @@ -15,19 +15,16 @@ See the License for the specific language governing permissions and limitations under the License. --> - + org.apache.commons commons-parent 11 4.0.0 - commons-math + org.apache.commons commons-math - 1.3-SNAPSHOT + 2.0-SNAPSHOT Commons Math 2003 @@ -41,9 +38,11 @@ - scm:svn:http://svn.apache.org/repos/asf/commons/proper/math/trunk - scm:svn:https://svn.apache.org/repos/asf/commons/proper/math/trunk - http://svn.apache.org/viewvc/commons/proper/math/trunk + + + scm:svn:http://svn.apache.org/repos/asf/commons/proper/math/branches/MATH_2_0 + scm:svn:https://svn.apache.org/repos/asf/commons/proper/math/branches/MATH_2_0 + http://svn.apache.org/viewvc/commons/proper/math/branches/MATH_2_0 @@ -106,6 +105,9 @@ Hasan Diwan + + Ted Dunning + Ken Geis @@ -127,6 +129,9 @@ Todd C. Parnell + + Andreas Rieger + Joni Salonen @@ -151,38 +156,10 @@ junit junit - 3.8.2 + 4.4 test - - commons-logging - commons-logging - 1.0.4 - - true - - - commons-discovery - commons-discovery - 0.2 - - true - - + math @@ -190,6 +167,8 @@ MATH 12310485 + 1.5 + 1.5 @@ -308,6 +287,8 @@ checkstyle.xml + true + 1.5 @@ -321,3 +302,4 @@ + diff --git a/project.properties b/project.properties index 79ffe9691..772a6a08b 100644 --- a/project.properties +++ b/project.properties @@ -45,8 +45,7 @@ maven.docs.dest=${maven.build.dir}/site maven.docs.src=${basedir}/src/site/xdoc maven.javadoc.links = http://java.sun.com/j2se/1.4.2/docs/api/,\ - http://commons.apache.org/collections/api/,\ - http://commons.apache.org/discovery/apidocs/ + http://commons.apache.org/collections/api/ maven.changes.issue.template=http://issues.apache.org/jira/browse/%ISSUE% diff --git a/project.xml b/project.xml index 462b7f7a5..bf6487cce 100644 --- a/project.xml +++ b/project.xml @@ -23,7 +23,7 @@ Math commons-math commons-math - 1.2-RC2 + 2.0-SNAPSHOT 2003 Commons Math The Math project is a library of lightweight, self-contained mathematics and statistics components addressing the most common practical problems not immediately available in the Java programming language or commons-lang. @@ -202,30 +202,6 @@ - - commons-logging - commons-logging - 1.0.4 - http://commons.apache.org/logging - - - Only required when commons-discovery is used for class factory - configuration. No hard compile or runtime dependency. - - - - - commons-discovery - commons-discovery - 0.2 - http://commons.apache.org/discovery - - - Only required when commons-discovery is used for class - factory configuration. No hard compile or runtime dependency. - - - maven-plugins maven-cobertura-plugin diff --git a/src/experimental/org/apache/commons/math/function/DefaultContext.java b/src/experimental/org/apache/commons/math/function/DefaultContext.java index 45d3561ec..0d9efed60 100644 --- a/src/experimental/org/apache/commons/math/function/DefaultContext.java +++ b/src/experimental/org/apache/commons/math/function/DefaultContext.java @@ -145,19 +145,19 @@ public class DefaultContext implements EvaluationContext { } DefaultValue(double d) { - value = new Double(d); + value = Double.valueOf(d); } DefaultValue(float f) { - value = new Float(f); + value = Float.valueOf(f); } DefaultValue(int i) { - value = new Integer(i); + value = Integer.valueOf(i); } DefaultValue(long l) { - value = new Long(l); + value = Long.valueOf(l); } /* (non-Javadoc) diff --git a/src/experimental/org/apache/commons/math/stat/univariate/BeanListUnivariateImpl.java b/src/experimental/org/apache/commons/math/stat/univariate/BeanListUnivariateImpl.java index 0df42a9c2..8bbd07539 100644 --- a/src/experimental/org/apache/commons/math/stat/univariate/BeanListUnivariateImpl.java +++ b/src/experimental/org/apache/commons/math/stat/univariate/BeanListUnivariateImpl.java @@ -134,7 +134,7 @@ public class BeanListUnivariateImpl extends ListUnivariateImpl implements Serial } catch (Exception ex) { // InstantiationException, IllegalAccessException throw new RuntimeException(ex); // should never happen } - dynaBean.set(propertyName, new Double(v)); + dynaBean.set(propertyName, Double.valueOf(v)); addObject(dynaBean); } diff --git a/src/experimental/org/apache/commons/math/stat/univariate/BeanListUnivariateImplTest.java b/src/experimental/org/apache/commons/math/stat/univariate/BeanListUnivariateImplTest.java index 9b60288c0..9e9751baf 100644 --- a/src/experimental/org/apache/commons/math/stat/univariate/BeanListUnivariateImplTest.java +++ b/src/experimental/org/apache/commons/math/stat/univariate/BeanListUnivariateImplTest.java @@ -60,21 +60,21 @@ public final class BeanListUnivariateImplTest extends TestCase { patientList = new ArrayList(); // Create and add patient bean 1 - VitalStats vs1 = new VitalStats( new Double(120.0), - new Double(96.4) ); - Patient p1 = new Patient( vs1, new Integer( 35 ) ); + VitalStats vs1 = new VitalStats( Double.valueOf(120.0), + Double.valueOf(96.4) ); + Patient p1 = new Patient( vs1, Integer.valueOf( 35 ) ); patientList.add( p1 ); // Create and add patient bean 2 - VitalStats vs2 = new VitalStats( new Double(70.0), - new Double(97.4) ); - Patient p2 = new Patient( vs2, new Integer( 23 ) ); + VitalStats vs2 = new VitalStats( Double.valueOf(70.0), + Double.valueOf(97.4) ); + Patient p2 = new Patient( vs2, Integer.valueOf( 23 ) ); patientList.add( p2 ); // Create and add patient bean 3 - VitalStats vs3 = new VitalStats( new Double(90.0), - new Double(98.6) ); - Patient p3 = new Patient( vs3, new Integer( 42 ) ); + VitalStats vs3 = new VitalStats( Double.valueOf(90.0), + Double.valueOf(98.6) ); + Patient p3 = new Patient( vs3, Integer.valueOf( 42 ) ); patientList.add( p3 ); } diff --git a/src/java/org/apache/commons/math/ArgumentOutsideDomainException.java b/src/java/org/apache/commons/math/ArgumentOutsideDomainException.java index 20efc0de2..e17a7dc56 100644 --- a/src/java/org/apache/commons/math/ArgumentOutsideDomainException.java +++ b/src/java/org/apache/commons/math/ArgumentOutsideDomainException.java @@ -38,7 +38,7 @@ public class ArgumentOutsideDomainException extends FunctionEvaluationException public ArgumentOutsideDomainException(double argument, double lower, double upper) { super(argument, "Argument {0} outside domain [{1} ; {2}]", - new Object[] { new Double(argument), new Double(lower), new Double(upper) }); + new Object[] { Double.valueOf(argument), Double.valueOf(lower), Double.valueOf(upper) }); } } diff --git a/src/java/org/apache/commons/math/ConvergenceException.java b/src/java/org/apache/commons/math/ConvergenceException.java index 985ee2029..c744c801c 100644 --- a/src/java/org/apache/commons/math/ConvergenceException.java +++ b/src/java/org/apache/commons/math/ConvergenceException.java @@ -31,7 +31,7 @@ public class ConvergenceException extends MathException { * Default constructor. */ public ConvergenceException() { - super("Convergence failed", new Object[0]); + super("Convergence failed", null); } /** @@ -65,30 +65,4 @@ public class ConvergenceException extends MathException { super(pattern, arguments, cause); } - /** - * Constructs a new ConvergenceException with specified - * detail message and nested Throwable root cause. - * - * @param msg the error message. - * @param rootCause the exception or error that caused this exception - * to be thrown. - * @deprecated as of 1.2, replaced by - * {@link #ConvergenceException(String, Object[], Throwable)} - */ - public ConvergenceException(String msg, Throwable rootCause) { - super(msg, rootCause); - } - - /** - * Constructs a new ConvergenceException with specified - * detail message. - * - * @param msg the error message. - * @deprecated as of 1.2, replaced by - * {@link #ConvergenceException(String, Object[])} - */ - public ConvergenceException(String msg) { - super(msg); - } - } diff --git a/src/java/org/apache/commons/math/DimensionMismatchException.java b/src/java/org/apache/commons/math/DimensionMismatchException.java index 0fdd0a1b0..a5ce4b4ca 100644 --- a/src/java/org/apache/commons/math/DimensionMismatchException.java +++ b/src/java/org/apache/commons/math/DimensionMismatchException.java @@ -35,7 +35,7 @@ public class DimensionMismatchException extends MathException { public DimensionMismatchException(int dimension1, int dimension2) { super("dimension mismatch {0} != {1}", new Object[] { - new Integer(dimension1), new Integer(dimension2) + Integer.valueOf(dimension1), Integer.valueOf(dimension2) }); this.dimension1 = dimension1; this.dimension2 = dimension2; diff --git a/src/java/org/apache/commons/math/DuplicateSampleAbscissaException.java b/src/java/org/apache/commons/math/DuplicateSampleAbscissaException.java index 2d1be1b89..d2986b06e 100644 --- a/src/java/org/apache/commons/math/DuplicateSampleAbscissaException.java +++ b/src/java/org/apache/commons/math/DuplicateSampleAbscissaException.java @@ -35,7 +35,7 @@ public class DuplicateSampleAbscissaException extends MathException { */ public DuplicateSampleAbscissaException(double abscissa, int i1, int i2) { super("Abscissa {0} is duplicated at both indices {1} and {2}", - new Object[] { new Double(abscissa), new Integer(i1), new Integer(i2) }); + new Object[] { Double.valueOf(abscissa), Integer.valueOf(i1), Integer.valueOf(i2) }); } /** diff --git a/src/java/org/apache/commons/math/FunctionEvaluationException.java b/src/java/org/apache/commons/math/FunctionEvaluationException.java index 13916e754..e0a957e4a 100644 --- a/src/java/org/apache/commons/math/FunctionEvaluationException.java +++ b/src/java/org/apache/commons/math/FunctionEvaluationException.java @@ -27,7 +27,7 @@ package org.apache.commons.math; public class FunctionEvaluationException extends MathException { /** Serializable version identifier. */ - private static final long serialVersionUID = -7619974756160279127L; + private static final long serialVersionUID = -2193260774031645876L; /** Argument causing function evaluation failure */ private double argument = Double.NaN; @@ -40,23 +40,10 @@ public class FunctionEvaluationException extends MathException { */ public FunctionEvaluationException(double argument) { super("Evaluation failed for argument = {0}", - new Object[] { new Double(argument) }); + new Object[] { Double.valueOf(argument) }); this.argument = argument; } - /** - * Construct an exception using the given argument and message - * text. - * - * @param argument the failing function argument - * @param message the exception message text - * @deprecated as of 1.2, replaced by {@link #FunctionEvaluationException(double, String, Object[])} - */ - public FunctionEvaluationException(double argument, String message) { - super(message); - this.argument = argument; - } - /** * Constructs an exception with specified formatted detail message. * Message formatting is delegated to {@link java.text.MessageFormat}. @@ -71,20 +58,6 @@ public class FunctionEvaluationException extends MathException { this.argument = argument; } - /** - * Construct an exception with the given argument, message and root cause. - * - * @param argument the failing function argument - * @param message descriptive error message - * @param cause root cause. - * @deprecated as of 1.2, replaced by {@link #FunctionEvaluationException(double, String, Object[], Throwable)} - */ - public FunctionEvaluationException(double argument, - String message, Throwable cause) { - super(message, cause); - this.argument = argument; - } - /** * Constructs an exception with specified root cause. * Message formatting is delegated to {@link java.text.MessageFormat}. diff --git a/src/java/org/apache/commons/math/MathConfigurationException.java b/src/java/org/apache/commons/math/MathConfigurationException.java index dd8bdadba..0bb48d9b9 100644 --- a/src/java/org/apache/commons/math/MathConfigurationException.java +++ b/src/java/org/apache/commons/math/MathConfigurationException.java @@ -25,7 +25,8 @@ import java.io.Serializable; public class MathConfigurationException extends MathException implements Serializable{ /** Serializable version identifier */ - private static final long serialVersionUID = -4056541384141349722L; + private static final long serialVersionUID = 5261476508226103366L; + /** * Default constructor. */ @@ -33,15 +34,6 @@ public class MathConfigurationException extends MathException implements Seriali super(); } - /** - * Construct an exception with the given message. - * @param message descriptive error message - * @deprecated as of 1.2, replaced by {@link #MathConfigurationException(String, Object[])} - */ - public MathConfigurationException(String message) { - super(message); - } - /** * Constructs an exception with specified formatted detail message. * Message formatting is delegated to {@link java.text.MessageFormat}. @@ -53,16 +45,6 @@ public class MathConfigurationException extends MathException implements Seriali super(pattern, arguments); } - /** - * Construct an exception with the given message and root cause. - * @param message descriptive error message - * @param cause the exception or error that caused this exception to be thrown - * @deprecated as of 1.2, replaced by {@link #MathConfigurationException(String, Object[], Throwable)} - */ - public MathConfigurationException(String message, Throwable cause) { - super(message, cause); - } - /** * Create an exception with a given root cause. * @param cause the exception or error that caused this exception to be thrown diff --git a/src/java/org/apache/commons/math/MathException.java b/src/java/org/apache/commons/math/MathException.java index e142e3fed..143e9a067 100644 --- a/src/java/org/apache/commons/math/MathException.java +++ b/src/java/org/apache/commons/math/MathException.java @@ -35,24 +35,8 @@ import java.util.ResourceBundle; */ public class MathException extends Exception { - /** Serializable version identifier */ - private static final long serialVersionUID = -8602234299177097102L; - - /** - * Does JDK support nested exceptions? - */ - private static final boolean JDK_SUPPORTS_NESTED; - - static { - boolean flag = false; - try { - Throwable.class.getDeclaredMethod("getCause", new Class[0]); - flag = true; - } catch (NoSuchMethodException ex) { - flag = false; - } - JDK_SUPPORTS_NESTED = flag; - } + /** Serializable version identifier. */ + private static final long serialVersionUID = 5924076008552401454L; /** Cache for resources bundle. */ private static ResourceBundle cachedResources = null; @@ -67,11 +51,6 @@ public class MathException extends Exception { */ private final Object[] arguments; - /** - * Root cause of the exception - */ - private final Throwable rootCause; - /** * Translate a string to a given locale. * @param s string to translate @@ -110,10 +89,7 @@ public class MathException extends Exception { * @return a message string */ private static String buildMessage(String pattern, Object[] arguments, Locale locale) { - // do it the hard way, for Java 1.3. compatibility - MessageFormat mf = new MessageFormat(translate(pattern, locale)); - mf.setLocale(locale); - return mf.format(arguments); + return (pattern == null) ? "" : new MessageFormat(translate(pattern, locale), locale).format(arguments); } /** @@ -124,23 +100,8 @@ public class MathException extends Exception { super(); this.pattern = null; this.arguments = new Object[0]; - this.rootCause = null; } - /** - * Constructs a new MathException with specified - * detail message. - * - * @param msg the error message. - * @deprecated as of 1.2, replaced by {@link #MathException(String, Object[])} - */ - public MathException(String msg) { - super(msg); - this.pattern = msg; - this.arguments = new Object[0]; - this.rootCause = null; - } - /** * Constructs a new MathException with specified * formatted detail message. @@ -151,8 +112,7 @@ public class MathException extends Exception { public MathException(String pattern, Object[] arguments) { super(buildMessage(pattern, arguments, Locale.US)); this.pattern = pattern; - this.arguments = (Object[]) arguments.clone(); - this.rootCause = null; + this.arguments = (arguments == null) ? new Object[0] : arguments.clone(); } /** @@ -163,28 +123,11 @@ public class MathException extends Exception { * to be thrown. */ public MathException(Throwable rootCause) { - super((rootCause == null ? null : rootCause.getMessage())); + super(rootCause); this.pattern = getMessage(); this.arguments = new Object[0]; - this.rootCause = rootCause; } - /** - * Constructs a new MathException with specified - * detail message and nested Throwable root cause. - * - * @param msg the error message. - * @param rootCause the exception or error that caused this exception - * to be thrown. - * @deprecated as of 1.2, replaced by {@link #MathException(String, Object[], Throwable)} - */ - public MathException(String msg, Throwable rootCause) { - super(msg); - this.pattern = msg; - this.arguments = new Object[0]; - this.rootCause = rootCause; - } - /** * Constructs a new MathException with specified * formatted detail message and nested Throwable root cause. @@ -196,10 +139,9 @@ public class MathException extends Exception { * @since 1.2 */ public MathException(String pattern, Object[] arguments, Throwable rootCause) { - super(buildMessage(pattern, arguments, Locale.US)); + super(buildMessage(pattern, arguments, Locale.US), rootCause); this.pattern = pattern; - this.arguments = (Object[]) arguments.clone(); - this.rootCause = rootCause; + this.arguments = (arguments == null) ? new Object[0] : arguments.clone(); } /** Gets the pattern used to build the message of this throwable. @@ -217,7 +159,7 @@ public class MathException extends Exception { * @since 1.2 */ public Object[] getArguments() { - return (Object[]) arguments.clone(); + return arguments.clone(); } /** Gets the message in a specified locale. @@ -228,18 +170,14 @@ public class MathException extends Exception { * @since 1.2 */ public String getMessage(Locale locale) { - return (pattern == null) ? null : buildMessage(pattern, arguments, locale); + return buildMessage(pattern, arguments, locale); } - /** - * Gets the cause of this throwable. - * - * @return the cause of this throwable, or null - */ - public Throwable getCause() { - return rootCause; + /** {@inheritDoc} */ + public String getLocalizedMessage() { + return getMessage(Locale.getDefault()); } - + /** * Prints the stack trace of this exception to the standard error stream. */ @@ -260,20 +198,5 @@ public class MathException extends Exception { pw.flush(); } } - - /** - * Prints the stack trace of this exception to the specified writer. - * - * @param out the PrintWriter to use for output - */ - public void printStackTrace(PrintWriter out) { - synchronized (out) { - super.printStackTrace(out); - if (rootCause != null && JDK_SUPPORTS_NESTED == false) { - out.print("Caused by: "); - rootCause.printStackTrace(out); - } - } - } } diff --git a/src/java/org/apache/commons/math/MathRuntimeException.java b/src/java/org/apache/commons/math/MathRuntimeException.java new file mode 100644 index 000000000..092e583d7 --- /dev/null +++ b/src/java/org/apache/commons/math/MathRuntimeException.java @@ -0,0 +1,334 @@ +/* + * 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; + +import java.io.EOFException; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.text.MessageFormat; +import java.text.ParseException; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** +* Base class for commons-math unchecked exceptions. +* +* @version $Revision$ $Date$ +* @since 2.0 +*/ +public class MathRuntimeException extends RuntimeException { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 8560172512507661982L; + + /** Cache for resources bundle. */ + private static ResourceBundle cachedResources = null; + + /** + * Pattern used to build the message. + */ + private final String pattern; + + /** + * Arguments used to build the message. + */ + private final Object[] arguments; + + /** + * Translate a string to a given locale. + * @param s string to translate + * @param locale locale into which to translate the string + * @return translated string or original string + * for unsupported locales or unknown strings + */ + private static String translate(final String s, final Locale locale) { + try { + if ((cachedResources == null) || (! cachedResources.getLocale().equals(locale))) { + // caching the resource bundle + cachedResources = + ResourceBundle.getBundle("org.apache.commons.math.MessagesResources", locale); + } + + if (cachedResources.getLocale().getLanguage().equals(locale.getLanguage())) { + // the value of the resource is the translated string + return cachedResources.getString(s); + } + + } catch (MissingResourceException mre) { + // do nothing here + } + + // the locale is not supported or the resource is unknown + // don't translate and fall back to using the string as is + return s; + + } + + /** + * Builds a message string by from a pattern and its arguments. + * @param pattern format specifier + * @param arguments format arguments + * @param locale Locale in which the message should be translated + * @return a message string + */ + private static String buildMessage(final String pattern, final Object[] arguments, + final Locale locale) { + return (pattern == null) ? "" : new MessageFormat(translate(pattern, locale), locale).format(arguments); + } + + /** + * Constructs a new MathRuntimeException with specified + * formatted detail message. + * Message formatting is delegated to {@link java.text.MessageFormat}. + * @param pattern format specifier + * @param arguments format arguments + */ + public MathRuntimeException(final String pattern, final Object[] arguments) { + super(buildMessage(pattern, arguments, Locale.US)); + this.pattern = pattern; + this.arguments = (arguments == null) ? new Object[0] : arguments.clone(); + } + + /** + * Constructs a new MathRuntimeException with specified + * nested Throwable root cause. + * + * @param rootCause the exception or error that caused this exception + * to be thrown. + */ + public MathRuntimeException(final Throwable rootCause) { + super(rootCause); + this.pattern = getMessage(); + this.arguments = new Object[0]; + } + + /** + * Constructs a new MathRuntimeException with specified + * formatted detail message and nested Throwable root cause. + * Message formatting is delegated to {@link java.text.MessageFormat}. + * @param pattern format specifier + * @param arguments format arguments + * @param rootCause the exception or error that caused this exception + * to be thrown. + */ + public MathRuntimeException(final String pattern, final Object[] arguments, + final Throwable rootCause) { + super(buildMessage(pattern, arguments, Locale.US), rootCause); + this.pattern = pattern; + this.arguments = (arguments == null) ? new Object[0] : arguments.clone(); + } + + /** Gets the pattern used to build the message of this throwable. + * + * @return the pattern used to build the message of this throwable + */ + public String getPattern() { + return pattern; + } + + /** Gets the arguments used to build the message of this throwable. + * + * @return the arguments used to build the message of this throwable + */ + public Object[] getArguments() { + return arguments.clone(); + } + + /** Gets the message in a specified locale. + * + * @param locale Locale in which the message should be translated + * + * @return localized message + */ + public String getMessage(final Locale locale) { + return buildMessage(pattern, arguments, locale); + } + + /** {@inheritDoc} */ + public String getLocalizedMessage() { + return getMessage(Locale.getDefault()); + } + + /** + * Prints the stack trace of this exception to the standard error stream. + */ + public void printStackTrace() { + printStackTrace(System.err); + } + + /** + * Prints the stack trace of this exception to the specified stream. + * + * @param out the PrintStream to use for output + */ + public void printStackTrace(final PrintStream out) { + synchronized (out) { + PrintWriter pw = new PrintWriter(out, false); + printStackTrace(pw); + // Flush the PrintWriter before it's GC'ed. + pw.flush(); + } + } + + /** + * Constructs a new ArithmeticException with specified formatted detail message. + * Message formatting is delegated to {@link java.text.MessageFormat}. + * @param pattern format specifier + * @param arguments format arguments + */ + public static ArithmeticException createArithmeticException(final String pattern, + final Object[] arguments) { + return new ArithmeticException(buildMessage(pattern, arguments, Locale.US)) { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 7705628723242533939L; + + /** {@inheritDoc} */ + public String getLocalizedMessage() { + return buildMessage(pattern, arguments, Locale.getDefault()); + } + + }; + } + + /** + * Constructs a new ArrayIndexOutOfBoundsException with specified formatted detail message. + * Message formatting is delegated to {@link java.text.MessageFormat}. + * @param pattern format specifier + * @param arguments format arguments + */ + public static ArrayIndexOutOfBoundsException createArrayIndexOutOfBoundsException(final String pattern, + final Object[] arguments) { + return new ArrayIndexOutOfBoundsException(buildMessage(pattern, arguments, Locale.US)) { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 8077627622976962141L; + + /** {@inheritDoc} */ + public String getLocalizedMessage() { + return buildMessage(pattern, arguments, Locale.getDefault()); + } + + }; + } + + /** + * Constructs a new EOFException with specified formatted detail message. + * Message formatting is delegated to {@link java.text.MessageFormat}. + * @param pattern format specifier + * @param arguments format arguments + */ + public static EOFException createEOFException(final String pattern, + final Object[] arguments) { + return new EOFException(buildMessage(pattern, arguments, Locale.US)) { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 279461544586092584L; + + /** {@inheritDoc} */ + public String getLocalizedMessage() { + return buildMessage(pattern, arguments, Locale.getDefault()); + } + + }; + } + + /** + * Constructs a new IOException with specified nested + * Throwable root cause. + *

This factory method allows chaining of other exceptions within an + * IOException even for Java 5. The constructor for + * IOException with a cause parameter was introduced only + * with Java 6.

+ * @param rootCause the exception or error that caused this exception + * to be thrown. + */ + public static IOException createIOException(final Throwable rootCause) { + IOException ioe = new IOException(rootCause.getLocalizedMessage()); + ioe.initCause(rootCause); + return ioe; + } + + /** + * Constructs a new IllegalArgumentException with specified formatted detail message. + * Message formatting is delegated to {@link java.text.MessageFormat}. + * @param pattern format specifier + * @param arguments format arguments + */ + public static IllegalArgumentException createIllegalArgumentException(final String pattern, + final Object[] arguments) { + return new IllegalArgumentException(buildMessage(pattern, arguments, Locale.US)) { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -7537852425838457684L; + + /** {@inheritDoc} */ + public String getLocalizedMessage() { + return buildMessage(pattern, arguments, Locale.getDefault()); + } + + }; + } + + /** + * Constructs a new IllegalStateException with specified formatted detail message. + * Message formatting is delegated to {@link java.text.MessageFormat}. + * @param pattern format specifier + * @param arguments format arguments + */ + public static IllegalStateException createIllegalStateException(final String pattern, + final Object[] arguments) { + return new IllegalStateException(buildMessage(pattern, arguments, Locale.US)) { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 5173599768297434381L; + + /** {@inheritDoc} */ + public String getLocalizedMessage() { + return buildMessage(pattern, arguments, Locale.getDefault()); + } + + }; + } + + /** + * Constructs a new ParseException with specified + * formatted detail message. + * Message formatting is delegated to {@link java.text.MessageFormat}. + * @param pattern format specifier + * @param arguments format arguments + * @param offset offset at which error occurred + */ + public static ParseException createParseException(final String pattern, + final Object[] arguments, + final int offset) { + return new ParseException(buildMessage(pattern, arguments, Locale.US), offset) { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -1103502177342465975L; + + /** {@inheritDoc} */ + public String getLocalizedMessage() { + return buildMessage(pattern, arguments, Locale.getDefault()); + } + + }; + } + +} diff --git a/src/java/org/apache/commons/math/MaxIterationsExceededException.java b/src/java/org/apache/commons/math/MaxIterationsExceededException.java index ecc59de94..f21d72734 100644 --- a/src/java/org/apache/commons/math/MaxIterationsExceededException.java +++ b/src/java/org/apache/commons/math/MaxIterationsExceededException.java @@ -41,7 +41,7 @@ public class MaxIterationsExceededException extends ConvergenceException { */ public MaxIterationsExceededException(int maxIterations) { super("Maximal number of iterations ({0}) exceeded", - new Object[] { new Integer(maxIterations) }); + new Object[] { Integer.valueOf(maxIterations) }); this.maxIterations = maxIterations; } diff --git a/src/java/org/apache/commons/math/MessagesResources_fr.java b/src/java/org/apache/commons/math/MessagesResources_fr.java index f983c11b8..f6f176895 100644 --- a/src/java/org/apache/commons/math/MessagesResources_fr.java +++ b/src/java/org/apache/commons/math/MessagesResources_fr.java @@ -133,7 +133,7 @@ public class MessagesResources_fr { "unable to orthogonalize matrix in {0} iterations", "impossible de rendre la matrice orthogonale en {0} it\u00e9rations" }, - // org.apache.commons.math.ode.AdaptiveStepsizeIntegrator + // org.apache.commons.math.ode.nonstiff.AdaptiveStepsizeIntegrator { "minimal step size ({0}) reached, integration needs {1}", "pas minimal ({0}) atteint, l''int\u00e9gration n\u00e9cessite {1}" }, { "dimensions mismatch: state vector has dimension {0}," + @@ -145,8 +145,8 @@ public class MessagesResources_fr "incompatibilit\u00e9 de dimensions entre le vecteur d''\u00e9tat ({0})," + " et le vecteur de tol\u00e9rance relative ({1})" }, - // org.apache.commons.math.ode.AdaptiveStepsizeIntegrator, - // org.apache.commons.math.ode.RungeKuttaIntegrator + // org.apache.commons.math.ode.nonstiff.AdaptiveStepsizeIntegrator, + // org.apache.commons.math.ode.nonstiff.RungeKuttaIntegrator { "dimensions mismatch: ODE problem has dimension {0}," + " initial state vector has dimension {1}", "incompatibilit\u00e9 de dimensions entre le probl\u00e8me ODE ({0})," + @@ -158,9 +158,153 @@ public class MessagesResources_fr { "too small integration interval: length = {0}", "intervalle d''int\u00e9gration trop petit : {0}" }, + // org.apache.commons.math.ode.ContinuousOutputModel + // org.apache.commons.math.optimization.DirectSearchOptimizer + { "unexpected exception caught", + "exception inattendue lev\u00e9e" }, + // org.apache.commons.math.optimization.DirectSearchOptimizer { "none of the {0} start points lead to convergence", - "aucun des {0} points de d\u00e9part n''aboutit \u00e0 une convergence" } + "aucun des {0} points de d\u00e9part n''aboutit \u00e0 une convergence" }, + + // org.apache.commons.math.random.EmpiricalDistributionImpl + { "no bin selected", + "aucun compartiment s\u00e9lectionn\u00e9" }, + + // org.apache.commons.math.linear.EigenDecompositionImpl + { "cannot solve degree {0} equation", + "impossible de r\u00e9soudre une \u00e9quation de degr\u00e9 {0}" }, + { "negative element on decomposed tridiagonal of {0}x{1} matrix", + "\u00e9l\u00e9ment n\u00e9gatif dans la d\u00e9composition tri-diagonale d''une matrice {0}x{1}" }, + + // org.apache.commons.math.linear.NonSquareMatrixException + { "a {0}x{1} matrix was provided instead of a square matrix", + "une matrice {0}x{1} a \u00e9t\u00e9 fournie \u00e0 la place d''une matrice carr\u00e9e" }, + + // org.apache.commons.math.linear.SingularMatrixException + { "matrix is singular", + "matrice singuli\u00e8re" }, + + // org.apache.commons.math.linear.RankDeficientMatrixException + { "matrix is rank-deficient", + "le rang de la matrice est inf\u00e9rieur \u00e0 sa dimension" }, + + // org.apache.commons.math.linear.RealVectorImpl + { "index {0} out of allowed range [{1}, {2}]", + "index {0} hors de la plage autoris\u00e9e [{1}, {2}]" }, + + // org.apache.commons.math.linear.BigMatrixImpl + // org.apache.commons.math.linear.RealMatrixImpl + { "row index {0} out of allowed range [{1}, {2}]", + "index de ligne {0} hors de la plage autoris\u00e9e [{1}, {2}]" }, + { "column index {0} out of allowed range [{1}, {2}]", + "index de colonne {0} hors de la plage autoris\u00e9e [{1}, {2}]" }, + { "no entry at indices ({0}, {1}) in a {2}x{3} matrix", + "pas d''entr\u00e9e aux indices ({0}, {1}) dans une matrice {2}x{3}" }, + { "initial row {0} after final row {1}", + "ligne initiale {0} apr\u00e8s la ligne finale {1}" }, + { "initial column {0} after final column {1}", + "colonne initiale {0} apr\u00e8s la colonne finale {1}" }, + { "empty selected row index array", + "tableau des indices de lignes s\u00e9lectionn\u00e9es vide" }, + { "empty selected column index array", + "tableau des indices de colonnes s\u00e9lectionn\u00e9es vide" }, + + // org.apache.commons.math.random.EmpiricalDistributionImpl + // org.apache.commons.math.random.ValueServer + { "URL {0} contains no data", + "l''adresse {0} ne contient aucune donn\u00e9e" }, + + // org.apache.commons.math.complex.ComplexFormat + { "unparseable complex number: \"{0}\"", + "\u00e9chec d''analyse du nombre complexe \"{0}\"" }, + + // org.apache.commons.math.fraction.FractionFormat + { "unparseable fraction number: \"{0}\"", + "\u00e9chec d''analyse du nombre rationnel \"{0}\"" }, + + // org.apache.commons.math.geometry.Vector3DFormat + { "unparseable 3D vector: \"{0}\"", + "\u00e9chec d''analyse du vecteur de dimension 3 \"{0}\"" }, + + // org.apache.commons.math.linear.RealVectorFormat + { "unparseable real vector: \"{0}\"", + "\u00e9chec d''analyse du vecteur r\u00e9el \"{0}\"" }, + + // org.apache.commons.math.util.ResizableDoubleArray + { "the index specified: {0} is larger than the current maximal index {1}", + "l''index sp\u00e9cifi\u00e9 ({0}) d\u00e9passe l''index maximal courant ({1})" }, + { "elements cannot be retrieved from a negative array index {0}", + "impossible d''extraire un \u00e9l\u00e9ment \u00e0 un index n\u00e9gatif ({0})" }, + { "cannot set an element at a negative index {0}", + "impossible de mettre un \u00e9l\u00e9ment \u00e0 un index n\u00e9gatif ({0})" }, + { "cannot substitute an element from an empty array", + "impossible de substituer un \u00e9l\u00e9ment dans un tableau vide" }, + + // org.apache.commons.math.analysis.PolynomialFunctionLagrangeForm + { "identical abscissas x[{0}] == x[{1}] == {2} cause division by zero", + "division par z\u00e9ro caus\u00e9e par les abscisses identiques x[{0}] == x[{1}] == {2}" }, + + // org.apache.commons.math.fraction.Fraction + { "zero denominator in fraction {0}/{1}", + "d\u00e9nominateur null dans le nombre rationnel {0}/{1}" }, + { "overflow in fraction {0}/{1}, cannot negate", + "d\u00e9passement de capacit\u00e9 pour la fraction {0}/{1}, son signe ne peut \u00eatre chang\u00e9" }, + { "overflow, numerator too large after multiply: {0}", + "d\u00e9passement de capacit\u00e9 pour le num\u00e9rateur apr\u00e8s multiplication : {0}" }, + { "the fraction to divide by must not be zero: {0}/{1}", + "division par un nombre rationnel nul : {0}/{1}" }, + + // org.apache.commons.math.geometry.Rotation + { "zero norm for rotation axis", + "norme nulle pour un axe de rotation" }, + + // org.apache.commons.math.geometry.Vector3D + // org.apache.commons.math.linear.RealVectorImpl + { "cannot normalize a zero norm vector", + "impossible de normer un vecteur de norme nulle" }, + { "zero norm", + "norme nulle" }, + + // org.apache.commons.math.analysis.UnivariateRealIntegratorImpl + // org.apache.commons.math.analysis.UnivariateRealSolverImpl + { "no result available", + "aucun r\u00e9sultat n''est disponible" }, + + // org.apache.commons.math.linear.BigMatrixImpl + { "first {0} rows are not initialized yet", + "les {0} premi\u00e8res lignes ne sont pas encore initialis\u00e9es" }, + { "first {0} columns are not initialized yet", + "les {0} premi\u00e8res colonnes ne sont pas encore initialis\u00e9es" }, + + // org.apache.commons.math.linear.EigenDecompositionImpl + // org.apache.commons.math.linear.LUDecompositionImpl + // org.apache.commons.math.linear.QRDecompositionImpl + // org.apache.commons.math.linear.SingularValueDecompositionImpl + { "no matrix have been decomposed yet", + "aucune matrice n''a encore \u00e9t\u00e9 d\u00e9compos\u00e9e" }, + + // org.apache.commons.math.random.EmpiricalDistributionImpl + { "distribution not loaded", + "aucune distribution n''a \u00e9t\u00e9 charg\u00e9e" }, + + // org.apache.commons.math.random.ValueServer + { "unknown mode {0}, known modes: {1} ({2}), {3} ({4}), {5} ({6}), {7} ({8}), {9} ({10}) and {11} ({12})", + "mode {0} inconnu, modes connus : {1} ({2}), {3} ({4}), {5} ({6}), {7} ({8}), {9} ({10}) et {11} ({12})" }, + { "digest not initialized", + "mod\u00e8le empirique non initialis\u00e9" }, + + // org.apache.commons.math.stat.descriptive.moment.GeometricMean + // org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics + // org.apache.commons.math.stat.descriptive.SummaryStatistics + { "{0} values have been added before statistic is configured", + "{0} valeurs ont \u00e9t\u00e9 ajout\u00e9es avant que la statistique ne soit configur\u00e9e" }, + + // org.apache.commons.math.stat.descriptive.moment.Kurtosis + { "statistics constructed from external moments cannot be incremented", + "les statistiques bas\u00e9es sur des moments externes ne peuvent pas \u00eatre incr\u00e9ment\u00e9es" }, + { "statistics constructed from external moments cannot be cleared", + "les statistiques bas\u00e9es sur des moments externes ne peuvent pas \u00eatre remises \u00e0 z\u00e9ro" } }; diff --git a/src/java/org/apache/commons/math/analysis/BrentSolver.java b/src/java/org/apache/commons/math/analysis/BrentSolver.java index da8b3f54b..57f64a974 100644 --- a/src/java/org/apache/commons/math/analysis/BrentSolver.java +++ b/src/java/org/apache/commons/math/analysis/BrentSolver.java @@ -26,7 +26,7 @@ import org.apache.commons.math.MaxIterationsExceededException; *

* The function should be continuous but not necessarily smooth.

* - * @version $Revision$ $Date$ + * @version $Revision:670469 $ $Date:2008-06-23 10:01:38 +0200 (lun., 23 juin 2008) $ */ public class BrentSolver extends UnivariateRealSolverImpl { diff --git a/src/java/org/apache/commons/math/analysis/DividedDifferenceInterpolator.java b/src/java/org/apache/commons/math/analysis/DividedDifferenceInterpolator.java index 3739b8365..9bbc26ce7 100644 --- a/src/java/org/apache/commons/math/analysis/DividedDifferenceInterpolator.java +++ b/src/java/org/apache/commons/math/analysis/DividedDifferenceInterpolator.java @@ -27,7 +27,7 @@ import org.apache.commons.math.DuplicateSampleAbscissaException; * functions. For reference, see Introduction to Numerical Analysis, * ISBN 038795452X, chapter 2. *

- * The actual code of Neville's evalution is in PolynomialFunctionLagrangeForm, + * The actual code of Neville's evaluation is in PolynomialFunctionLagrangeForm, * this class provides an easy-to-use interface to it.

* * @version $Revision$ $Date$ diff --git a/src/java/org/apache/commons/math/analysis/PolynomialFunctionLagrangeForm.java b/src/java/org/apache/commons/math/analysis/PolynomialFunctionLagrangeForm.java index 7cf021252..68135ef87 100644 --- a/src/java/org/apache/commons/math/analysis/PolynomialFunctionLagrangeForm.java +++ b/src/java/org/apache/commons/math/analysis/PolynomialFunctionLagrangeForm.java @@ -20,6 +20,7 @@ import java.io.Serializable; import org.apache.commons.math.DuplicateSampleAbscissaException; import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MathRuntimeException; /** * Implements the representation of a real polynomial function in @@ -253,8 +254,14 @@ public class PolynomialFunctionLagrangeForm implements UnivariateRealFunction, } if (d == 0.0) { // This happens only when two abscissas are identical. - throw new ArithmeticException - ("Identical abscissas cause division by zero."); + for (int k = 0; k < n; ++k) { + if ((i != k) && (x[i] == x[k])) { + throw MathRuntimeException.createArithmeticException("identical abscissas x[{0}] == x[{1}] == {2} cause division by zero", + new Object[] { + i, k, x[i] + }); + } + } } t = y[i] / d; // Lagrange polynomial is the sum of n terms, each of which is a diff --git a/src/java/org/apache/commons/math/analysis/UnivariateRealIntegratorImpl.java b/src/java/org/apache/commons/math/analysis/UnivariateRealIntegratorImpl.java index 911c55bb6..693d99e1f 100644 --- a/src/java/org/apache/commons/math/analysis/UnivariateRealIntegratorImpl.java +++ b/src/java/org/apache/commons/math/analysis/UnivariateRealIntegratorImpl.java @@ -18,6 +18,8 @@ package org.apache.commons.math.analysis; import java.io.Serializable; +import org.apache.commons.math.MathRuntimeException; + /** * Provide a default implementation for several generic functions. * @@ -99,7 +101,7 @@ public abstract class UnivariateRealIntegratorImpl implements if (resultComputed) { return result; } else { - throw new IllegalStateException("No result available."); + throw MathRuntimeException.createIllegalStateException("no result available", null); } } @@ -113,7 +115,7 @@ public abstract class UnivariateRealIntegratorImpl implements if (resultComputed) { return iterationCount; } else { - throw new IllegalStateException("No result available."); + throw MathRuntimeException.createIllegalStateException("no result available", null); } } diff --git a/src/java/org/apache/commons/math/analysis/UnivariateRealSolverFactory.java b/src/java/org/apache/commons/math/analysis/UnivariateRealSolverFactory.java index f80f4cd96..74f36d888 100644 --- a/src/java/org/apache/commons/math/analysis/UnivariateRealSolverFactory.java +++ b/src/java/org/apache/commons/math/analysis/UnivariateRealSolverFactory.java @@ -16,8 +16,6 @@ */ package org.apache.commons.math.analysis; -import org.apache.commons.discovery.tools.DiscoverClass; - /** * Abstract factory class used to create {@link UnivariateRealSolver} instances. *

@@ -37,11 +35,6 @@ import org.apache.commons.discovery.tools.DiscoverClass; * BrentSolver solver = factory.newBrentSolver(f); * * - * Apache Commons Discovery - * is used to determine the concrete factory returned by - * UnivariateRealSolverFactory.newInstance(). The default is - * {@link UnivariateRealSolverFactoryImpl}. - * * @version $Revision$ $Date$ */ public abstract class UnivariateRealSolverFactory { @@ -56,16 +49,7 @@ public abstract class UnivariateRealSolverFactory { * @return a new factory. */ public static UnivariateRealSolverFactory newInstance() { - UnivariateRealSolverFactory factory = null; - try { - DiscoverClass dc = new DiscoverClass(); - factory = (UnivariateRealSolverFactory) dc.newInstance( - UnivariateRealSolverFactory.class, - "org.apache.commons.math.analysis.UnivariateRealSolverFactoryImpl"); - } catch(Throwable t) { - return new UnivariateRealSolverFactoryImpl(); - } - return factory; + return new UnivariateRealSolverFactoryImpl(); } /** diff --git a/src/java/org/apache/commons/math/analysis/UnivariateRealSolverImpl.java b/src/java/org/apache/commons/math/analysis/UnivariateRealSolverImpl.java index df10405fe..04bed1eab 100644 --- a/src/java/org/apache/commons/math/analysis/UnivariateRealSolverImpl.java +++ b/src/java/org/apache/commons/math/analysis/UnivariateRealSolverImpl.java @@ -20,6 +20,7 @@ package org.apache.commons.math.analysis; import java.io.Serializable; import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MathRuntimeException; /** * Provide a default implementation for several functions useful to generic @@ -111,7 +112,7 @@ public abstract class UnivariateRealSolverImpl implements UnivariateRealSolver, if (resultComputed) { return result; } else { - throw new IllegalStateException("No result available"); + throw MathRuntimeException.createIllegalStateException("no result available", null); } } @@ -126,7 +127,7 @@ public abstract class UnivariateRealSolverImpl implements UnivariateRealSolver, if (resultComputed) { return iterationCount; } else { - throw new IllegalStateException("No result available"); + throw MathRuntimeException.createIllegalStateException("no result available", null); } } diff --git a/src/java/org/apache/commons/math/analysis/UnivariateRealSolverUtils.java b/src/java/org/apache/commons/math/analysis/UnivariateRealSolverUtils.java index 8c11bd5df..89eb0196d 100644 --- a/src/java/org/apache/commons/math/analysis/UnivariateRealSolverUtils.java +++ b/src/java/org/apache/commons/math/analysis/UnivariateRealSolverUtils.java @@ -199,9 +199,9 @@ public class UnivariateRealSolverUtils { if (fa * fb >= 0.0 ) { throw new ConvergenceException ("Number of iterations={0}, maximum iterations={1}, initial={2}, lower bound={3}, upper bound={4}, final a value={5}, final b value={6}, f(a)={7}, f(b)={8}", - new Object[] { new Integer(numIterations), new Integer(maximumIterations), - new Double(initial), new Double(lowerBound), new Double(upperBound), - new Double(a), new Double(b), new Double(fa), new Double(fb) }); + new Object[] { Integer.valueOf(numIterations), Integer.valueOf(maximumIterations), + Double.valueOf(initial), Double.valueOf(lowerBound), Double.valueOf(upperBound), + Double.valueOf(a), Double.valueOf(b), Double.valueOf(fa), Double.valueOf(fb) }); } return new double[]{a, b}; diff --git a/src/java/org/apache/commons/math/complex/Complex.java b/src/java/org/apache/commons/math/complex/Complex.java index 4bcf6d469..8b622cb29 100644 --- a/src/java/org/apache/commons/math/complex/Complex.java +++ b/src/java/org/apache/commons/math/complex/Complex.java @@ -58,15 +58,13 @@ public class Complex implements Serializable { /** * The imaginary part - * @deprecated to be made final and private in 2.0 */ - protected double imaginary; + private final double imaginary; /** * The real part - * @deprecated to be made final and private in 2.0 */ - protected double real; + private final double real; /** * Create a complex number given the real and imaginary parts. @@ -257,10 +255,7 @@ public class Complex implements Serializable { if (rhs.isNaN()) { ret = this.isNaN(); } else { - ret = (Double.doubleToRawLongBits(real) == - Double.doubleToRawLongBits(rhs.getReal())) && - (Double.doubleToRawLongBits(imaginary) == - Double.doubleToRawLongBits(rhs.getImaginary())); + ret = (real == rhs.real) && (imaginary == rhs.imaginary); } } catch (ClassCastException ex) { // ignore exception diff --git a/src/java/org/apache/commons/math/complex/ComplexFormat.java b/src/java/org/apache/commons/math/complex/ComplexFormat.java index 0757d9b91..c5d929ec7 100644 --- a/src/java/org/apache/commons/math/complex/ComplexFormat.java +++ b/src/java/org/apache/commons/math/complex/ComplexFormat.java @@ -17,28 +17,29 @@ package org.apache.commons.math.complex; -import java.io.Serializable; import java.text.FieldPosition; -import java.text.Format; import java.text.NumberFormat; import java.text.ParseException; import java.text.ParsePosition; import java.util.Locale; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.util.CompositeFormat; + /** * Formats a Complex number in cartesian format "Re(c) + Im(c)i". 'i' can - * be replaced with 'j', and the number format for both real and imaginary parts - * can be configured. + * be replaced with 'j' (or anything else), and the number format for both real + * and imaginary parts can be configured. * * @author Apache Software Foundation * @version $Revision$ $Date$ */ -public class ComplexFormat extends Format implements Serializable { +public class ComplexFormat extends CompositeFormat { /** Serializable version identifier */ - private static final long serialVersionUID = -6337346779577272306L; - - /** The default imaginary character. */ + private static final long serialVersionUID = -3343698360149467646L; + + /** The default imaginary character. */ private static final String DEFAULT_IMAGINARY_CHARACTER = "i"; /** The notation used to signify the imaginary part of the complex number. */ @@ -73,8 +74,7 @@ public class ComplexFormat extends Format implements Serializable { * @param realFormat the custom format for the real part. * @param imaginaryFormat the custom format for the imaginary part. */ - public ComplexFormat(NumberFormat realFormat, - NumberFormat imaginaryFormat) { + public ComplexFormat(NumberFormat realFormat, NumberFormat imaginaryFormat) { this(DEFAULT_IMAGINARY_CHARACTER, realFormat, imaginaryFormat); } @@ -114,14 +114,23 @@ public class ComplexFormat extends Format implements Serializable { } /** - * This static method calls formatComplex() on a default instance of + * Get the set of locales for which complex formats are available. + *

This is the same set as the {@link NumberFormat} set.

+ * @return available complex format locales. + */ + public static Locale[] getAvailableLocales() { + return NumberFormat.getAvailableLocales(); + } + + /** + * This static method calls {@link #format(Object)} on a default instance of * ComplexFormat. * * @param c Complex object to format * @return A formatted number in the form "Re(c) + Im(c)i" */ - public static String formatComplex( Complex c ) { - return getInstance().format( c ); + public static String formatComplex(Complex c) { + return getInstance().format(c); } /** @@ -182,74 +191,12 @@ public class ComplexFormat extends Format implements Serializable { ret = format( new Complex(((Number)obj).doubleValue(), 0.0), toAppendTo, pos); } else { - throw new IllegalArgumentException( - "Cannot format given Object as a Date"); + throw new IllegalArgumentException("Cannot format given Object as a Complex"); } return ret; } - /** - * Formats a double value to produce a string. In general, the value is - * formatted using the formatting rules of format. There are - * three exceptions to this: - *
    - *
  1. NaN is formatted as '(NaN)'
  2. - *
  3. Positive infinity is formatted as '(Infinity)'
  4. - *
  5. Negative infinity is formatted as '(-Infinity)'
  6. - *
- * - * @param value the double to format. - * @param format the format used. - * @param toAppendTo where the text is to be appended - * @param pos On input: an alignment field, if desired. On output: the - * offsets of the alignment field - * @return the value passed in as toAppendTo. - */ - private StringBuffer formatDouble(double value, NumberFormat format, - StringBuffer toAppendTo, FieldPosition pos) { - if( Double.isNaN(value) || Double.isInfinite(value) ) { - toAppendTo.append('('); - toAppendTo.append(value); - toAppendTo.append(')'); - } else { - format.format(value, toAppendTo, pos); - } - return toAppendTo; - } - - /** - * Get the set of locales for which complex formats are available. This - * is the same set as the {@link NumberFormat} set. - * @return available complex format locales. - */ - public static Locale[] getAvailableLocales() { - return NumberFormat.getAvailableLocales(); - } - - /** - * Create a default number format. The default number format is based on - * {@link NumberFormat#getInstance()} with the only customizing is the - * maximum number of fraction digits, which is set to 2. - * @return the default number format. - */ - private static NumberFormat getDefaultNumberFormat() { - return getDefaultNumberFormat(Locale.getDefault()); - } - - /** - * Create a default number format. The default number format is based on - * {@link NumberFormat#getInstance(java.util.Locale)} with the only - * customizing is the maximum number of fraction digits, which is set to 2. - * @param locale the specific locale used by the format. - * @return the default number format specific to the given locale. - */ - private static NumberFormat getDefaultNumberFormat(Locale locale) { - NumberFormat nf = NumberFormat.getInstance(locale); - nf.setMaximumFractionDigits(2); - return nf; - } - /** * Access the imaginaryCharacter. * @return the imaginaryCharacter. @@ -304,8 +251,9 @@ public class ComplexFormat extends Format implements Serializable { ParsePosition parsePosition = new ParsePosition(0); Complex result = parse(source, parsePosition); if (parsePosition.getIndex() == 0) { - throw new ParseException("Unparseable complex number: \"" + source + - "\"", parsePosition.getErrorIndex()); + throw MathRuntimeException.createParseException("unparseable complex number: \"{0}\"", + new Object[] { source }, + parsePosition.getErrorIndex()); } return result; } @@ -328,7 +276,6 @@ public class ComplexFormat extends Format implements Serializable { if (re == null) { // invalid real number // set index back to initial, error index should already be set - // character examined. pos.setIndex(initialIndex); return null; } @@ -365,129 +312,19 @@ public class ComplexFormat extends Format implements Serializable { if (im == null) { // invalid imaginary number // set index back to initial, error index should already be set - // character examined. pos.setIndex(initialIndex); return null; } // parse imaginary character - int n = getImaginaryCharacter().length(); - startIndex = pos.getIndex(); - int endIndex = startIndex + n; - if ((startIndex >= source.length()) || - (endIndex > source.length()) || - source.substring(startIndex, endIndex).compareTo( - getImaginaryCharacter()) != 0) { - // set index back to initial, error index should be the start index - // character examined. - pos.setIndex(initialIndex); - pos.setErrorIndex(startIndex); + if (!parseFixedstring(source, getImaginaryCharacter(), pos)) { return null; } - pos.setIndex(endIndex); return new Complex(re.doubleValue(), im.doubleValue() * sign); + } - /** - * Parses source until a non-whitespace character is found. - * - * @param source the string to parse - * @param pos input/ouput parsing parameter. On output, pos - * holds the index of the next non-whitespace character. - */ - private void parseAndIgnoreWhitespace(String source, ParsePosition pos) { - parseNextCharacter(source, pos); - pos.setIndex(pos.getIndex() - 1); - } - - /** - * Parses source until a non-whitespace character is found. - * - * @param source the string to parse - * @param pos input/ouput parsing parameter. - * @return the first non-whitespace character. - */ - private char parseNextCharacter(String source, ParsePosition pos) { - int index = pos.getIndex(); - int n = source.length(); - char ret = 0; - - if (index < n) { - char c; - do { - c = source.charAt(index++); - } while (Character.isWhitespace(c) && index < n); - pos.setIndex(index); - - if (index < n) { - ret = c; - } - } - - return ret; - } - - /** - * Parses source for a special double values. These values - * include Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY. - * - * @param source the string to parse - * @param value the special value to parse. - * @param pos input/ouput parsing parameter. - * @return the special number. - */ - private Number parseNumber(String source, double value, ParsePosition pos) { - Number ret = null; - - StringBuffer sb = new StringBuffer(); - sb.append('('); - sb.append(value); - sb.append(')'); - - int n = sb.length(); - int startIndex = pos.getIndex(); - int endIndex = startIndex + n; - if (endIndex < source.length()) { - if (source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) { - ret = new Double(value); - pos.setIndex(endIndex); - } - } - - return ret; - } - - /** - * Parses source for a number. This method can parse normal, - * numeric values as well as special values. These special values include - * Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY. - * - * @param source the string to parse - * @param format the number format used to parse normal, numeric values. - * @param pos input/ouput parsing parameter. - * @return the parsed number. - */ - private Number parseNumber(String source, NumberFormat format, ParsePosition pos) { - int startIndex = pos.getIndex(); - Number number = format.parse(source, pos); - int endIndex = pos.getIndex(); - - // check for error parsing number - if (startIndex == endIndex) { - // try parsing special numbers - double[] special = {Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}; - for (int i = 0; i < special.length; ++i) { - number = parseNumber(source, special[i], pos); - if (number != null) { - break; - } - } - } - - return number; - } - /** * Parses a string to produce a object. * @@ -499,6 +336,7 @@ public class ComplexFormat extends Format implements Serializable { public Object parseObject(String source, ParsePosition pos) { return parse(source, pos); } + /** * Modify the imaginaryCharacter. * @param imaginaryCharacter The new imaginaryCharacter value. @@ -540,4 +378,5 @@ public class ComplexFormat extends Format implements Serializable { } this.realFormat = realFormat; } + } diff --git a/src/java/org/apache/commons/math/complex/ComplexUtils.java b/src/java/org/apache/commons/math/complex/ComplexUtils.java index 457ca9a64..80faa9e05 100644 --- a/src/java/org/apache/commons/math/complex/ComplexUtils.java +++ b/src/java/org/apache/commons/math/complex/ComplexUtils.java @@ -17,23 +17,9 @@ package org.apache.commons.math.complex; -import org.apache.commons.math.util.MathUtils; - /** * Static implementations of common - * {@link org.apache.commons.math.complex.Complex}-valued functions. Included - * are trigonometric, exponential, log, power and square root functions. - *

- * Reference: - *

- * See individual method javadocs for the computational formulas used. - * In general, NaN values in either real or imaginary parts of input arguments - * result in {@link Complex#NaN} returned. Otherwise, infinite or NaN values - * are returned as they arise in computing the real functions specified in the - * computational formulas. Null arguments result in NullPointerExceptions. + * {@link org.apache.commons.math.complex.Complex} utilities functions. * * @version $Revision$ $Date$ */ @@ -46,198 +32,6 @@ public class ComplexUtils { super(); } - /** - * Compute the - * - * inverse cosine for the given complex argument. - *

- * Implements the formula:

-     *  acos(z) = -i (log(z + i (sqrt(1 - z2))))
- *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN or infinite. - * - * @param z the value whose inverse cosine is to be returned - * @return the inverse cosine of z - * @throws NullPointerException if z is null - * @deprecated use Complex.acos() - */ - public static Complex acos(Complex z) { - return z.acos(); - } - - /** - * Compute the - * - * inverse sine for the given complex argument. - *

- * Implements the formula:

-     *  asin(z) = -i (log(sqrt(1 - z2) + iz)) 
- *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN or infinite. - * - * @param z the value whose inverse sine is to be returned. - * @return the inverse sine of z. - * @throws NullPointerException if z is null - * @deprecated use Complex.asin() - */ - public static Complex asin(Complex z) { - return z.asin(); - } - - /** - * Compute the - * - * inverse tangent for the given complex argument. - *

- * Implements the formula:

-     *  atan(z) = (i/2) log((i + z)/(i - z)) 
- *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN or infinite. - * - * @param z the value whose inverse tangent is to be returned - * @return the inverse tangent of z - * @throws NullPointerException if z is null - * @deprecated use Complex.atan() - */ - public static Complex atan(Complex z) { - return z.atan(); - } - - /** - * Compute the - * - * cosine - * for the given complex argument. - *

- * Implements the formula:

-     *  cos(a + bi) = cos(a)cosh(b) - sin(a)sinh(b)i
- * where the (real) functions on the right-hand side are - * {@link java.lang.Math#sin}, {@link java.lang.Math#cos}, - * {@link MathUtils#cosh} and {@link MathUtils#sinh}. - *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN. - *

- * Infinite values in real or imaginary parts of the input may result in - * infinite or NaN values returned in parts of the result.

-     * Examples: 
-     * 
-     * cos(1 ± INFINITY i) = 1 ∓ INFINITY i
-     * cos(±INFINITY + i) = NaN + NaN i
-     * cos(±INFINITY ± INFINITY i) = NaN + NaN i
- * - * @param z the value whose cosine is to be returned - * @return the cosine of z - * @throws NullPointerException if z is null - * @deprecated use Complex.cos() - */ - public static Complex cos(Complex z) { - return z.cos(); - } - - /** - * Compute the - * - * hyperbolic cosine for the given complex argument. - *

- * Implements the formula:

-     *  cosh(a + bi) = cosh(a)cos(b) + sinh(a)sin(b)i
- * where the (real) functions on the right-hand side are - * {@link java.lang.Math#sin}, {@link java.lang.Math#cos}, - * {@link MathUtils#cosh} and {@link MathUtils#sinh}. - *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN. - *

- * Infinite values in real or imaginary parts of the input may result in - * infinite or NaN values returned in parts of the result.

-     * Examples: 
-     * 
-     * cosh(1 ± INFINITY i) = NaN + NaN i
-     * cosh(±INFINITY + i) = INFINITY ± INFINITY i
-     * cosh(±INFINITY ± INFINITY i) = NaN + NaN i
- *

- * Throws NullPointerException if z is null. - * - * @param z the value whose hyperbolic cosine is to be returned. - * @return the hyperbolic cosine of z. - * @deprecated use Complex.cosh() - */ - public static Complex cosh(Complex z) { - return z.cosh(); - } - - /** - * Compute the - * - * exponential function for the given complex argument. - *

- * Implements the formula:

-     *  exp(a + bi) = exp(a)cos(b) + exp(a)sin(b)i
- * where the (real) functions on the right-hand side are - * {@link java.lang.Math#exp}, {@link java.lang.Math#cos}, and - * {@link java.lang.Math#sin}. - *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN. - *

- * Infinite values in real or imaginary parts of the input may result in - * infinite or NaN values returned in parts of the result.

-     * Examples: 
-     * 
-     * exp(1 ± INFINITY i) = NaN + NaN i
-     * exp(INFINITY + i) = INFINITY + INFINITY i
-     * exp(-INFINITY + i) = 0 + 0i
-     * exp(±INFINITY ± INFINITY i) = NaN + NaN i
- *

- * Throws NullPointerException if z is null. - * - * @param z the value - * @return ez - * @deprecated use Complex.exp() - */ - public static Complex exp(Complex z) { - return z.exp(); - } - - /** - * Compute the - * - * natural logarithm for the given complex argument. - *

- * Implements the formula:

-     *  log(a + bi) = ln(|a + bi|) + arg(a + bi)i
- * where ln on the right hand side is {@link java.lang.Math#log}, - * |a + bi| is the modulus, {@link Complex#abs}, and - * arg(a + bi) = {@link java.lang.Math#atan2}(b, a) - *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN. - *

- * Infinite (or critical) values in real or imaginary parts of the input may - * result in infinite or NaN values returned in parts of the result.

-     * Examples: 
-     * 
-     * log(1 ± INFINITY i) = INFINITY ± (π/2)i
-     * log(INFINITY + i) = INFINITY + 0i
-     * log(-INFINITY + i) = INFINITY + πi
-     * log(INFINITY ± INFINITY i) = INFINITY ± (π/4)i
-     * log(-INFINITY ± INFINITY i) = INFINITY ± (3π/4)i
-     * log(0 + 0i) = -INFINITY + 0i
-     * 
- * Throws NullPointerException if z is null. - * - * @param z the value. - * @return ln z. - * @deprecated use Complex.log() - */ - public static Complex log(Complex z) { - return z.log(); - } - /** * Creates a complex number from the given polar representation. *

@@ -271,216 +65,4 @@ public class ComplexUtils { return new Complex(r * Math.cos(theta), r * Math.sin(theta)); } - /** - * Returns of value of y raised to the power of x. - *

- * Implements the formula:

-     *  yx = exp(x·log(y))
- * where exp and log are {@link #exp} and - * {@link #log}, respectively. - *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN or infinite, or if y - * equals {@link Complex#ZERO}. - * - * @param y the base. - * @param x the exponent. - * @return yx - * @throws NullPointerException if either x or y is null - * @deprecated use Complex.pow(x) - */ - public static Complex pow(Complex y, Complex x) { - return y.pow(x); - } - - /** - * Compute the - * - * sine - * for the given complex argument. - *

- * Implements the formula:

-     *  sin(a + bi) = sin(a)cosh(b) - cos(a)sinh(b)i
- * where the (real) functions on the right-hand side are - * {@link java.lang.Math#sin}, {@link java.lang.Math#cos}, - * {@link MathUtils#cosh} and {@link MathUtils#sinh}. - *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN. - *

- * Infinite values in real or imaginary parts of the input may result in - * infinite or NaN values returned in parts of the result.

-     * Examples: 
-     * 
-     * sin(1 ± INFINITY i) = 1 ± INFINITY i
-     * sin(±INFINITY + i) = NaN + NaN i
-     * sin(±INFINITY ± INFINITY i) = NaN + NaN i
- * - * Throws NullPointerException if z is null. - * - * @param z the value whose sine is to be returned. - * @return the sine of z. - * @deprecated use Complex.sin() - */ - public static Complex sin(Complex z) { - return z.sin(); - } - - /** - * Compute the - * - * hyperbolic sine for the given complex argument. - *

- * Implements the formula:

-     *  sinh(a + bi) = sinh(a)cos(b)) + cosh(a)sin(b)i
- * where the (real) functions on the right-hand side are - * {@link java.lang.Math#sin}, {@link java.lang.Math#cos}, - * {@link MathUtils#cosh} and {@link MathUtils#sinh}. - *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN. - *

- * Infinite values in real or imaginary parts of the input may result in - * infinite or NaN values returned in parts of the result.

-     * Examples: 
-     * 
-     * sinh(1 ± INFINITY i) = NaN + NaN i
-     * sinh(±INFINITY + i) = ± INFINITY + INFINITY i
-     * sinh(±INFINITY ± INFINITY i) = NaN + NaN i
z - * @throws NullPointerException if z is null - * @deprecated use Complex.sinh() - */ - public static Complex sinh(Complex z) { - return z.sinh(); - } - - /** - * Compute the - * - * square root for the given complex argument. - *

- * Implements the following algorithm to compute sqrt(a + bi): - *

  1. Let t = sqrt((|a| + |a + bi|) / 2)
  2. - *
  3. if  a ≥ 0 return t + (b/2t)i
    -     *  else return |b|/2t + sign(b)t i 
  4. - *
- * where
    - *
  • |a| = {@link Math#abs}(a)
  • - *
  • |a + bi| = {@link Complex#abs}(a + bi)
  • - *
  • sign(b) = {@link MathUtils#indicator}(b) - *
- *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN. - *

- * Infinite values in real or imaginary parts of the input may result in - * infinite or NaN values returned in parts of the result.

-     * Examples: 
-     * 
-     * sqrt(1 ± INFINITY i) = INFINITY + NaN i
-     * sqrt(INFINITY + i) = INFINITY + 0i
-     * sqrt(-INFINITY + i) = 0 + INFINITY i
-     * sqrt(INFINITY ± INFINITY i) = INFINITY + NaN i
-     * sqrt(-INFINITY ± INFINITY i) = NaN ± INFINITY i
-     * 
- * - * @param z the value whose square root is to be returned - * @return the square root of z - * @throws NullPointerException if z is null - * @deprecated use Complex.sqrt() - */ - public static Complex sqrt(Complex z) { - return z.sqrt(); - } - - /** - * Compute the - * - * square root of 1 - z2 for the given complex - * argument. - *

- * Computes the result directly as - * sqrt(Complex.ONE.subtract(z.multiply(z))). - *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN. - *

- * Infinite values in real or imaginary parts of the input may result in - * infinite or NaN values returned in parts of the result. - * - * @param z the value - * @return the square root of 1 - z2 - * @throws NullPointerException if z is null - * @deprecated use Complex.sqrt1z() - */ - public static Complex sqrt1z(Complex z) { - return z.sqrt1z(); - } - - /** - * Compute the - * - * tangent for the given complex argument. - *

- * Implements the formula:

-     * tan(a + bi) = sin(2a)/(cos(2a)+cosh(2b)) + [sinh(2b)/(cos(2a)+cosh(2b))]i
- * where the (real) functions on the right-hand side are - * {@link java.lang.Math#sin}, {@link java.lang.Math#cos}, - * {@link MathUtils#cosh} and {@link MathUtils#sinh}. - *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN. - *

- * Infinite (or critical) values in real or imaginary parts of the input may - * result in infinite or NaN values returned in parts of the result.

-     * Examples: 
-     * 
-     * tan(1 ± INFINITY i) = 0 + NaN i
-     * tan(±INFINITY + i) = NaN + NaN i
-     * tan(±INFINITY ± INFINITY i) = NaN + NaN i
-     * tan(±π/2 + 0 i) = ±INFINITY + NaN i
- * - * @param z the value whose tangent is to be returned - * @return the tangent of z - * @throws NullPointerException if z is null - * @deprecated use Complex.tan() - */ - public static Complex tan(Complex z) { - return z.tan(); - } - - /** - * Compute the - * - * hyperbolic tangent for the given complex argument. - *

- * Implements the formula:

-     * tan(a + bi) = sinh(2a)/(cosh(2a)+cos(2b)) + [sin(2b)/(cosh(2a)+cos(2b))]i
- * where the (real) functions on the right-hand side are - * {@link java.lang.Math#sin}, {@link java.lang.Math#cos}, - * {@link MathUtils#cosh} and {@link MathUtils#sinh}. - *

- * Returns {@link Complex#NaN} if either real or imaginary part of the - * input argument is NaN. - *

- * Infinite values in real or imaginary parts of the input may result in - * infinite or NaN values returned in parts of the result.

-     * Examples: 
-     * 
-     * tanh(1 ± INFINITY i) = NaN + NaN i
-     * tanh(±INFINITY + i) = NaN + 0 i
-     * tanh(±INFINITY ± INFINITY i) = NaN + NaN i
-     * tanh(0 + (π/2)i) = NaN + INFINITY i
- * - * @param z the value whose hyperbolic tangent is to be returned - * @return the hyperbolic tangent of z - * @throws NullPointerException if z is null - * @deprecated use Complex.tanh() - */ - public static Complex tanh(Complex z) { - return z.tanh(); - } } diff --git a/src/java/org/apache/commons/math/distribution/BetaDistribution.java b/src/java/org/apache/commons/math/distribution/BetaDistribution.java new file mode 100644 index 000000000..caac986df --- /dev/null +++ b/src/java/org/apache/commons/math/distribution/BetaDistribution.java @@ -0,0 +1,61 @@ +/* + * 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.distribution; + +import org.apache.commons.math.MathException; + +/** + * Computes the cumulative, inverse cumulative and density functions for the beta distribuiton. + * + * @see Beta_distribution + * @version $Revision$ $Date$ + * @since 2.0 + */ +public interface BetaDistribution extends ContinuousDistribution, HasDensity { + /** + * Modify the shape parameter, alpha. + * @param alpha the new shape parameter. + */ + void setAlpha(double alpha); + + /** + * Access the shape parameter, alpha + * @return alpha. + */ + double getAlpha(); + + /** + * Modify the shape parameter, beta. + * @param beta the new scale parameter. + */ + void setBeta(double beta); + + /** + * Access the shape parameter, beta + * @return beta. + */ + double getBeta(); + + /** + * Return the probability density for a particular point. + * @param x The point at which the density should be computed. + * @return The pdf at point x. + * @exception MathException if probability density cannot be computed + */ + double density(Double x) throws MathException; + +} diff --git a/src/java/org/apache/commons/math/distribution/BetaDistributionImpl.java b/src/java/org/apache/commons/math/distribution/BetaDistributionImpl.java new file mode 100644 index 000000000..99484fcfd --- /dev/null +++ b/src/java/org/apache/commons/math/distribution/BetaDistributionImpl.java @@ -0,0 +1,157 @@ +/* + * 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.distribution; + +import org.apache.commons.math.MathException; +import org.apache.commons.math.special.Gamma; +import org.apache.commons.math.special.Beta; + +/** + * Implements the Beta distribution. + *

+ * References: + *

+ *

+ * @version $Revision$ $Date$ + * @since 2.0 + */ +public class BetaDistributionImpl + extends AbstractContinuousDistribution implements BetaDistribution { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -1221965979403477668L; + + /** First shape parameter. */ + private double alpha; + + /** Second shape parameter. */ + private double beta; + + /** Normalizing factor used in density computations. + * updated whenever alpha or beta are changed. + */ + private double z; + + /** + * Build a new instance. + * @param alpha first shape parameter (must be positive) + * @param beta second shape parameter (must be positive) + */ + public BetaDistributionImpl(double alpha, double beta) { + this.alpha = alpha; + this.beta = beta; + z = Double.NaN; + } + + /** {@inheritDoc} */ + public void setAlpha(double alpha) { + this.alpha = alpha; + z = Double.NaN; + } + + /** {@inheritDoc} */ + public double getAlpha() { + return alpha; + } + + /** {@inheritDoc} */ + public void setBeta(double beta) { + this.beta = beta; + z = Double.NaN; + } + + /** {@inheritDoc} */ + public double getBeta() { + return beta; + } + + /** + * Recompute the normalization factor. + */ + private void recomputeZ() { + if (Double.isNaN(z)) { + z = Gamma.logGamma(alpha) + Gamma.logGamma(beta) - Gamma.logGamma(alpha + beta); + } + } + + /** {@inheritDoc} */ + public double density(Double x) throws MathException { + recomputeZ(); + if (x < 0 || x > 1) { + return 0; + } else if (x == 0) { + if (alpha < 1) { + throw new MathException("Cannot compute beta density at 0 when alpha = {0,number}", new Double[]{alpha}); + } + return 0; + } else if (x == 1) { + if (beta < 1) { + throw new MathException("Cannot compute beta density at 1 when beta = %.3g", new Double[]{beta}); + } + return 0; + } else { + double logX = Math.log(x); + double log1mX = Math.log1p(-x); + return Math.exp((alpha - 1) * logX + (beta - 1) * log1mX - z); + } + } + + /** {@inheritDoc} */ + public double inverseCumulativeProbability(double p) throws MathException { + if (p == 0) { + return 0; + } else if (p == 1) { + return 1; + } else { + return super.inverseCumulativeProbability(p); + } + } + + /** {@inheritDoc} */ + protected double getInitialDomain(double p) { + return p; + } + + /** {@inheritDoc} */ + protected double getDomainLowerBound(double p) { + return 0; + } + + /** {@inheritDoc} */ + protected double getDomainUpperBound(double p) { + return 1; + } + + /** {@inheritDoc} */ + public double cumulativeProbability(double x) throws MathException { + if (x <= 0) { + return 0; + } else if (x >= 1) { + return 1; + } else { + return Beta.regularizedBeta(x, alpha, beta); + } + } + + /** {@inheritDoc} */ + public double cumulativeProbability(double x0, double x1) throws MathException { + return cumulativeProbability(x1) - cumulativeProbability(x0); + } +} diff --git a/src/java/org/apache/commons/math/distribution/ChiSquaredDistribution.java b/src/java/org/apache/commons/math/distribution/ChiSquaredDistribution.java index a90287420..37fa8fe89 100644 --- a/src/java/org/apache/commons/math/distribution/ChiSquaredDistribution.java +++ b/src/java/org/apache/commons/math/distribution/ChiSquaredDistribution.java @@ -29,7 +29,7 @@ package org.apache.commons.math.distribution; * * @version $Revision$ $Date$ */ -public interface ChiSquaredDistribution extends ContinuousDistribution { +public interface ChiSquaredDistribution extends ContinuousDistribution, HasDensity { /** * Modify the degrees of freedom. * @param degreesOfFreedom the new degrees of freedom. @@ -41,4 +41,11 @@ public interface ChiSquaredDistribution extends ContinuousDistribution { * @return the degrees of freedom. */ double getDegreesOfFreedom(); + + /** + * Return the probability density for a particular point. + * @param x The point at which the density should be computed. + * @return The pdf at point x. + */ + double density(Double x); } \ No newline at end of file diff --git a/src/java/org/apache/commons/math/distribution/ChiSquaredDistributionImpl.java b/src/java/org/apache/commons/math/distribution/ChiSquaredDistributionImpl.java index d6f6b7c92..5efbbc61e 100644 --- a/src/java/org/apache/commons/math/distribution/ChiSquaredDistributionImpl.java +++ b/src/java/org/apache/commons/math/distribution/ChiSquaredDistributionImpl.java @@ -70,7 +70,17 @@ public class ChiSquaredDistributionImpl public double getDegreesOfFreedom() { return getGamma().getAlpha() * 2.0; } - + + /** + * Return the probability density for a particular point. + * + * @param x The point at which the density should be computed. + * @return The pdf at point x. + */ + public double density(Double x) { + return gamma.density(x); + } + /** * For this distribution, X, this method returns P(X < x). * @param x the value at which the CDF is evaluated. diff --git a/src/java/org/apache/commons/math/distribution/DistributionFactory.java b/src/java/org/apache/commons/math/distribution/DistributionFactory.java deleted file mode 100644 index f1702489e..000000000 --- a/src/java/org/apache/commons/math/distribution/DistributionFactory.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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.distribution; - -/** - * This factory provids the means to create common statistical distributions. - * The following distributions are supported: - *
    - *
  • Binomial
  • - *
  • Cauchy
  • - *
  • Chi-Squared
  • - *
  • Exponential
  • - *
  • F
  • - *
  • Gamma
  • - *
  • HyperGeometric
  • - *
  • Poisson
  • - *
  • Normal
  • - *
  • Student's t
  • - *
  • Weibull
  • - *
  • Pascal
  • - *
- * - * Common usage:
- * DistributionFactory factory = DistributionFactory.newInstance();
- *
- * // create a Chi-Square distribution with 5 degrees of freedom.
- * ChiSquaredDistribution chi = factory.createChiSquareDistribution(5.0);
- * 
- * - * @version $Revision$ $Date$ - * @deprecated pluggability of distribution instances is now provided through - * constructors and setters. - */ -public abstract class DistributionFactory { - /** - * Default constructor. - */ - protected DistributionFactory() { - super(); - } - - /** - * Create an instance of a DistributionFactory - * @return a new factory. - */ - public static DistributionFactory newInstance() { - return new DistributionFactoryImpl(); - } - - /** - * Create a binomial distribution with the given number of trials and - * probability of success. - * - * @param numberOfTrials the number of trials. - * @param probabilityOfSuccess the probability of success - * @return a new binomial distribution - */ - public abstract BinomialDistribution createBinomialDistribution( - int numberOfTrials, double probabilityOfSuccess); - - /** - * Create a Pascal distribution with the given number of successes and - * probability of success. - * - * @param numberOfSuccesses the number of successes. - * @param probabilityOfSuccess the probability of success - * @return a new Pascal distribution - * @since 1.2 - */ - public PascalDistribution createPascalDistribution( - int numberOfSuccesses, double probabilityOfSuccess) { - return new PascalDistributionImpl(numberOfSuccesses, probabilityOfSuccess); - } - - /** - * Create a new cauchy distribution with the given median and scale. - * @param median the median of the distribution - * @param scale the scale - * @return a new cauchy distribution - * @since 1.1 - */ - public CauchyDistribution createCauchyDistribution( - double median, double scale) - { - return new CauchyDistributionImpl(median, scale); - } - - /** - * Create a new chi-square distribution with the given degrees of freedom. - * - * @param degreesOfFreedom degrees of freedom - * @return a new chi-square distribution - */ - public abstract ChiSquaredDistribution createChiSquareDistribution( - double degreesOfFreedom); - - /** - * Create a new exponential distribution with the given degrees of freedom. - * - * @param mean mean - * @return a new exponential distribution - */ - public abstract ExponentialDistribution createExponentialDistribution( - double mean); - - /** - * Create a new F-distribution with the given degrees of freedom. - * - * @param numeratorDegreesOfFreedom numerator degrees of freedom - * @param denominatorDegreesOfFreedom denominator degrees of freedom - * @return a new F-distribution - */ - public abstract FDistribution createFDistribution( - double numeratorDegreesOfFreedom, double denominatorDegreesOfFreedom); - - /** - * Create a new gamma distribution with the given shape and scale - * parameters. - * - * @param alpha the shape parameter - * @param beta the scale parameter - * - * @return a new gamma distribution - */ - public abstract GammaDistribution createGammaDistribution( - double alpha, double beta); - - /** - * Create a new t distribution with the given degrees of freedom. - * - * @param degreesOfFreedom degrees of freedom - * @return a new t distribution - */ - public abstract TDistribution createTDistribution(double degreesOfFreedom); - - /** - * Create a new hypergeometric distribution with the given the population - * size, the number of successes in the population, and the sample size. - * - * @param populationSize the population size - * @param numberOfSuccesses number of successes in the population - * @param sampleSize the sample size - * @return a new hypergeometric desitribution - */ - public abstract HypergeometricDistribution - createHypergeometricDistribution(int populationSize, - int numberOfSuccesses, int sampleSize); - - /** - * Create a new normal distribution with the given mean and standard - * deviation. - * - * @param mean the mean of the distribution - * @param sd standard deviation - * @return a new normal distribution - */ - public abstract NormalDistribution - createNormalDistribution(double mean, double sd); - - /** - * Create a new normal distribution with mean zero and standard - * deviation one. - * - * @return a new normal distribution. - */ - public abstract NormalDistribution createNormalDistribution(); - - /** - * Create a new Poisson distribution with poisson parameter lambda. - * - * @param lambda poisson parameter - * @return a new poisson distribution. - */ - public abstract PoissonDistribution - createPoissonDistribution(double lambda); - - /** - * Create a new Weibull distribution with the given shape and scale - * parameters. - * - * @param alpha the shape parameter. - * @param beta the scale parameter. - * @return a new Weibull distribution. - * @since 1.1 - */ - public WeibullDistribution createWeibullDistribution( - double alpha, double beta) - { - return new WeibullDistributionImpl(alpha, beta); - } -} diff --git a/src/java/org/apache/commons/math/distribution/DistributionFactoryImpl.java b/src/java/org/apache/commons/math/distribution/DistributionFactoryImpl.java deleted file mode 100644 index 20d988b0c..000000000 --- a/src/java/org/apache/commons/math/distribution/DistributionFactoryImpl.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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.distribution; - -/** - * A concrete distribution factory. This is the default factory used by - * Commons-Math. - * - * @version $Revision$ $Date$ - * @deprecated pluggability of distribution instances is now provided through - * constructors and setters. - */ -public class DistributionFactoryImpl extends DistributionFactory { - - /** - * Default constructor. Package scope to prevent unwanted instantiation. - */ - public DistributionFactoryImpl() { - super(); - } - - /** - * Create a new chi-square distribution with the given degrees of freedom. - * - * @param degreesOfFreedom degrees of freedom - * @return a new chi-square distribution - */ - public ChiSquaredDistribution createChiSquareDistribution( - final double degreesOfFreedom) { - - return new ChiSquaredDistributionImpl(degreesOfFreedom); - } - - /** - * Create a new gamma distribution the given shape and scale parameters. - * - * @param alpha the shape parameter - * @param beta the scale parameter - * @return a new gamma distribution - */ - public GammaDistribution createGammaDistribution( - double alpha, double beta) { - - return new GammaDistributionImpl(alpha, beta); - } - - /** - * Create a new t distribution with the given degrees of freedom. - * - * @param degreesOfFreedom degrees of freedom - * @return a new t distribution. - */ - public TDistribution createTDistribution(double degreesOfFreedom) { - return new TDistributionImpl(degreesOfFreedom); - } - - /** - * Create a new F-distribution with the given degrees of freedom. - * - * @param numeratorDegreesOfFreedom numerator degrees of freedom - * @param denominatorDegreesOfFreedom denominator degrees of freedom - * @return a new F-distribution - */ - public FDistribution createFDistribution( - double numeratorDegreesOfFreedom, - double denominatorDegreesOfFreedom) { - return new FDistributionImpl(numeratorDegreesOfFreedom, - denominatorDegreesOfFreedom); - } - - /** - * Create a new exponential distribution with the given degrees of freedom. - * - * @param mean mean - * @return a new exponential distribution - */ - public ExponentialDistribution createExponentialDistribution(double mean) { - return new ExponentialDistributionImpl(mean); - } - - /** - * Create a binomial distribution with the given number of trials and - * probability of success. - * - * @param numberOfTrials the number of trials - * @param probabilityOfSuccess the probability of success - * @return a new binomial distribution - */ - public BinomialDistribution createBinomialDistribution( - int numberOfTrials, double probabilityOfSuccess) { - return new BinomialDistributionImpl(numberOfTrials, - probabilityOfSuccess); - } - - /** - * Create a new hypergeometric distribution with the given the population - * size, the number of successes in the population, and the sample size. - * - * @param populationSize the population size - * @param numberOfSuccesses number of successes in the population - * @param sampleSize the sample size - * @return a new hypergeometric desitribution - */ - public HypergeometricDistribution createHypergeometricDistribution( - int populationSize, int numberOfSuccesses, int sampleSize) { - return new HypergeometricDistributionImpl(populationSize, - numberOfSuccesses, sampleSize); - } - - /** - * Create a new normal distribution with the given mean and standard - * deviation. - * - * @param mean the mean of the distribution - * @param sd standard deviation - * @return a new normal distribution - */ - public NormalDistribution createNormalDistribution(double mean, double sd) { - return new NormalDistributionImpl(mean, sd); - } - - /** - * Create a new normal distribution with the mean zero and standard - * deviation one. - * - * @return a new normal distribution - */ - public NormalDistribution createNormalDistribution() { - return new NormalDistributionImpl(); - } - - /** - * Create a new Poisson distribution with poisson parameter lambda. - *

- * lambda must be postive; otherwise an - * IllegalArgumentException is thrown. - * - * @param lambda poisson parameter - * @return a new Poisson distribution - * @throws IllegalArgumentException if lambda ≤ 0 - */ - public PoissonDistribution createPoissonDistribution(double lambda) { - return new PoissonDistributionImpl(lambda); - } -} diff --git a/src/java/org/apache/commons/math/distribution/ExponentialDistribution.java b/src/java/org/apache/commons/math/distribution/ExponentialDistribution.java index 326443c08..a511fceda 100644 --- a/src/java/org/apache/commons/math/distribution/ExponentialDistribution.java +++ b/src/java/org/apache/commons/math/distribution/ExponentialDistribution.java @@ -29,7 +29,7 @@ package org.apache.commons.math.distribution; * * @version $Revision$ $Date$ */ -public interface ExponentialDistribution extends ContinuousDistribution { +public interface ExponentialDistribution extends ContinuousDistribution, HasDensity { /** * Modify the mean. * @param mean the new mean. @@ -41,4 +41,11 @@ public interface ExponentialDistribution extends ContinuousDistribution { * @return the mean. */ double getMean(); + + /** + * Return the probability density for a particular point. + * @param x The point at which the density should be computed. + * @return The pdf at point x. + */ + double density(Double x); } \ No newline at end of file diff --git a/src/java/org/apache/commons/math/distribution/ExponentialDistributionImpl.java b/src/java/org/apache/commons/math/distribution/ExponentialDistributionImpl.java index 5d9f331e0..735fcb8da 100644 --- a/src/java/org/apache/commons/math/distribution/ExponentialDistributionImpl.java +++ b/src/java/org/apache/commons/math/distribution/ExponentialDistributionImpl.java @@ -63,6 +63,19 @@ public class ExponentialDistributionImpl extends AbstractContinuousDistribution return mean; } + /** + * Return the probability density for a particular point. + * + * @param x The point at which the density should be computed. + * @return The pdf at point x. + */ + public double density(Double x) { + if (x < 0) { + return 0; + } + return Math.exp(-x / getMean()) / getMean(); + } + /** * For this distribution, X, this method returns P(X < x). * @@ -157,6 +170,8 @@ public class ExponentialDistributionImpl extends AbstractContinuousDistribution */ protected double getInitialDomain(double p) { // TODO: try to improve on this estimate + // TODO: what should really happen here is not derive from AbstractContinuousDistribution + // TODO: because the inverse cumulative distribution is simple. // Exponential is skewed to the left, therefore, P(X < μ) > .5 if (p < .5) { // use 1/2 mean diff --git a/src/java/org/apache/commons/math/distribution/FDistributionImpl.java b/src/java/org/apache/commons/math/distribution/FDistributionImpl.java index 4e85614cb..59aeb07b0 100644 --- a/src/java/org/apache/commons/math/distribution/FDistributionImpl.java +++ b/src/java/org/apache/commons/math/distribution/FDistributionImpl.java @@ -24,188 +24,173 @@ import org.apache.commons.math.special.Beta; /** * Default implementation of * {@link org.apache.commons.math.distribution.FDistribution}. - * - * @version $Revision$ $Date: 2008-02-08 09:44:11 -0600 (Fri, 08 Feb - * 2008) $ + * + * @version $Revision$ $Date$ */ -public class FDistributionImpl extends AbstractContinuousDistribution implements - FDistribution, Serializable { +public class FDistributionImpl + extends AbstractContinuousDistribution + implements FDistribution, Serializable { - /** Serializable version identifier */ - private static final long serialVersionUID = -8516354193418641566L; + /** Serializable version identifier */ + private static final long serialVersionUID = -8516354193418641566L; - /** The numerator degrees of freedom */ - private double numeratorDegreesOfFreedom; + /** The numerator degrees of freedom*/ + private double numeratorDegreesOfFreedom; - /** The numerator degrees of freedom */ - private double denominatorDegreesOfFreedom; + /** The numerator degrees of freedom*/ + private double denominatorDegreesOfFreedom; + + /** + * Create a F distribution using the given degrees of freedom. + * @param numeratorDegreesOfFreedom the numerator degrees of freedom. + * @param denominatorDegreesOfFreedom the denominator degrees of freedom. + */ + public FDistributionImpl(double numeratorDegreesOfFreedom, + double denominatorDegreesOfFreedom) { + super(); + setNumeratorDegreesOfFreedom(numeratorDegreesOfFreedom); + setDenominatorDegreesOfFreedom(denominatorDegreesOfFreedom); + } + + /** + * For this distribution, X, this method returns P(X < x). + * + * The implementation of this method is based on: + *

+ * + * @param x the value at which the CDF is evaluated. + * @return CDF for this distribution. + * @throws MathException if the cumulative probability can not be + * computed due to convergence or other numerical errors. + */ + public double cumulativeProbability(double x) throws MathException { + double ret; + if (x <= 0.0) { + ret = 0.0; + } else { + double n = getNumeratorDegreesOfFreedom(); + double m = getDenominatorDegreesOfFreedom(); + + ret = Beta.regularizedBeta((n * x) / (m + n * x), + 0.5 * n, + 0.5 * m); + } + return ret; + } + + /** + * For this distribution, X, this method returns the critical point x, such + * that P(X < x) = p. + *

+ * Returns 0 for p=0 and Double.POSITIVE_INFINITY for p=1.

+ * + * @param p the desired probability + * @return x, such that P(X < x) = p + * @throws MathException if the inverse cumulative probability can not be + * computed due to convergence or other numerical errors. + * @throws IllegalArgumentException if p is not a valid + * probability. + */ + public double inverseCumulativeProbability(final double p) + throws MathException { + if (p == 0) { + return 0d; + } + if (p == 1) { + return Double.POSITIVE_INFINITY; + } + return super.inverseCumulativeProbability(p); + } + + /** + * Access the domain value lower bound, based on p, used to + * bracket a CDF root. This method is used by + * {@link #inverseCumulativeProbability(double)} to find critical values. + * + * @param p the desired probability for the critical value + * @return domain value lower bound, i.e. + * P(X < lower bound) < p + */ + protected double getDomainLowerBound(double p) { + return 0.0; + } - /** - * Create a F distribution using the given degrees of freedom. - * - * @param numeratorDegreesOfFreedom - * the numerator degrees of freedom. - * @param denominatorDegreesOfFreedom - * the denominator degrees of freedom. - */ - public FDistributionImpl(double numeratorDegreesOfFreedom, - double denominatorDegreesOfFreedom) { - super(); - setNumeratorDegreesOfFreedom(numeratorDegreesOfFreedom); - setDenominatorDegreesOfFreedom(denominatorDegreesOfFreedom); - } + /** + * Access the domain value upper bound, based on p, used to + * bracket a CDF root. This method is used by + * {@link #inverseCumulativeProbability(double)} to find critical values. + * + * @param p the desired probability for the critical value + * @return domain value upper bound, i.e. + * P(X < upper bound) > p + */ + protected double getDomainUpperBound(double p) { + return Double.MAX_VALUE; + } - /** - * For this distribution, X, this method returns P(X < x). - * - * The implementation of this method is based on: - * - * - * @param x - * the value at which the CDF is evaluated. - * @return CDF for this distribution. - * @throws MathException - * if the cumulative probability can not be computed due to - * convergence or other numerical errors. - */ - public double cumulativeProbability(double x) throws MathException { - double ret; - if (x <= 0.0) { - ret = 0.0; - } else { - double n = getNumeratorDegreesOfFreedom(); - double m = getDenominatorDegreesOfFreedom(); - - ret = Beta.regularizedBeta((n * x) / (m + n * x), 0.5 * n, 0.5 * m); - } - return ret; - } - - /** - * For this distribution, X, this method returns the critical point x, such - * that P(X < x) = p. - *

- * Returns 0 for p=0 and Double.POSITIVE_INFINITY for p=1. - *

- * - * @param p - * the desired probability - * @return x, such that P(X < x) = p - * @throws MathException - * if the inverse cumulative probability can not be computed due - * to convergence or other numerical errors. - * @throws IllegalArgumentException - * if p is not a valid probability. - */ - public double inverseCumulativeProbability(final double p) - throws MathException { - if (p == 0) { - return 0d; - } - if (p == 1) { - return Double.POSITIVE_INFINITY; - } - return super.inverseCumulativeProbability(p); - } - - /** - * Access the domain value lower bound, based on p, used to - * bracket a CDF root. This method is used by - * {@link #inverseCumulativeProbability(double)} to find critical values. - * - * @param p - * the desired probability for the critical value - * @return domain value lower bound, i.e. P(X < lower bound) < - * p - */ - protected double getDomainLowerBound(double p) { - return 0.0; - } - - /** - * Access the domain value upper bound, based on p, used to - * bracket a CDF root. This method is used by - * {@link #inverseCumulativeProbability(double)} to find critical values. - * - * @param p - * the desired probability for the critical value - * @return domain value upper bound, i.e. P(X < upper bound) > - * p - */ - protected double getDomainUpperBound(double p) { - return Double.MAX_VALUE; - } - - /** - * Access the initial domain value, based on p, used to bracket - * a CDF root. This method is used by - * {@link #inverseCumulativeProbability(double)} to find critical values. - * - * @param p - * the desired probability for the critical value - * @return initial domain value - */ - protected double getInitialDomain(double p) { - double ret = 1.0; - double d = getDenominatorDegreesOfFreedom(); - if (d > 2.0) { - // use mean - ret = d / (d - 2.0); - } - return ret; - } - - /** - * Modify the numerator degrees of freedom. - * - * @param degreesOfFreedom - * the new numerator degrees of freedom. - * @throws IllegalArgumentException - * if degreesOfFreedom is not positive. - */ - public void setNumeratorDegreesOfFreedom(double degreesOfFreedom) { - if (degreesOfFreedom <= 0.0) { - throw new IllegalArgumentException( - "degrees of freedom must be positive."); - } - this.numeratorDegreesOfFreedom = degreesOfFreedom; - } - - /** - * Access the numerator degrees of freedom. - * - * @return the numerator degrees of freedom. - */ - public double getNumeratorDegreesOfFreedom() { - return numeratorDegreesOfFreedom; - } - - /** - * Modify the denominator degrees of freedom. - * - * @param degreesOfFreedom - * the new denominator degrees of freedom. - * @throws IllegalArgumentException - * if degreesOfFreedom is not positive. - */ - public void setDenominatorDegreesOfFreedom(double degreesOfFreedom) { - if (degreesOfFreedom <= 0.0) { - throw new IllegalArgumentException( - "degrees of freedom must be positive."); - } - this.denominatorDegreesOfFreedom = degreesOfFreedom; - } - - /** - * Access the denominator degrees of freedom. - * - * @return the denominator degrees of freedom. - */ - public double getDenominatorDegreesOfFreedom() { - return denominatorDegreesOfFreedom; - } + /** + * Access the initial domain value, based on p, used to + * bracket a CDF root. This method is used by + * {@link #inverseCumulativeProbability(double)} to find critical values. + * + * @param p the desired probability for the critical value + * @return initial domain value + */ + protected double getInitialDomain(double p) { + double ret = 1.0; + double d = getDenominatorDegreesOfFreedom(); + if (d > 2.0) { + // use mean + ret = d / (d - 2.0); + } + return ret; + } + + /** + * Modify the numerator degrees of freedom. + * @param degreesOfFreedom the new numerator degrees of freedom. + * @throws IllegalArgumentException if degreesOfFreedom is not + * positive. + */ + public void setNumeratorDegreesOfFreedom(double degreesOfFreedom) { + if (degreesOfFreedom <= 0.0) { + throw new IllegalArgumentException( + "degrees of freedom must be positive."); + } + this.numeratorDegreesOfFreedom = degreesOfFreedom; + } + + /** + * Access the numerator degrees of freedom. + * @return the numerator degrees of freedom. + */ + public double getNumeratorDegreesOfFreedom() { + return numeratorDegreesOfFreedom; + } + + /** + * Modify the denominator degrees of freedom. + * @param degreesOfFreedom the new denominator degrees of freedom. + * @throws IllegalArgumentException if degreesOfFreedom is not + * positive. + */ + public void setDenominatorDegreesOfFreedom(double degreesOfFreedom) { + if (degreesOfFreedom <= 0.0) { + throw new IllegalArgumentException( + "degrees of freedom must be positive."); + } + this.denominatorDegreesOfFreedom = degreesOfFreedom; + } + + /** + * Access the denominator degrees of freedom. + * @return the denominator degrees of freedom. + */ + public double getDenominatorDegreesOfFreedom() { + return denominatorDegreesOfFreedom; + } } diff --git a/src/java/org/apache/commons/math/distribution/GammaDistribution.java b/src/java/org/apache/commons/math/distribution/GammaDistribution.java index 21595ec2b..8404d248b 100644 --- a/src/java/org/apache/commons/math/distribution/GammaDistribution.java +++ b/src/java/org/apache/commons/math/distribution/GammaDistribution.java @@ -29,7 +29,7 @@ package org.apache.commons.math.distribution; * * @version $Revision$ $Date$ */ -public interface GammaDistribution extends ContinuousDistribution { +public interface GammaDistribution extends ContinuousDistribution, HasDensity { /** * Modify the shape parameter, alpha. * @param alpha the new shape parameter. @@ -53,4 +53,11 @@ public interface GammaDistribution extends ContinuousDistribution { * @return beta. */ double getBeta(); + + /** + * Return the probability density for a particular point. + * @param x The point at which the density should be computed. + * @return The pdf at point x. + */ + double density(Double x); } diff --git a/src/java/org/apache/commons/math/distribution/GammaDistributionImpl.java b/src/java/org/apache/commons/math/distribution/GammaDistributionImpl.java index ea1282cb9..5007a69d3 100644 --- a/src/java/org/apache/commons/math/distribution/GammaDistributionImpl.java +++ b/src/java/org/apache/commons/math/distribution/GammaDistributionImpl.java @@ -141,7 +141,18 @@ public class GammaDistributionImpl extends AbstractContinuousDistribution public double getBeta() { return beta; } - + + /** + * Return the probability density for a particular point. + * + * @param x The point at which the density should be computed. + * @return The pdf at point x. + */ + public double density(Double x) { + if (x < 0) return 0; + return Math.pow(x / getBeta(), getAlpha() - 1) / getBeta() * Math.exp(-x / getBeta()) / Math.exp(Gamma.logGamma(getAlpha())); + } + /** * Access the domain value lower bound, based on p, used to * bracket a CDF root. This method is used by diff --git a/src/java/org/apache/commons/math/distribution/HasDensity.java b/src/java/org/apache/commons/math/distribution/HasDensity.java new file mode 100644 index 000000000..e414990ca --- /dev/null +++ b/src/java/org/apache/commons/math/distribution/HasDensity.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.distribution; + +import org.apache.commons.math.MathException; + +/** + * Interface that signals that a distribution can compute the probability density function + * for a particular point. + * @param

the type of the point at which density is to be computed, this + * may be for example Double + * @version $Revision$ $Date$ + */ +public interface HasDensity

{ + + /** + * Compute the probability density function. + * @param x point for which the probability density is requested + * @return probability density at point x + * @throws MathException if probability density cannot be computed at specifed point + */ + double density(P x) throws MathException; + +} diff --git a/src/java/org/apache/commons/math/distribution/NormalDistribution.java b/src/java/org/apache/commons/math/distribution/NormalDistribution.java index 6adf5dfbd..2cc611c01 100644 --- a/src/java/org/apache/commons/math/distribution/NormalDistribution.java +++ b/src/java/org/apache/commons/math/distribution/NormalDistribution.java @@ -30,7 +30,7 @@ package org.apache.commons.math.distribution; * * @version $Revision$ $Date$ */ -public interface NormalDistribution extends ContinuousDistribution { +public interface NormalDistribution extends ContinuousDistribution, HasDensity { /** * Access the mean. * @return mean for this distribution @@ -51,4 +51,11 @@ public interface NormalDistribution extends ContinuousDistribution { * @param sd standard deviation for this distribution */ void setStandardDeviation(double sd); + + /** + * Return the probability density for a particular point. + * @param x The point at which the density should be computed. + * @return The pdf at point x. + */ + double density(Double x); } diff --git a/src/java/org/apache/commons/math/distribution/NormalDistributionImpl.java b/src/java/org/apache/commons/math/distribution/NormalDistributionImpl.java index 787b7d0cb..cc3fac70a 100644 --- a/src/java/org/apache/commons/math/distribution/NormalDistributionImpl.java +++ b/src/java/org/apache/commons/math/distribution/NormalDistributionImpl.java @@ -35,12 +35,15 @@ public class NormalDistributionImpl extends AbstractContinuousDistribution /** Serializable version identifier */ private static final long serialVersionUID = 8589540077390120676L; + /** &sqrt;(2 π) */ + private static final double SQRT2PI = Math.sqrt(2 * Math.PI); + /** The mean of this distribution. */ private double mean = 0; /** The standard deviation of this distribution. */ private double standardDeviation = 1; - + /** * Create a normal distribution using the given mean and standard deviation. * @param mean mean for this distribution @@ -97,6 +100,17 @@ public class NormalDistributionImpl extends AbstractContinuousDistribution standardDeviation = sd; } + /** + * Return the probability density for a particular point. + * + * @param x The point at which the density should be computed. + * @return The pdf at point x. + */ + public double density(Double x) { + double x0 = x - getMean(); + return Math.exp(-x0 * x0 / (2 * getStandardDeviation() * getStandardDeviation())) / (getStandardDeviation() * SQRT2PI); + } + /** * For this distribution, X, this method returns P(X < x). * @param x the value at which the CDF is evaluated. diff --git a/src/java/org/apache/commons/math/estimation/AbstractEstimator.java b/src/java/org/apache/commons/math/estimation/AbstractEstimator.java index 8e5fe1ad0..b2b09b463 100644 --- a/src/java/org/apache/commons/math/estimation/AbstractEstimator.java +++ b/src/java/org/apache/commons/math/estimation/AbstractEstimator.java @@ -98,7 +98,7 @@ public abstract class AbstractEstimator implements Estimator { if (++costEvaluations > maxCostEval) { throw new EstimationException("maximal number of evaluations exceeded ({0})", - new Object[] { new Integer(maxCostEval) }); + new Object[] { Integer.valueOf(maxCostEval) }); } cost = 0; @@ -179,10 +179,10 @@ public abstract class AbstractEstimator implements Estimator { try { // compute the covariances matrix - return new RealMatrixImpl(jTj).inverse().getData(); + return ((RealMatrixImpl) new RealMatrixImpl(jTj, false).inverse()).getDataRef(); } catch (InvalidMatrixException ime) { throw new EstimationException("unable to compute covariances: singular problem", - new Object[0]); + null); } } @@ -202,7 +202,7 @@ public abstract class AbstractEstimator implements Estimator { int p = problem.getUnboundParameters().length; if (m <= p) { throw new EstimationException("no degrees of freedom ({0} measurements, {1} parameters)", - new Object[] { new Integer(m), new Integer(p)}); + new Object[] { Integer.valueOf(m), Integer.valueOf(p)}); } double[] errors = new double[problem.getUnboundParameters().length]; final double c = Math.sqrt(getChiSquare(problem) / (m - p)); diff --git a/src/java/org/apache/commons/math/estimation/GaussNewtonEstimator.java b/src/java/org/apache/commons/math/estimation/GaussNewtonEstimator.java index 6361fb04f..7492c042e 100644 --- a/src/java/org/apache/commons/math/estimation/GaussNewtonEstimator.java +++ b/src/java/org/apache/commons/math/estimation/GaussNewtonEstimator.java @@ -20,8 +20,11 @@ package org.apache.commons.math.estimation; import java.io.Serializable; import org.apache.commons.math.linear.InvalidMatrixException; +import org.apache.commons.math.linear.LUDecompositionImpl; import org.apache.commons.math.linear.RealMatrix; import org.apache.commons.math.linear.RealMatrixImpl; +import org.apache.commons.math.linear.RealVector; +import org.apache.commons.math.linear.RealVectorImpl; /** * This class implements a solver for estimation problems. @@ -106,8 +109,8 @@ public class GaussNewtonEstimator extends AbstractEstimator implements Serializa // work matrices double[] grad = new double[parameters.length]; - RealMatrixImpl bDecrement = new RealMatrixImpl(parameters.length, 1); - double[][] bDecrementData = bDecrement.getDataRef(); + RealVectorImpl bDecrement = new RealVectorImpl(parameters.length); + double[] bDecrementData = bDecrement.getDataRef(); RealMatrixImpl wGradGradT = new RealMatrixImpl(parameters.length, parameters.length); double[][] wggData = wGradGradT.getDataRef(); @@ -117,7 +120,7 @@ public class GaussNewtonEstimator extends AbstractEstimator implements Serializa // build the linear problem incrementJacobianEvaluationsCounter(); - RealMatrix b = new RealMatrixImpl(parameters.length, 1); + RealVector b = new RealVectorImpl(parameters.length); RealMatrix a = new RealMatrixImpl(parameters.length, parameters.length); for (int i = 0; i < measurements.length; ++i) { if (! measurements [i].isIgnored()) { @@ -128,7 +131,7 @@ public class GaussNewtonEstimator extends AbstractEstimator implements Serializa // compute the normal equation for (int j = 0; j < parameters.length; ++j) { grad[j] = measurements[i].getPartial(parameters[j]); - bDecrementData[j][0] = weight * residual * grad[j]; + bDecrementData[j] = weight * residual * grad[j]; } // build the contribution matrix for measurement i @@ -150,15 +153,15 @@ public class GaussNewtonEstimator extends AbstractEstimator implements Serializa try { // solve the linearized least squares problem - RealMatrix dX = a.solve(b); + RealVector dX = new LUDecompositionImpl(a).solve(b); // update the estimated parameters for (int i = 0; i < parameters.length; ++i) { - parameters[i].setEstimate(parameters[i].getEstimate() + dX.getEntry(i, 0)); + parameters[i].setEstimate(parameters[i].getEstimate() + dX.getEntry(i)); } } catch(InvalidMatrixException e) { - throw new EstimationException("unable to solve: singular problem", new Object[0]); + throw new EstimationException("unable to solve: singular problem", null); } diff --git a/src/java/org/apache/commons/math/estimation/LevenbergMarquardtEstimator.java b/src/java/org/apache/commons/math/estimation/LevenbergMarquardtEstimator.java index 07bffbad1..7cbb91396 100644 --- a/src/java/org/apache/commons/math/estimation/LevenbergMarquardtEstimator.java +++ b/src/java/org/apache/commons/math/estimation/LevenbergMarquardtEstimator.java @@ -401,16 +401,16 @@ public class LevenbergMarquardtEstimator extends AbstractEstimator implements Se throw new EstimationException("cost relative tolerance is too small ({0})," + " no further reduction in the" + " sum of squares is possible", - new Object[] { new Double(costRelativeTolerance) }); + new Object[] { Double.valueOf(costRelativeTolerance) }); } else if (delta <= 2.2204e-16 * xNorm) { throw new EstimationException("parameters relative tolerance is too small" + " ({0}), no further improvement in" + " the approximate solution is possible", - new Object[] { new Double(parRelativeTolerance) }); + new Object[] { Double.valueOf(parRelativeTolerance) }); } else if (maxCosine <= 2.2204e-16) { throw new EstimationException("orthogonality tolerance is too small ({0})," + " solution is orthogonal to the jacobian", - new Object[] { new Double(orthoTolerance) }); + new Object[] { Double.valueOf(orthoTolerance) }); } } @@ -763,7 +763,7 @@ public class LevenbergMarquardtEstimator extends AbstractEstimator implements Se } if (Double.isInfinite(norm2) || Double.isNaN(norm2)) { throw new EstimationException("unable to perform Q.R decomposition on the {0}x{1} jacobian matrix", - new Object[] { new Integer(rows), new Integer(cols) }); + new Object[] { Integer.valueOf(rows), Integer.valueOf(cols) }); } if (norm2 > ak2) { nextColumn = i; diff --git a/src/java/org/apache/commons/math/estimation/SimpleEstimationProblem.java b/src/java/org/apache/commons/math/estimation/SimpleEstimationProblem.java index 304203e21..1f32600f3 100644 --- a/src/java/org/apache/commons/math/estimation/SimpleEstimationProblem.java +++ b/src/java/org/apache/commons/math/estimation/SimpleEstimationProblem.java @@ -18,7 +18,6 @@ package org.apache.commons.math.estimation; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; /** @@ -46,8 +45,8 @@ public class SimpleEstimationProblem implements EstimationProblem { * Build an empty instance without parameters nor measurements. */ public SimpleEstimationProblem() { - parameters = new ArrayList(); - measurements = new ArrayList(); + parameters = new ArrayList(); + measurements = new ArrayList(); } /** @@ -65,9 +64,8 @@ public class SimpleEstimationProblem implements EstimationProblem { public EstimatedParameter[] getUnboundParameters() { // filter the unbound parameters - List unbound = new ArrayList(parameters.size()); - for (Iterator iterator = parameters.iterator(); iterator.hasNext();) { - EstimatedParameter p = (EstimatedParameter) iterator.next(); + List unbound = new ArrayList(parameters.size()); + for (EstimatedParameter p : parameters) { if (! p.isBound()) { unbound.add(p); } @@ -102,9 +100,9 @@ public class SimpleEstimationProblem implements EstimationProblem { } /** Estimated parameters. */ - private final List parameters; + private final List parameters; /** Measurements. */ - private final List measurements; + private final List measurements; } diff --git a/src/java/org/apache/commons/math/estimation/WeightedMeasurement.java b/src/java/org/apache/commons/math/estimation/WeightedMeasurement.java index aa268c83d..2038799c1 100644 --- a/src/java/org/apache/commons/math/estimation/WeightedMeasurement.java +++ b/src/java/org/apache/commons/math/estimation/WeightedMeasurement.java @@ -51,7 +51,10 @@ import java.io.Serializable; public abstract class WeightedMeasurement implements Serializable { - /** + /** Serializable version identifier. */ + private static final long serialVersionUID = 4360046376796901941L; + + /** * Simple constructor. * Build a measurement with the given parameters, and set its ignore * flag to false. diff --git a/src/java/org/apache/commons/math/fraction/Fraction.java b/src/java/org/apache/commons/math/fraction/Fraction.java index 4f9015cd9..b11b01dfc 100644 --- a/src/java/org/apache/commons/math/fraction/Fraction.java +++ b/src/java/org/apache/commons/math/fraction/Fraction.java @@ -17,6 +17,8 @@ package org.apache.commons.math.fraction; import java.math.BigInteger; + +import org.apache.commons.math.MathRuntimeException; import org.apache.commons.math.util.MathUtils; /** @@ -25,7 +27,7 @@ import org.apache.commons.math.util.MathUtils; * @since 1.1 * @version $Revision$ $Date$ */ -public class Fraction extends Number implements Comparable { +public class Fraction extends Number implements Comparable { /** A fraction representing "1 / 1". */ public static final Fraction ONE = new Fraction(1, 1); @@ -34,7 +36,7 @@ public class Fraction extends Number implements Comparable { public static final Fraction ZERO = new Fraction(0, 1); /** Serializable version identifier */ - private static final long serialVersionUID = -8958519416450949235L; + private static final long serialVersionUID = -5731055832688548463L; /** The denominator. */ private final int denominator; @@ -200,12 +202,13 @@ public class Fraction extends Number implements Comparable { public Fraction(int num, int den) { super(); if (den == 0) { - throw new ArithmeticException("The denominator must not be zero"); + throw MathRuntimeException.createArithmeticException("zero denominator in fraction {0}/{1}", + new Object[] { num, den}); } if (den < 0) { - if (num == Integer.MIN_VALUE || - den == Integer.MIN_VALUE) { - throw new ArithmeticException("overflow: can't negate"); + if (num == Integer.MIN_VALUE || den == Integer.MIN_VALUE) { + throw MathRuntimeException.createArithmeticException("overflow in fraction {0}/{1}, cannot negate", + new Object[] { num, den}); } num = -num; den = -den; @@ -246,13 +249,12 @@ public class Fraction extends Number implements Comparable { * @return -1 if this is less than object, +1 if this is greater * than object, 0 if they are equal. */ - public int compareTo(Object object) { + public int compareTo(Fraction object) { int ret = 0; if (this != object) { - Fraction other = (Fraction)object; double first = doubleValue(); - double second = other.doubleValue(); + double second = object.doubleValue(); if (first < second) { ret = -1; @@ -362,7 +364,8 @@ public class Fraction extends Number implements Comparable { */ public Fraction negate() { if (numerator==Integer.MIN_VALUE) { - throw new ArithmeticException("overflow: too large to negate"); + throw MathRuntimeException.createArithmeticException("overflow in fraction {0}/{1}, cannot negate", + new Object[] { numerator, denominator}); } return new Fraction(-numerator, denominator); } @@ -452,8 +455,8 @@ public class Fraction extends Number implements Comparable { // result is (t/d2) / (u'/d1)(v'/d2) BigInteger w = t.divide(BigInteger.valueOf(d2)); if (w.bitLength() > 31) { - throw new ArithmeticException - ("overflow: numerator too large after multiply"); + throw MathRuntimeException.createArithmeticException("overflow, numerator too large after multiply: {0}", + new Object[] { w }); } return new Fraction (w.intValue(), MathUtils.mulAndCheck(denominator/d1, @@ -501,7 +504,8 @@ public class Fraction extends Number implements Comparable { throw new IllegalArgumentException("The fraction must not be null"); } if (fraction.numerator == 0) { - throw new ArithmeticException("The fraction to divide by must not be zero"); + throw MathRuntimeException.createArithmeticException("the fraction to divide by must not be zero: {0}/{1}", + new Object[] { fraction.numerator, fraction.denominator }); } return multiply(fraction.reciprocal()); } @@ -519,7 +523,8 @@ public class Fraction extends Number implements Comparable { */ public static Fraction getReducedFraction(int numerator, int denominator) { if (denominator == 0) { - throw new ArithmeticException("The denominator must not be zero"); + throw MathRuntimeException.createArithmeticException("zero denominator in fraction {0}/{1}", + new Object[] { numerator, denominator}); } if (numerator==0) { return ZERO; // normalize zero. @@ -531,7 +536,8 @@ public class Fraction extends Number implements Comparable { if (denominator < 0) { if (numerator==Integer.MIN_VALUE || denominator==Integer.MIN_VALUE) { - throw new ArithmeticException("overflow: can't negate"); + throw MathRuntimeException.createArithmeticException("overflow in fraction {0}/{1}, cannot negate", + new Object[] { numerator, denominator}); } numerator = -numerator; denominator = -denominator; diff --git a/src/java/org/apache/commons/math/fraction/FractionConversionException.java b/src/java/org/apache/commons/math/fraction/FractionConversionException.java index 1cde8d592..fdcbb7e85 100644 --- a/src/java/org/apache/commons/math/fraction/FractionConversionException.java +++ b/src/java/org/apache/commons/math/fraction/FractionConversionException.java @@ -39,7 +39,7 @@ public class FractionConversionException extends ConvergenceException { */ public FractionConversionException(double value, int maxIterations) { super("Unable to convert {0} to fraction after {1} iterations", - new Object[] { new Double(value), new Integer(maxIterations) }); + new Object[] { Double.valueOf(value), Integer.valueOf(maxIterations) }); } /** @@ -51,7 +51,7 @@ public class FractionConversionException extends ConvergenceException { */ public FractionConversionException(double value, long p, long q) { super("Overflow trying to convert {0} to fraction ({1}/{2})", - new Object[] { new Double(value), new Long(p), new Long(q) }); + new Object[] { Double.valueOf(value), Long.valueOf(p), Long.valueOf(q) }); } } diff --git a/src/java/org/apache/commons/math/fraction/FractionFormat.java b/src/java/org/apache/commons/math/fraction/FractionFormat.java index b38b679b6..785c45bba 100644 --- a/src/java/org/apache/commons/math/fraction/FractionFormat.java +++ b/src/java/org/apache/commons/math/fraction/FractionFormat.java @@ -26,6 +26,7 @@ import java.text.ParsePosition; import java.util.Locale; import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.MathRuntimeException; /** * Formats a Fraction number in proper format or improper format. The number @@ -244,8 +245,9 @@ public class FractionFormat extends Format implements Serializable { ParsePosition parsePosition = new ParsePosition(0); Fraction result = parse(source, parsePosition); if (parsePosition.getIndex() == 0) { - throw new ParseException("Unparseable fraction number: \"" + - source + "\"", parsePosition.getErrorIndex()); + throw MathRuntimeException.createParseException("unparseable fraction number: \"{0}\"", + new Object[] { source }, + parsePosition.getErrorIndex()); } return result; } diff --git a/src/java/org/apache/commons/math/genetics/Chromosome.java b/src/java/org/apache/commons/math/genetics/Chromosome.java index 713c54d08..5dcbcc25c 100644 --- a/src/java/org/apache/commons/math/genetics/Chromosome.java +++ b/src/java/org/apache/commons/math/genetics/Chromosome.java @@ -18,6 +18,7 @@ package org.apache.commons.math.genetics; /** * Individual in a population. Chromosomes are compared based on their fitness. + * @version $Revision$ $Date$ */ public interface Chromosome { /** diff --git a/src/java/org/apache/commons/math/genetics/ChromosomePair.java b/src/java/org/apache/commons/math/genetics/ChromosomePair.java index e2737654e..0cb095cc0 100644 --- a/src/java/org/apache/commons/math/genetics/ChromosomePair.java +++ b/src/java/org/apache/commons/math/genetics/ChromosomePair.java @@ -18,6 +18,7 @@ package org.apache.commons.math.genetics; /** * A pair of {@link Chromosome} objects. + * @version $Revision$ $Date$ */ public class ChromosomePair { /** the first chromosome in the pair. */ diff --git a/src/java/org/apache/commons/math/genetics/CrossoverPolicy.java b/src/java/org/apache/commons/math/genetics/CrossoverPolicy.java index 6463c5d03..1f19120af 100644 --- a/src/java/org/apache/commons/math/genetics/CrossoverPolicy.java +++ b/src/java/org/apache/commons/math/genetics/CrossoverPolicy.java @@ -19,6 +19,7 @@ package org.apache.commons.math.genetics; /** * Policy used to create a pair of new chromosomes by performing a crossover * operation on a source pair of chromosomes. + * @version $Revision$ $Date$ */ public interface CrossoverPolicy { /** diff --git a/src/java/org/apache/commons/math/genetics/GeneticAlgorithm.java b/src/java/org/apache/commons/math/genetics/GeneticAlgorithm.java index e19628c41..e6994b3d0 100644 --- a/src/java/org/apache/commons/math/genetics/GeneticAlgorithm.java +++ b/src/java/org/apache/commons/math/genetics/GeneticAlgorithm.java @@ -19,6 +19,8 @@ package org.apache.commons.math.genetics; /** * Implementation of a genetic algorithm. All factors that govern the operation * of the algorithm can be configured for a specific problem. + * + * @version $Revision$ $Date$ */ public class GeneticAlgorithm { /** the crossover policy used by the algorithm. */ @@ -98,7 +100,24 @@ public class GeneticAlgorithm { } /** - * Evolve the given population into the next generation. + *

Evolve the given population into the next generation.

+ *

    + *
  1. Get nextGeneration polulation to fill from current + * generation, using its nextGeneration method
  2. + *
  3. Loop until new generation is filled:
  4. + *
    • Apply configured SelectionPolicy to select a pair of parents + * from current
    • + *
    • With probability = {@link #getCrossoverRate()}, apply + * configured {@link CrossoverPolicy} to parents
    • + *
    • With probability = {@link #getMutationRate()}, apply + * configured {@link MutationPolicy} to each of the offspring
    • + *
    • Add offspring individually to nextGeneration, + * space permitting
    • + *
    + *
  5. Return nextGeneration
  6. + *
+ *

+ * * * @param current the current population. * @return the population for the next generation. @@ -111,23 +130,29 @@ public class GeneticAlgorithm { // select parent chromosomes ChromosomePair pair = getSelectionPolicy().select(current); - // apply crossover policy to create two offspring + // crossover? if (Math.random() < getCrossoverRate()) { + // apply crossover policy to create two offspring pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond()); } - // apply mutation policy to first offspring + // mutation? if (Math.random() < getMutationRate()) { - nextGeneration.addChromosome(getMutationPolicy().mutate( - pair.getFirst())); + // apply mutation policy to the chromosomes + pair = new ChromosomePair( + getMutationPolicy().mutate(pair.getFirst()), + getMutationPolicy().mutate(pair.getSecond()) + ); + } - if (nextGeneration.getPopulationSize() < nextGeneration - .getPopulationLimit()) { - // apply mutation policy to second offspring - nextGeneration.addChromosome(getMutationPolicy().mutate( - pair.getSecond())); - } + // add the first chromosome to the population + nextGeneration.addChromosome(pair.getFirst()); + // is there still a place for the second chromosome? + if (nextGeneration.getPopulationSize() < nextGeneration + .getPopulationLimit()) { + // add the second chromosome to the population + nextGeneration.addChromosome(pair.getSecond()); } } diff --git a/src/java/org/apache/commons/math/genetics/MutationPolicy.java b/src/java/org/apache/commons/math/genetics/MutationPolicy.java index 6ea53e41e..06fff0a6c 100644 --- a/src/java/org/apache/commons/math/genetics/MutationPolicy.java +++ b/src/java/org/apache/commons/math/genetics/MutationPolicy.java @@ -18,6 +18,7 @@ package org.apache.commons.math.genetics; /** * Algorithm used to mutate a chrommosome. + * @version $Revision$ $Date$ */ public interface MutationPolicy { diff --git a/src/java/org/apache/commons/math/genetics/Population.java b/src/java/org/apache/commons/math/genetics/Population.java index 648a28dcf..36fdbb7a2 100644 --- a/src/java/org/apache/commons/math/genetics/Population.java +++ b/src/java/org/apache/commons/math/genetics/Population.java @@ -18,6 +18,7 @@ package org.apache.commons.math.genetics; /** * A collection of chromosomes that facilitates generational evolution. + * @version $Revision$ $Date$ */ public interface Population { /** diff --git a/src/java/org/apache/commons/math/genetics/SelectionPolicy.java b/src/java/org/apache/commons/math/genetics/SelectionPolicy.java index 7f564d20b..2b281841f 100644 --- a/src/java/org/apache/commons/math/genetics/SelectionPolicy.java +++ b/src/java/org/apache/commons/math/genetics/SelectionPolicy.java @@ -18,6 +18,7 @@ package org.apache.commons.math.genetics; /** * Algorithm used to select a chromosome pair from a population. + * @version $Revision$ $Date$ */ public interface SelectionPolicy { /** diff --git a/src/java/org/apache/commons/math/genetics/StoppingCondition.java b/src/java/org/apache/commons/math/genetics/StoppingCondition.java index 49e523184..2904554fd 100644 --- a/src/java/org/apache/commons/math/genetics/StoppingCondition.java +++ b/src/java/org/apache/commons/math/genetics/StoppingCondition.java @@ -18,6 +18,7 @@ package org.apache.commons.math.genetics; /** * Algorithm used to determine when to stop evolution. + * @version $Revision$ $Date$ */ public interface StoppingCondition { /** diff --git a/src/java/org/apache/commons/math/geometry/CardanEulerSingularityException.java b/src/java/org/apache/commons/math/geometry/CardanEulerSingularityException.java index d3b34270b..702ad3f8f 100644 --- a/src/java/org/apache/commons/math/geometry/CardanEulerSingularityException.java +++ b/src/java/org/apache/commons/math/geometry/CardanEulerSingularityException.java @@ -35,7 +35,7 @@ public class CardanEulerSingularityException * if false it is related to EulerAngles */ public CardanEulerSingularityException(boolean isCardan) { - super(isCardan ? "Cardan angles singularity" : "Euler angles singularity", new Object[0]); + super(isCardan ? "Cardan angles singularity" : "Euler angles singularity", null); } /** Serializable version identifier */ diff --git a/src/java/org/apache/commons/math/geometry/Rotation.java b/src/java/org/apache/commons/math/geometry/Rotation.java index 118a07dc0..a5171541d 100644 --- a/src/java/org/apache/commons/math/geometry/Rotation.java +++ b/src/java/org/apache/commons/math/geometry/Rotation.java @@ -19,6 +19,8 @@ package org.apache.commons.math.geometry; import java.io.Serializable; +import org.apache.commons.math.MathRuntimeException; + /** * This class implements rotations in a three-dimensional space. * @@ -88,14 +90,23 @@ import java.io.Serializable; public class Rotation implements Serializable { - /** Build the identity rotation. - */ - public Rotation() { - q0 = 1; - q1 = 0; - q2 = 0; - q3 = 0; - } + /** Identity rotation. */ + public static final Rotation IDENTITY = new Rotation(1.0, 0.0, 0.0, 0.0, false); + + /** Serializable version identifier */ + private static final long serialVersionUID = -2153622329907944313L; + + /** Scalar coordinate of the quaternion. */ + private final double q0; + + /** First coordinate of the vectorial part of the quaternion. */ + private final double q1; + + /** Second coordinate of the vectorial part of the quaternion. */ + private final double q2; + + /** Third coordinate of the vectorial part of the quaternion. */ + private final double q3; /** Build a rotation from the quaternion coordinates. *

A rotation can be built from a normalized quaternion, @@ -145,7 +156,8 @@ public class Rotation implements Serializable { double norm = axis.getNorm(); if (norm == 0) { - throw new ArithmeticException("zero norm for rotation axis"); + throw MathRuntimeException.createArithmeticException("zero norm for rotation axis", + null); } double halfAngle = -0.5 * angle; @@ -453,10 +465,10 @@ public class Rotation implements Serializable { /** Build a rotation from three Cardan or Euler elementary rotations. *

Cardan rotations are three successive rotations around the - * canonical axes X, Y and Z, each axis beeing used once. There are + * canonical axes X, Y and Z, each axis being used once. There are * 6 such sets of rotations (XYZ, XZY, YXZ, YZX, ZXY and ZYX). Euler * rotations are three successive rotations around the canonical - * axes X, Y and Z, the first and last rotations beeing around the + * axes X, Y and Z, the first and last rotations being around the * same axis. There are 6 such sets of rotations (XYX, XZX, YXY, * YZY, ZXZ and ZYZ), the most popular one being ZXZ.

*

Beware that many people routinely use the term Euler angles even @@ -592,8 +604,8 @@ public class Rotation implements Serializable { // (-r) (Vector3D.plusI) coordinates are : // cos (psi) cos (theta), -sin (psi) cos (theta), sin (theta) // and we can choose to have theta in the interval [-PI/2 ; +PI/2] - Vector3D v1 = applyTo(Vector3D.plusK); - Vector3D v2 = applyInverseTo(Vector3D.plusI); + Vector3D v1 = applyTo(Vector3D.PLUS_K); + Vector3D v2 = applyInverseTo(Vector3D.PLUS_I); if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) { throw new CardanEulerSingularityException(true); } @@ -610,8 +622,8 @@ public class Rotation implements Serializable { // (-r) (Vector3D.plusI) coordinates are : // cos (theta) cos (psi), -sin (psi), sin (theta) cos (psi) // and we can choose to have psi in the interval [-PI/2 ; +PI/2] - Vector3D v1 = applyTo(Vector3D.plusJ); - Vector3D v2 = applyInverseTo(Vector3D.plusI); + Vector3D v1 = applyTo(Vector3D.PLUS_J); + Vector3D v2 = applyInverseTo(Vector3D.PLUS_I); if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) { throw new CardanEulerSingularityException(true); } @@ -628,8 +640,8 @@ public class Rotation implements Serializable { // (-r) (Vector3D.plusJ) coordinates are : // sin (psi) cos (phi), cos (psi) cos (phi), -sin (phi) // and we can choose to have phi in the interval [-PI/2 ; +PI/2] - Vector3D v1 = applyTo(Vector3D.plusK); - Vector3D v2 = applyInverseTo(Vector3D.plusJ); + Vector3D v1 = applyTo(Vector3D.PLUS_K); + Vector3D v2 = applyInverseTo(Vector3D.PLUS_J); if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) { throw new CardanEulerSingularityException(true); } @@ -646,8 +658,8 @@ public class Rotation implements Serializable { // (-r) (Vector3D.plusJ) coordinates are : // sin (psi), cos (phi) cos (psi), -sin (phi) cos (psi) // and we can choose to have psi in the interval [-PI/2 ; +PI/2] - Vector3D v1 = applyTo(Vector3D.plusI); - Vector3D v2 = applyInverseTo(Vector3D.plusJ); + Vector3D v1 = applyTo(Vector3D.PLUS_I); + Vector3D v2 = applyInverseTo(Vector3D.PLUS_J); if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) { throw new CardanEulerSingularityException(true); } @@ -664,8 +676,8 @@ public class Rotation implements Serializable { // (-r) (Vector3D.plusK) coordinates are : // -sin (theta) cos (phi), sin (phi), cos (theta) cos (phi) // and we can choose to have phi in the interval [-PI/2 ; +PI/2] - Vector3D v1 = applyTo(Vector3D.plusJ); - Vector3D v2 = applyInverseTo(Vector3D.plusK); + Vector3D v1 = applyTo(Vector3D.PLUS_J); + Vector3D v2 = applyInverseTo(Vector3D.PLUS_K); if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) { throw new CardanEulerSingularityException(true); } @@ -682,8 +694,8 @@ public class Rotation implements Serializable { // (-r) (Vector3D.plusK) coordinates are : // -sin (theta), sin (phi) cos (theta), cos (phi) cos (theta) // and we can choose to have theta in the interval [-PI/2 ; +PI/2] - Vector3D v1 = applyTo(Vector3D.plusI); - Vector3D v2 = applyInverseTo(Vector3D.plusK); + Vector3D v1 = applyTo(Vector3D.PLUS_I); + Vector3D v2 = applyInverseTo(Vector3D.PLUS_K); if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) { throw new CardanEulerSingularityException(true); } @@ -700,8 +712,8 @@ public class Rotation implements Serializable { // (-r) (Vector3D.plusI) coordinates are : // cos (theta), sin (theta) sin (phi2), sin (theta) cos (phi2) // and we can choose to have theta in the interval [0 ; PI] - Vector3D v1 = applyTo(Vector3D.plusI); - Vector3D v2 = applyInverseTo(Vector3D.plusI); + Vector3D v1 = applyTo(Vector3D.PLUS_I); + Vector3D v2 = applyInverseTo(Vector3D.PLUS_I); if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) { throw new CardanEulerSingularityException(false); } @@ -718,8 +730,8 @@ public class Rotation implements Serializable { // (-r) (Vector3D.plusI) coordinates are : // cos (psi), -sin (psi) cos (phi2), sin (psi) sin (phi2) // and we can choose to have psi in the interval [0 ; PI] - Vector3D v1 = applyTo(Vector3D.plusI); - Vector3D v2 = applyInverseTo(Vector3D.plusI); + Vector3D v1 = applyTo(Vector3D.PLUS_I); + Vector3D v2 = applyInverseTo(Vector3D.PLUS_I); if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) { throw new CardanEulerSingularityException(false); } @@ -736,8 +748,8 @@ public class Rotation implements Serializable { // (-r) (Vector3D.plusJ) coordinates are : // sin (phi) sin (theta2), cos (phi), -sin (phi) cos (theta2) // and we can choose to have phi in the interval [0 ; PI] - Vector3D v1 = applyTo(Vector3D.plusJ); - Vector3D v2 = applyInverseTo(Vector3D.plusJ); + Vector3D v1 = applyTo(Vector3D.PLUS_J); + Vector3D v2 = applyInverseTo(Vector3D.PLUS_J); if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) { throw new CardanEulerSingularityException(false); } @@ -754,8 +766,8 @@ public class Rotation implements Serializable { // (-r) (Vector3D.plusJ) coordinates are : // sin (psi) cos (theta2), cos (psi), sin (psi) sin (theta2) // and we can choose to have psi in the interval [0 ; PI] - Vector3D v1 = applyTo(Vector3D.plusJ); - Vector3D v2 = applyInverseTo(Vector3D.plusJ); + Vector3D v1 = applyTo(Vector3D.PLUS_J); + Vector3D v2 = applyInverseTo(Vector3D.PLUS_J); if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) { throw new CardanEulerSingularityException(false); } @@ -772,8 +784,8 @@ public class Rotation implements Serializable { // (-r) (Vector3D.plusK) coordinates are : // sin (phi) sin (psi2), sin (phi) cos (psi2), cos (phi) // and we can choose to have phi in the interval [0 ; PI] - Vector3D v1 = applyTo(Vector3D.plusK); - Vector3D v2 = applyInverseTo(Vector3D.plusK); + Vector3D v1 = applyTo(Vector3D.PLUS_K); + Vector3D v2 = applyInverseTo(Vector3D.PLUS_K); if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) { throw new CardanEulerSingularityException(false); } @@ -790,8 +802,8 @@ public class Rotation implements Serializable { // (-r) (Vector3D.plusK) coordinates are : // -sin (theta) cos (psi2), sin (theta) sin (psi2), cos (theta) // and we can choose to have theta in the interval [0 ; PI] - Vector3D v1 = applyTo(Vector3D.plusK); - Vector3D v2 = applyInverseTo(Vector3D.plusK); + Vector3D v1 = applyTo(Vector3D.PLUS_K); + Vector3D v2 = applyInverseTo(Vector3D.PLUS_K); if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) { throw new CardanEulerSingularityException(false); } @@ -1017,19 +1029,32 @@ public class Rotation implements Serializable { }); } - /** Scalar coordinate of the quaternion. */ - private final double q0; - - /** First coordinate of the vectorial part of the quaternion. */ - private final double q1; - - /** Second coordinate of the vectorial part of the quaternion. */ - private final double q2; - - /** Third coordinate of the vectorial part of the quaternion. */ - private final double q3; - - /** Serializable version identifier */ - private static final long serialVersionUID = 8225864499430109352L; + /** Compute the distance between two rotations. + *

The distance is intended here as a way to check if two + * rotations are almost similar (i.e. they transform vectors the same way) + * or very different. It is mathematically defined as the angle of + * the rotation r that prepended to one of the rotations gives the other + * one:

+ *
+   *        r1(r) = r2
+   * 
+ *

This distance is an angle between 0 and π. Its value is the smallest + * possible upper bound of the angle in radians between r1(v) + * and r2(v) for all possible vectors v. This upper bound is + * reached for some v. The distance is equal to 0 if and only if the two + * rotations are identical.

+ *

Comparing two rotations should always be done using this value rather + * than for example comparing the components of the quaternions. It is much + * more stable, and has a geometric meaning. Also comparing quaternions + * components is error prone since for example quaternions (0.36, 0.48, -0.48, -0.64) + * and (-0.36, -0.48, 0.48, 0.64) represent exactly the same rotation despite + * their components are different (they are exact opposites).

+ * @param r1 first rotation + * @param r2 second rotation + * @return distance between r1 and r2 + */ + public static double distance(Rotation r1, Rotation r2) { + return r1.applyInverseTo(r2).getAngle(); + } } diff --git a/src/java/org/apache/commons/math/geometry/RotationOrder.java b/src/java/org/apache/commons/math/geometry/RotationOrder.java index 34e12a148..519b42e40 100644 --- a/src/java/org/apache/commons/math/geometry/RotationOrder.java +++ b/src/java/org/apache/commons/math/geometry/RotationOrder.java @@ -80,84 +80,84 @@ public final class RotationOrder { * around Z */ public static final RotationOrder XYZ = - new RotationOrder("XYZ", Vector3D.plusI, Vector3D.plusJ, Vector3D.plusK); + new RotationOrder("XYZ", Vector3D.PLUS_I, Vector3D.PLUS_J, Vector3D.PLUS_K); /** Set of Cardan angles. * this ordered set of rotations is around X, then around Z, then * around Y */ public static final RotationOrder XZY = - new RotationOrder("XZY", Vector3D.plusI, Vector3D.plusK, Vector3D.plusJ); + new RotationOrder("XZY", Vector3D.PLUS_I, Vector3D.PLUS_K, Vector3D.PLUS_J); /** Set of Cardan angles. * this ordered set of rotations is around Y, then around X, then * around Z */ public static final RotationOrder YXZ = - new RotationOrder("YXZ", Vector3D.plusJ, Vector3D.plusI, Vector3D.plusK); + new RotationOrder("YXZ", Vector3D.PLUS_J, Vector3D.PLUS_I, Vector3D.PLUS_K); /** Set of Cardan angles. * this ordered set of rotations is around Y, then around Z, then * around X */ public static final RotationOrder YZX = - new RotationOrder("YZX", Vector3D.plusJ, Vector3D.plusK, Vector3D.plusI); + new RotationOrder("YZX", Vector3D.PLUS_J, Vector3D.PLUS_K, Vector3D.PLUS_I); /** Set of Cardan angles. * this ordered set of rotations is around Z, then around X, then * around Y */ public static final RotationOrder ZXY = - new RotationOrder("ZXY", Vector3D.plusK, Vector3D.plusI, Vector3D.plusJ); + new RotationOrder("ZXY", Vector3D.PLUS_K, Vector3D.PLUS_I, Vector3D.PLUS_J); /** Set of Cardan angles. * this ordered set of rotations is around Z, then around Y, then * around X */ public static final RotationOrder ZYX = - new RotationOrder("ZYX", Vector3D.plusK, Vector3D.plusJ, Vector3D.plusI); + new RotationOrder("ZYX", Vector3D.PLUS_K, Vector3D.PLUS_J, Vector3D.PLUS_I); /** Set of Euler angles. * this ordered set of rotations is around X, then around Y, then * around X */ public static final RotationOrder XYX = - new RotationOrder("XYX", Vector3D.plusI, Vector3D.plusJ, Vector3D.plusI); + new RotationOrder("XYX", Vector3D.PLUS_I, Vector3D.PLUS_J, Vector3D.PLUS_I); /** Set of Euler angles. * this ordered set of rotations is around X, then around Z, then * around X */ public static final RotationOrder XZX = - new RotationOrder("XZX", Vector3D.plusI, Vector3D.plusK, Vector3D.plusI); + new RotationOrder("XZX", Vector3D.PLUS_I, Vector3D.PLUS_K, Vector3D.PLUS_I); /** Set of Euler angles. * this ordered set of rotations is around Y, then around X, then * around Y */ public static final RotationOrder YXY = - new RotationOrder("YXY", Vector3D.plusJ, Vector3D.plusI, Vector3D.plusJ); + new RotationOrder("YXY", Vector3D.PLUS_J, Vector3D.PLUS_I, Vector3D.PLUS_J); /** Set of Euler angles. * this ordered set of rotations is around Y, then around Z, then * around Y */ public static final RotationOrder YZY = - new RotationOrder("YZY", Vector3D.plusJ, Vector3D.plusK, Vector3D.plusJ); + new RotationOrder("YZY", Vector3D.PLUS_J, Vector3D.PLUS_K, Vector3D.PLUS_J); /** Set of Euler angles. * this ordered set of rotations is around Z, then around X, then * around Z */ public static final RotationOrder ZXZ = - new RotationOrder("ZXZ", Vector3D.plusK, Vector3D.plusI, Vector3D.plusK); + new RotationOrder("ZXZ", Vector3D.PLUS_K, Vector3D.PLUS_I, Vector3D.PLUS_K); /** Set of Euler angles. * this ordered set of rotations is around Z, then around Y, then * around Z */ public static final RotationOrder ZYZ = - new RotationOrder("ZYZ", Vector3D.plusK, Vector3D.plusJ, Vector3D.plusK); + new RotationOrder("ZYZ", Vector3D.PLUS_K, Vector3D.PLUS_J, Vector3D.PLUS_K); /** Name of the rotations order. */ private final String name; diff --git a/src/java/org/apache/commons/math/geometry/Vector3D.java b/src/java/org/apache/commons/math/geometry/Vector3D.java index 8616bc11d..9f89fa86b 100644 --- a/src/java/org/apache/commons/math/geometry/Vector3D.java +++ b/src/java/org/apache/commons/math/geometry/Vector3D.java @@ -19,6 +19,9 @@ package org.apache.commons.math.geometry; import java.io.Serializable; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.util.MathUtils; + /** * This class implements vectors in a three-dimensional space. *

Instance of this class are guaranteed to be immutable.

@@ -29,35 +32,53 @@ import java.io.Serializable; public class Vector3D implements Serializable { + /** Null vector (coordinates: 0, 0, 0). */ + public static final Vector3D ZERO = new Vector3D(0, 0, 0); + /** First canonical vector (coordinates: 1, 0, 0). */ - public static final Vector3D plusI = new Vector3D(1, 0, 0); + public static final Vector3D PLUS_I = new Vector3D(1, 0, 0); /** Opposite of the first canonical vector (coordinates: -1, 0, 0). */ - public static final Vector3D minusI = new Vector3D(-1, 0, 0); + public static final Vector3D MINUS_I = new Vector3D(-1, 0, 0); /** Second canonical vector (coordinates: 0, 1, 0). */ - public static final Vector3D plusJ = new Vector3D(0, 1, 0); + public static final Vector3D PLUS_J = new Vector3D(0, 1, 0); /** Opposite of the second canonical vector (coordinates: 0, -1, 0). */ - public static final Vector3D minusJ = new Vector3D(0, -1, 0); + public static final Vector3D MINUS_J = new Vector3D(0, -1, 0); /** Third canonical vector (coordinates: 0, 0, 1). */ - public static final Vector3D plusK = new Vector3D(0, 0, 1); + public static final Vector3D PLUS_K = new Vector3D(0, 0, 1); /** Opposite of the third canonical vector (coordinates: 0, 0, -1). */ - public static final Vector3D minusK = new Vector3D(0, 0, -1); + public static final Vector3D MINUS_K = new Vector3D(0, 0, -1); - /** Null vector (coordinates: 0, 0, 0). */ - public static final Vector3D zero = new Vector3D(0, 0, 0); + /** A vector with all coordinates set to NaN. */ + public static final Vector3D NaN = new Vector3D(Double.NaN, Double.NaN, Double.NaN); - /** Simple constructor. - * Build a null vector. - */ - public Vector3D() { - x = 0; - y = 0; - z = 0; - } + /** A vector with all coordinates set to positive infinity. */ + public static final Vector3D POSITIVE_INFINITY = + new Vector3D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); + + /** A vector with all coordinates set to negative infinity. */ + public static final Vector3D NEGATIVE_INFINITY = + new Vector3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); + + /** Default format. */ + private static final Vector3DFormat DEFAULT_FORMAT = + Vector3DFormat.getInstance(); + + /** Serializable version identifier. */ + private static final long serialVersionUID = 5133268763396045979L; + + /** Abscissa. */ + private final double x; + + /** Ordinate. */ + private final double y; + + /** Height. */ + private final double z; /** Simple constructor. * Build a vector from its coordinates @@ -182,6 +203,13 @@ public class Vector3D return Math.sqrt (x * x + y * y + z * z); } + /** Get the square of the norm for the vector. + * @return square of the euclidian norm for the vector + */ + public double getNormSq() { + return x * x + y * y + z * z; + } + /** Get the azimuth of the vector. * @return azimuth (α) of the vector, between -π and +π * @see #Vector3D(double, double) @@ -239,7 +267,8 @@ public class Vector3D public Vector3D normalize() { double s = getNorm(); if (s == 0) { - throw new ArithmeticException("cannot normalize a zero norm vector"); + throw MathRuntimeException.createArithmeticException("cannot normalize a zero norm vector", + null); } return scalarMultiply(1 / s); } @@ -263,7 +292,7 @@ public class Vector3D double threshold = 0.6 * getNorm(); if (threshold == 0) { - throw new ArithmeticException("null norm"); + throw MathRuntimeException.createArithmeticException("zero norm", null); } if ((x >= -threshold) && (x <= threshold)) { @@ -293,7 +322,7 @@ public class Vector3D double normProduct = v1.getNorm() * v2.getNorm(); if (normProduct == 0) { - throw new ArithmeticException("null norm"); + throw MathRuntimeException.createArithmeticException("zero norm", null); } double dot = dotProduct(v1, v2); @@ -327,6 +356,83 @@ public class Vector3D return new Vector3D(a * x, a * y, a * z); } + /** + * Returns true if any coordinate of this vector is NaN; false otherwise + * @return true if any coordinate of this vector is NaN; false otherwise + */ + public boolean isNaN() { + return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z); + } + + /** + * Returns true if any coordinate of this vector is infinite and none are NaN; + * false otherwise + * @return true if any coordinate of this vector is infinite and none are NaN; + * false otherwise + */ + public boolean isInfinite() { + return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y) || Double.isInfinite(z)); + } + + /** + * Test for the equality of two 3D vectors. + *

+ * If all coordinates of two 3D vectors are exactly the same, and none are + * Double.NaN, the two 3D vectors are considered to be equal. + *

+ *

+ * NaN coordinates are considered to affect globally the vector + * and be equals to each other - i.e, if either (or all) coordinates of the + * 3D vector are equal to Double.NaN, the 3D vector is equal to + * {@link #NaN}. + *

+ * + * @param other Object to test for equality to this + * @return true if two 3D vector objects are equal, false if + * object is null, not an instance of Vector3D, or + * not equal to this Vector3D instance + * + */ + public boolean equals(Object other) { + + if (this == other) { + return true; + } + + if (other == null) { + return false; + } + + try { + + final Vector3D rhs = (Vector3D)other; + if (rhs.isNaN()) { + return this.isNaN(); + } + + return (x == rhs.x) && (y == rhs.y) && (z == rhs.z); + + } catch (ClassCastException ex) { + // ignore exception + return false; + } + + } + + /** + * Get a hashCode for the 3D vector. + *

+ * All NaN values have the same hash code.

+ * + * @return a hash code value for this object + */ + public int hashCode() { + if (isNaN()) { + return 8; + } + return 31 * (23 * MathUtils.hash(x) + 19 * MathUtils.hash(y) + MathUtils.hash(z)); + } + /** Compute the dot-product of two vectors. * @param v1 first vector * @param v2 second vector @@ -347,17 +453,41 @@ public class Vector3D v1.x * v2.y - v1.y * v2.x); } - /** Abscissa. */ - private final double x; + /** Compute the distance between two vectors. + *

Calling this method is equivalent to calling: + * v1.subtract(v2).getNorm() except that no intermediate + * vector is built

+ * @param v1 first vector + * @param v2 second vector + * @return the distance between v1 and v2 + */ + public static double distance(Vector3D v1, Vector3D v2) { + final double dx = v2.x - v1.x; + final double dy = v2.y - v1.y; + final double dz = v2.z - v1.z; + return Math.sqrt(dx * dx + dy * dy + dz * dz); + } - /** Ordinate. */ - private final double y; - - /** Height. */ - private final double z; - - /** Serializable version identifier */ - private static final long serialVersionUID = -5721105387745193385L; + /** Compute the square of the distance between two vectors. + *

Calling this method is equivalent to calling: + * v1.subtract(v2).getNormSq() except that no intermediate + * vector is built

+ * @param v1 first vector + * @param v2 second vector + * @return the square of the distance between v1 and v2 + */ + public static double distanceSq(Vector3D v1, Vector3D v2) { + final double dx = v2.x - v1.x; + final double dy = v2.y - v1.y; + final double dz = v2.z - v1.z; + return dx * dx + dy * dy + dz * dz; + } + /** Get a string representation of this vector. + * @return a string representation of this vector + */ + public String toString() { + return DEFAULT_FORMAT.format(this); + } } diff --git a/src/java/org/apache/commons/math/geometry/Vector3DFormat.java b/src/java/org/apache/commons/math/geometry/Vector3DFormat.java new file mode 100644 index 000000000..417e2da0d --- /dev/null +++ b/src/java/org/apache/commons/math/geometry/Vector3DFormat.java @@ -0,0 +1,339 @@ +/* + * 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.geometry; + +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Locale; + +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.util.CompositeFormat; + +/** + * Formats a 3D vector in components list format "{x; y; z}". + *

The prefix and suffix "{" and "}" and the separator "; " can be replaced by + * any user-defined strings. The number format for components can be configured.

+ *

White space is ignored at parse time, even if it is in the prefix, suffix + * or separator specifications. So even if the default separator does include a space + * character that is used at format time, both input string "{1;1;1}" and + * " { 1 ; 1 ; 1 } " will be parsed without error and the same vector will be + * returned. In the second case, however, the parse position after parsing will be + * just after the closing curly brace, i.e. just before the trailing space.

+ * + * @version $Revision$ $Date$ + */ +public class Vector3DFormat extends CompositeFormat { + + /** Serializable version identifier */ + private static final long serialVersionUID = -5447606608652576301L; + + /** The default prefix: "{". */ + private static final String DEFAULT_PREFIX = "{"; + + /** The default suffix: "}". */ + private static final String DEFAULT_SUFFIX = "}"; + + /** The default separator: ", ". */ + private static final String DEFAULT_SEPARATOR = "; "; + + /** Prefix. */ + private final String prefix; + + /** Suffix. */ + private final String suffix; + + /** Separator. */ + private final String separator; + + /** Trimmed prefix. */ + private final String trimmedPrefix; + + /** Trimmed suffix. */ + private final String trimmedSuffix; + + /** Trimmed separator. */ + private final String trimmedSeparator; + + /** The format used for components. */ + private NumberFormat format; + + /** + * Create an instance with default settings. + *

The instance uses the default prefix, suffix and separator: + * "{", "}", and "; " and the default number format for components.

+ */ + public Vector3DFormat() { + this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, getDefaultNumberFormat()); + } + + /** + * Create an instance with a custom number format for components. + * @param format the custom format for components. + */ + public Vector3DFormat(final NumberFormat format) { + this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format); + } + + /** + * Create an instance with custom prefix, suffix and separator. + * @param prefix prefix to use instead of the default "{" + * @param suffix suffix to use instead of the default "}" + * @param separator separator to use instead of the default "; " + */ + public Vector3DFormat(final String prefix, final String suffix, + final String separator) { + this(prefix, suffix, separator, getDefaultNumberFormat()); + } + + /** + * Create an instance with custom prefix, suffix, separator and format + * for components. + * @param prefix prefix to use instead of the default "{" + * @param suffix suffix to use instead of the default "}" + * @param separator separator to use instead of the default "; " + * @param format the custom format for components. + */ + public Vector3DFormat(final String prefix, final String suffix, + final String separator, final NumberFormat format) { + this.prefix = prefix; + this.suffix = suffix; + this.separator = separator; + trimmedPrefix = prefix.trim(); + trimmedSuffix = suffix.trim(); + trimmedSeparator = separator.trim(); + this.format = format; + } + + /** + * Get the set of locales for which 3D vectors formats are available. + *

This is the same set as the {@link NumberFormat} set.

+ * @return available 3D vector format locales. + */ + public static Locale[] getAvailableLocales() { + return NumberFormat.getAvailableLocales(); + } + + /** + * Get the format prefix. + * @return format prefix. + */ + public String getPrefix() { + return prefix; + } + + /** + * Get the format suffix. + * @return format suffix. + */ + public String getSuffix() { + return suffix; + } + + /** + * Get the format separator between components. + * @return format separator. + */ + public String getSeparator() { + return separator; + } + + /** + * Get the components format. + * @return components format. + */ + public NumberFormat getFormat() { + return format; + } + + /** + * Returns the default 3D vector format for the current locale. + * @return the default 3D vector format. + */ + public static Vector3DFormat getInstance() { + return getInstance(Locale.getDefault()); + } + + /** + * Returns the default 3D vector format for the given locale. + * @param locale the specific locale used by the format. + * @return the 3D vector format specific to the given locale. + */ + public static Vector3DFormat getInstance(final Locale locale) { + return new Vector3DFormat(getDefaultNumberFormat(locale)); + } + + /** + * This static method calls {@link #format(Object)} on a default instance of + * Vector3DFormat. + * + * @param v Vector3D object to format + * @return A formatted vector + */ + public static String formatVector3D(Vector3D v) { + return getInstance().format(v); + } + + /** + * Formats a {@link Vector3D} object to produce a string. + * @param vector the object to format. + * @param toAppendTo where the text is to be appended + * @param pos On input: an alignment field, if desired. On output: the + * offsets of the alignment field + * @return the value passed in as toAppendTo. + */ + public StringBuffer format(Vector3D vector, StringBuffer toAppendTo, + FieldPosition pos) { + + pos.setBeginIndex(0); + pos.setEndIndex(0); + + // format prefix + toAppendTo.append(prefix); + + // format components + formatDouble(vector.getX(), format, toAppendTo, pos); + toAppendTo.append(separator); + formatDouble(vector.getY(), format, toAppendTo, pos); + toAppendTo.append(separator); + formatDouble(vector.getZ(), format, toAppendTo, pos); + + // format suffix + toAppendTo.append(suffix); + + return toAppendTo; + + } + + /** + * Formats a object to produce a string. + *

obj must be a {@link Vector3D} object. Any other type of + * object will result in an {@link IllegalArgumentException} being thrown.

+ * @param obj the object to format. + * @param toAppendTo where the text is to be appended + * @param pos On input: an alignment field, if desired. On output: the + * offsets of the alignment field + * @return the value passed in as toAppendTo. + * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition) + * @throws IllegalArgumentException is obj is not a valid type. + */ + public StringBuffer format(Object obj, StringBuffer toAppendTo, + FieldPosition pos) { + + if (obj instanceof Vector3D) { + return format( (Vector3D)obj, toAppendTo, pos); + } + + throw new IllegalArgumentException("Cannot format given Object as a Vector3D"); + + } + + /** + * Parses a string to produce a {@link Vector3D} object. + * @param source the string to parse + * @return the parsed {@link Vector3D} object. + * @exception ParseException if the beginning of the specified string + * cannot be parsed. + */ + public Vector3D parse(String source) throws ParseException { + ParsePosition parsePosition = new ParsePosition(0); + Vector3D result = parse(source, parsePosition); + if (parsePosition.getIndex() == 0) { + throw MathRuntimeException.createParseException("unparseable 3D vector: \"{0}\"", + new Object[] { source }, + parsePosition.getErrorIndex()); + } + return result; + } + + /** + * Parses a string to produce a {@link Vector3D} object. + * @param source the string to parse + * @param pos input/ouput parsing parameter. + * @return the parsed {@link Vector3D} object. + */ + public Vector3D parse(String source, ParsePosition pos) { + int initialIndex = pos.getIndex(); + + // parse prefix + parseAndIgnoreWhitespace(source, pos); + if (!parseFixedstring(source, trimmedPrefix, pos)) { + return null; + } + + // parse X component + parseAndIgnoreWhitespace(source, pos); + Number x = parseNumber(source, format, pos); + if (x == null) { + // invalid abscissa + // set index back to initial, error index should already be set + pos.setIndex(initialIndex); + return null; + } + + // parse Y component + parseAndIgnoreWhitespace(source, pos); + if (!parseFixedstring(source, trimmedSeparator, pos)) { + return null; + } + parseAndIgnoreWhitespace(source, pos); + Number y = parseNumber(source, format, pos); + if (y == null) { + // invalid ordinate + // set index back to initial, error index should already be set + pos.setIndex(initialIndex); + return null; + } + + // parse Z component + parseAndIgnoreWhitespace(source, pos); + if (!parseFixedstring(source, trimmedSeparator, pos)) { + return null; + } + parseAndIgnoreWhitespace(source, pos); + Number z = parseNumber(source, format, pos); + if (z == null) { + // invalid height + // set index back to initial, error index should already be set + pos.setIndex(initialIndex); + return null; + } + + // parse suffix + parseAndIgnoreWhitespace(source, pos); + if (!parseFixedstring(source, trimmedSuffix, pos)) { + return null; + } + + return new Vector3D(x.doubleValue(), y.doubleValue(), z.doubleValue()); + + } + + /** + * Parses a string to produce a object. + * @param source the string to parse + * @param pos input/ouput parsing parameter. + * @return the parsed object. + * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition) + */ + public Object parseObject(String source, ParsePosition pos) { + return parse(source, pos); + } + +} diff --git a/src/java/org/apache/commons/math/linear/BiDiagonalTransformer.java b/src/java/org/apache/commons/math/linear/BiDiagonalTransformer.java new file mode 100644 index 000000000..c6353f7f8 --- /dev/null +++ b/src/java/org/apache/commons/math/linear/BiDiagonalTransformer.java @@ -0,0 +1,393 @@ +/* + * 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.linear; + +import java.io.Serializable; + +/** + * Class transforming any matrix to bi-diagonal shape. + *

Any m × n matrix A can be written as the product of three matrices: + * A = U × B × VT with U an m × m orthogonal matrix, + * B an m × n bi-diagonal matrix (lower diagonal if m < n, upper diagonal + * otherwise), and V an n × n orthogonal matrix.

+ *

Transformation to bi-diagonal shape is often not a goal by itself, but it is + * an intermediate step in more general decomposition algorithms like {@link + * SingularValueDecomposition Singular Value Decomposition}. This class is therefore + * intended for internal use by the library and is not public. As a consequence of + * this explicitly limited scope, many methods directly returns references to + * internal arrays, not copies.

+ * @version $Revision$ $Date$ + * @since 2.0 + */ +class BiDiagonalTransformer implements Serializable { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 8935390784125343332L; + + /** Householder vectors. */ + private final double householderVectors[][]; + + /** Main diagonal. */ + private final double[] main; + + /** Secondary diagonal. */ + private final double[] secondary; + + /** Cached value of U. */ + private RealMatrix cachedU; + + /** Cached value of B. */ + private RealMatrix cachedB; + + /** Cached value of V. */ + private RealMatrix cachedV; + + /** + * Build the transformation to bi-diagonal shape of a matrix. + * @param matrix the matrix to transform. + */ + public BiDiagonalTransformer(RealMatrix matrix) { + + final int m = matrix.getRowDimension(); + final int n = matrix.getColumnDimension(); + final int p = Math.min(m, n); + householderVectors = matrix.getData(); + main = new double[p]; + secondary = new double[p - 1]; + cachedU = null; + cachedB = null; + cachedV = null; + + // transform matrix + if (m >= n) { + transformToUpperBiDiagonal(); + } else { + transformToLowerBiDiagonal(); + } + + } + + /** + * Returns the matrix U of the transform. + *

U is an orthogonal matrix, i.e. its transpose is also its inverse.

+ * @return the U matrix + */ + public RealMatrix getU() { + + if (cachedU == null) { + + final int m = householderVectors.length; + final int n = householderVectors[0].length; + final int p = main.length; + final int diagOffset = (m >= n) ? 0 : 1; + final double[] diagonal = (m >= n) ? main : secondary; + final double[][] uData = new double[m][m]; + + // fill up the part of the matrix not affected by Householder transforms + for (int k = m - 1; k >= p; --k) { + uData[k][k] = 1; + } + + // build up first part of the matrix by applying Householder transforms + for (int k = p - 1; k >= diagOffset; --k) { + final double[] hK = householderVectors[k]; + uData[k][k] = 1; + if (hK[k - diagOffset] != 0.0) { + for (int j = k; j < m; ++j) { + double alpha = 0; + for (int i = k; i < m; ++i) { + alpha -= uData[i][j] * householderVectors[i][k - diagOffset]; + } + alpha /= diagonal[k - diagOffset] * hK[k - diagOffset]; + + for (int i = k; i < m; ++i) { + uData[i][j] -= alpha * householderVectors[i][k - diagOffset]; + } + } + } + } + if (diagOffset > 0) { + uData[0][0] = 1; + } + + // cache the matrix for subsequent calls + cachedU = new RealMatrixImpl(uData, false); + + } + + // return the cached matrix + return cachedU; + + } + + /** + * Returns the bi-diagonal matrix B of the transform. + * @return the B matrix + */ + public RealMatrix getB() { + + if (cachedB == null) { + + final int m = householderVectors.length; + final int n = householderVectors[0].length; + double[][] bData = new double[m][n]; + for (int i = 0; i < main.length; ++i) { + double[] bDataI = bData[i]; + bDataI[i] = main[i]; + if (m < n) { + if (i > 0) { + bDataI[i - 1] = secondary[i - 1]; + } + } else { + if (i < main.length - 1) { + bDataI[i + 1] = secondary[i]; + } + } + } + + // cache the matrix for subsequent calls + cachedB = new RealMatrixImpl(bData, false); + + } + + // return the cached matrix + return cachedB; + + } + + /** + * Returns the matrix V of the transform. + *

V is an orthogonal matrix, i.e. its transpose is also its inverse.

+ * @return the V matrix + */ + public RealMatrix getV() { + + if (cachedV == null) { + + final int m = householderVectors.length; + final int n = householderVectors[0].length; + final int p = main.length; + final int diagOffset = (m >= n) ? 1 : 0; + final double[] diagonal = (m >= n) ? secondary : main; + final double[][] vData = new double[n][n]; + + // fill up the part of the matrix not affected by Householder transforms + for (int k = n - 1; k >= p; --k) { + vData[k][k] = 1; + } + + // build up first part of the matrix by applying Householder transforms + for (int k = p - 1; k >= diagOffset; --k) { + final double[] hK = householderVectors[k - diagOffset]; + vData[k][k] = 1; + if (hK[k] != 0.0) { + for (int j = k; j < n; ++j) { + double beta = 0; + for (int i = k; i < n; ++i) { + beta -= vData[i][j] * hK[i]; + } + beta /= diagonal[k - diagOffset] * hK[k]; + + for (int i = k; i < n; ++i) { + vData[i][j] -= beta * hK[i]; + } + } + } + } + if (diagOffset > 0) { + vData[0][0] = 1; + } + + // cache the matrix for subsequent calls + cachedV = new RealMatrixImpl(vData, false); + + } + + // return the cached matrix + return cachedV; + + } + + /** + * Get the Householder vectors of the transform. + *

Note that since this class is only intended for internal use, + * it returns directly a reference to its internal arrays, not a copy.

+ * @return the main diagonal elements of the B matrix + */ + double[][] getHouseholderVectorsRef() { + return householderVectors; + } + + /** + * Get the main diagonal elements of the matrix B of the transform. + *

Note that since this class is only intended for internal use, + * it returns directly a reference to its internal arrays, not a copy.

+ * @return the main diagonal elements of the B matrix + */ + double[] getMainDiagonalRef() { + return main; + } + + /** + * Get the secondary diagonal elements of the matrix B of the transform. + *

Note that since this class is only intended for internal use, + * it returns directly a reference to its internal arrays, not a copy.

+ * @return the secondary diagonal elements of the B matrix + */ + double[] getSecondaryDiagonalRef() { + return secondary; + } + + /** + * Check if the matrix is transformed to upper bi-diagonal. + * @return true if the matrix is transformed to upper bi-diagonal + */ + boolean isUpperBiDiagonal() { + return householderVectors.length >= householderVectors[0].length; + } + + /** + * Transform original matrix to upper bi-diagonal form. + *

Transformation is done using alternate Householder transforms + * on columns and rows.

+ */ + private void transformToUpperBiDiagonal() { + + final int m = householderVectors.length; + final int n = householderVectors[0].length; + for (int k = 0; k < n; k++) { + + //zero-out a column + double xNormSqr = 0; + for (int i = k; i < m; ++i) { + final double c = householderVectors[i][k]; + xNormSqr += c * c; + } + final double[] hK = householderVectors[k]; + final double a = (hK[k] > 0) ? -Math.sqrt(xNormSqr) : Math.sqrt(xNormSqr); + main[k] = a; + if (a != 0.0) { + hK[k] -= a; + for (int j = k + 1; j < n; ++j) { + double alpha = 0; + for (int i = k; i < m; ++i) { + final double[] hI = householderVectors[i]; + alpha -= hI[j] * hI[k]; + } + alpha /= a * householderVectors[k][k]; + for (int i = k; i < m; ++i) { + final double[] hI = householderVectors[i]; + hI[j] -= alpha * hI[k]; + } + } + } + + if (k < n - 1) { + //zero-out a row + xNormSqr = 0; + for (int j = k + 1; j < n; ++j) { + final double c = hK[j]; + xNormSqr += c * c; + } + final double b = (hK[k + 1] > 0) ? -Math.sqrt(xNormSqr) : Math.sqrt(xNormSqr); + secondary[k] = b; + if (b != 0.0) { + hK[k + 1] -= b; + for (int i = k + 1; i < m; ++i) { + final double[] hI = householderVectors[i]; + double beta = 0; + for (int j = k + 1; j < n; ++j) { + beta -= hI[j] * hK[j]; + } + beta /= b * hK[k + 1]; + for (int j = k + 1; j < n; ++j) { + hI[j] -= beta * hK[j]; + } + } + } + } + + } + } + + /** + * Transform original matrix to lower bi-diagonal form. + *

Transformation is done using alternate Householder transforms + * on rows and columns.

+ */ + private void transformToLowerBiDiagonal() { + + final int m = householderVectors.length; + final int n = householderVectors[0].length; + for (int k = 0; k < m; k++) { + + //zero-out a row + final double[] hK = householderVectors[k]; + double xNormSqr = 0; + for (int j = k; j < n; ++j) { + final double c = hK[j]; + xNormSqr += c * c; + } + final double a = (hK[k] > 0) ? -Math.sqrt(xNormSqr) : Math.sqrt(xNormSqr); + main[k] = a; + if (a != 0.0) { + hK[k] -= a; + for (int i = k + 1; i < m; ++i) { + final double[] hI = householderVectors[i]; + double alpha = 0; + for (int j = k; j < n; ++j) { + alpha -= hI[j] * hK[j]; + } + alpha /= a * householderVectors[k][k]; + for (int j = k; j < n; ++j) { + hI[j] -= alpha * hK[j]; + } + } + } + + if (k < m - 1) { + //zero-out a column + final double[] hKp1 = householderVectors[k + 1]; + xNormSqr = 0; + for (int i = k + 1; i < m; ++i) { + final double c = householderVectors[i][k]; + xNormSqr += c * c; + } + final double b = (hKp1[k] > 0) ? -Math.sqrt(xNormSqr) : Math.sqrt(xNormSqr); + secondary[k] = b; + if (b != 0.0) { + hKp1[k] -= b; + for (int j = k + 1; j < n; ++j) { + double beta = 0; + for (int i = k + 1; i < m; ++i) { + final double[] hI = householderVectors[i]; + beta -= hI[j] * hI[k]; + } + beta /= b * hKp1[k]; + for (int i = k + 1; i < m; ++i) { + final double[] hI = householderVectors[i]; + hI[j] -= beta * hI[k]; + } + } + } + } + + } + } + +} diff --git a/src/java/org/apache/commons/math/linear/BigMatrixImpl.java b/src/java/org/apache/commons/math/linear/BigMatrixImpl.java index ccb9a9d14..f8f5fdfea 100644 --- a/src/java/org/apache/commons/math/linear/BigMatrixImpl.java +++ b/src/java/org/apache/commons/math/linear/BigMatrixImpl.java @@ -19,6 +19,8 @@ package org.apache.commons.math.linear; import java.io.Serializable; import java.math.BigDecimal; +import org.apache.commons.math.MathRuntimeException; + /** * Implementation of {@link BigMatrix} using a BigDecimal[][] array to store entries * and @@ -53,18 +55,18 @@ public class BigMatrixImpl implements BigMatrix, Serializable { private static final long serialVersionUID = -1011428905656140431L; /** Entries of the matrix */ - private BigDecimal data[][] = null; + protected BigDecimal data[][] = null; /** Entries of cached LU decomposition. * All updates to data (other than luDecompose()) *must* set this to null */ - private BigDecimal lu[][] = null; + protected BigDecimal lu[][] = null; /** Permutation associated with LU decomposition */ - private int[] permutation = null; + protected int[] permutation = null; /** Parity of the permutation associated with the LU decomposition */ - private int parity = 1; + protected int parity = 1; /** Rounding mode for divisions **/ private int roundingMode = BigDecimal.ROUND_HALF_UP; @@ -73,7 +75,7 @@ public class BigMatrixImpl implements BigMatrix, Serializable { private int scale = 64; /** Bound to determine effective singularity in LU decomposition */ - protected static BigDecimal TOO_SMALL = new BigDecimal(10E-12); + private static final BigDecimal TOO_SMALL = new BigDecimal(10E-12); /** BigDecimal 0 */ static final BigDecimal ZERO = new BigDecimal(0); @@ -106,8 +108,9 @@ public class BigMatrixImpl implements BigMatrix, Serializable { /** * Create a new BigMatrix using d as the underlying * data array. - *

- * The input array is copied, not referenced.

+ *

The input array is copied, not referenced. This constructor has + * the same effect as calling {@link #BigMatrixImpl(BigDecimal[][], boolean)} + * with the second argument set to true.

* * @param d data for new matrix * @throws IllegalArgumentException if d is not rectangular @@ -118,12 +121,52 @@ public class BigMatrixImpl implements BigMatrix, Serializable { this.copyIn(d); lu = null; } - + + /** + * Create a new BigMatrix using the input array as the underlying + * data array. + *

If an array is built specially in order to be embedded in a + * BigMatrix and not used directly, the copyArray may be + * set to false + * @param d data for new matrix + * @param copyArray if true, the input array will be copied, otherwise + * it will be referenced + * @throws IllegalArgumentException if d is not rectangular + * (not all rows have the same length) or empty + * @throws NullPointerException if d is null + * @see #BigMatrixImpl(BigDecimal[][]) + */ + public BigMatrixImpl(BigDecimal[][] d, boolean copyArray) { + if (copyArray) { + copyIn(d); + } else { + if (d == null) { + throw new NullPointerException(); + } + final int nRows = d.length; + if (nRows == 0) { + throw new IllegalArgumentException("Matrix must have at least one row."); + } + final int nCols = d[0].length; + if (nCols == 0) { + throw new IllegalArgumentException("Matrix must have at least one column."); + } + for (int r = 1; r < nRows; r++) { + if (d[r].length != nCols) { + throw new IllegalArgumentException("All input rows must have the same length."); + } + } + data = d; + } + lu = null; + } + /** * Create a new BigMatrix using d as the underlying * data array. - *

- * The input array is copied, not referenced.

+ *

Since the underlying array will hold BigDecimal + * instances, it will be created.

* * @param d data for new matrix * @throws IllegalArgumentException if d is not rectangular @@ -131,12 +174,12 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @throws NullPointerException if d is null */ public BigMatrixImpl(double[][] d) { - int nRows = d.length; + final int nRows = d.length; if (nRows == 0) { throw new IllegalArgumentException( "Matrix must have at least one row."); } - int nCols = d[0].length; + final int nCols = d[0].length; if (nCols == 0) { throw new IllegalArgumentException( "Matrix must have at least one column."); @@ -161,12 +204,12 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @throws NullPointerException if d is null */ public BigMatrixImpl(String[][] d) { - int nRows = d.length; + final int nRows = d.length; if (nRows == 0) { throw new IllegalArgumentException( "Matrix must have at least one row."); } - int nCols = d[0].length; + final int nCols = d[0].length; if (nCols == 0) { throw new IllegalArgumentException( "Matrix must have at least one column."); @@ -191,7 +234,7 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @param v column vector holding data for new matrix */ public BigMatrixImpl(BigDecimal[] v) { - int nRows = v.length; + final int nRows = v.length; data = new BigDecimal[nRows][1]; for (int row = 0; row < nRows; row++) { data[row][0] = v[row]; @@ -204,7 +247,7 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @return the cloned matrix */ public BigMatrix copy() { - return new BigMatrixImpl(this.copyOut()); + return new BigMatrixImpl(this.copyOut(), false); } /** @@ -212,47 +255,107 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * * @param m matrix to be added * @return this + m - * @exception IllegalArgumentException if m is not the same size as this + * @throws IllegalArgumentException if m is not the same size as this */ public BigMatrix add(BigMatrix m) throws IllegalArgumentException { - if (this.getColumnDimension() != m.getColumnDimension() || - this.getRowDimension() != m.getRowDimension()) { + try { + return add((BigMatrixImpl) m); + } catch (ClassCastException cce) { + final int rowCount = getRowDimension(); + final int columnCount = getColumnDimension(); + if (columnCount != m.getColumnDimension() || rowCount != m.getRowDimension()) { + throw new IllegalArgumentException("matrix dimension mismatch"); + } + final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; + for (int row = 0; row < rowCount; row++) { + final BigDecimal[] dataRow = data[row]; + final BigDecimal[] outDataRow = outData[row]; + for (int col = 0; col < columnCount; col++) { + outDataRow[col] = dataRow[col].add(m.getEntry(row, col)); + } + } + return new BigMatrixImpl(outData, false); + } + } + + /** + * Compute the sum of this and m. + * + * @param m matrix to be added + * @return this + m + * @throws IllegalArgumentException if m is not the same size as this + */ + public BigMatrixImpl add(BigMatrixImpl m) throws IllegalArgumentException { + final int rowCount = getRowDimension(); + final int columnCount = getColumnDimension(); + if (columnCount != m.getColumnDimension() || rowCount != m.getRowDimension()) { throw new IllegalArgumentException("matrix dimension mismatch"); } - int rowCount = this.getRowDimension(); - int columnCount = this.getColumnDimension(); - BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; + final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { + final BigDecimal[] dataRow = data[row]; + final BigDecimal[] mRow = m.data[row]; + final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { - outData[row][col] = data[row][col].add(m.getEntry(row, col)); - } + outDataRow[col] = dataRow[col].add(mRow[col]); + } } - return new BigMatrixImpl(outData); + return new BigMatrixImpl(outData, false); } - + /** * Compute this minus m. * * @param m matrix to be subtracted * @return this + m - * @exception IllegalArgumentException if m is not the same size as *this + * @throws IllegalArgumentException if m is not the same size as this */ public BigMatrix subtract(BigMatrix m) throws IllegalArgumentException { - if (this.getColumnDimension() != m.getColumnDimension() || - this.getRowDimension() != m.getRowDimension()) { + try { + return subtract((BigMatrixImpl) m); + } catch (ClassCastException cce) { + final int rowCount = getRowDimension(); + final int columnCount = getColumnDimension(); + if (columnCount != m.getColumnDimension() || rowCount != m.getRowDimension()) { + throw new IllegalArgumentException("matrix dimension mismatch"); + } + final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; + for (int row = 0; row < rowCount; row++) { + final BigDecimal[] dataRow = data[row]; + final BigDecimal[] outDataRow = outData[row]; + for (int col = 0; col < columnCount; col++) { + outDataRow[col] = dataRow[col].subtract(getEntry(row, col)); + } + } + return new BigMatrixImpl(outData, false); + } + } + + /** + * Compute this minus m. + * + * @param m matrix to be subtracted + * @return this + m + * @throws IllegalArgumentException if m is not the same size as this + */ + public BigMatrixImpl subtract(BigMatrixImpl m) throws IllegalArgumentException { + final int rowCount = getRowDimension(); + final int columnCount = getColumnDimension(); + if (columnCount != m.getColumnDimension() || rowCount != m.getRowDimension()) { throw new IllegalArgumentException("matrix dimension mismatch"); } - int rowCount = this.getRowDimension(); - int columnCount = this.getColumnDimension(); - BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; + final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { + final BigDecimal[] dataRow = data[row]; + final BigDecimal[] mRow = m.data[row]; + final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { - outData[row][col] = data[row][col].subtract(m.getEntry(row, col)); - } + outDataRow[col] = dataRow[col].subtract(mRow[col]); + } } - return new BigMatrixImpl(outData); + return new BigMatrixImpl(outData, false); } - + /** * Returns the result of adding d to each entry of this. * @@ -260,34 +363,38 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @return d + this */ public BigMatrix scalarAdd(BigDecimal d) { - int rowCount = this.getRowDimension(); - int columnCount = this.getColumnDimension(); - BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; + final int rowCount = getRowDimension(); + final int columnCount = getColumnDimension(); + final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { + final BigDecimal[] dataRow = data[row]; + final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { - outData[row][col] = data[row][col].add(d); + outDataRow[col] = dataRow[col].add(d); } } - return new BigMatrixImpl(outData); + return new BigMatrixImpl(outData, false); } - + /** - * Returns the result multiplying each entry of this by d + * Returns the result of multiplying each entry of this by d * @param d value to multiply all entries by * @return d * this */ public BigMatrix scalarMultiply(BigDecimal d) { - int rowCount = this.getRowDimension(); - int columnCount = this.getColumnDimension(); - BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; + final int rowCount = getRowDimension(); + final int columnCount = getColumnDimension(); + final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { + final BigDecimal[] dataRow = data[row]; + final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { - outData[row][col] = data[row][col].multiply(d); + outDataRow[col] = dataRow[col].multiply(d); } } - return new BigMatrixImpl(outData); + return new BigMatrixImpl(outData, false); } - + /** * Returns the result of postmultiplying this by m. * @param m matrix to postmultiply by @@ -296,26 +403,60 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * if columnDimension(this) != rowDimension(m) */ public BigMatrix multiply(BigMatrix m) throws IllegalArgumentException { + try { + return multiply((BigMatrixImpl) m); + } catch (ClassCastException cce) { + if (this.getColumnDimension() != m.getRowDimension()) { + throw new IllegalArgumentException("Matrices are not multiplication compatible."); + } + final int nRows = this.getRowDimension(); + final int nCols = m.getColumnDimension(); + final int nSum = this.getColumnDimension(); + final BigDecimal[][] outData = new BigDecimal[nRows][nCols]; + for (int row = 0; row < nRows; row++) { + final BigDecimal[] dataRow = data[row]; + final BigDecimal[] outDataRow = outData[row]; + for (int col = 0; col < nCols; col++) { + BigDecimal sum = ZERO; + for (int i = 0; i < nSum; i++) { + sum = sum.add(dataRow[i].multiply(m.getEntry(i, col))); + } + outDataRow[col] = sum; + } + } + return new BigMatrixImpl(outData, false); + } + } + + /** + * Returns the result of postmultiplying this by m. + * @param m matrix to postmultiply by + * @return this*m + * @throws IllegalArgumentException + * if columnDimension(this) != rowDimension(m) + */ + public BigMatrixImpl multiply(BigMatrixImpl m) throws IllegalArgumentException { if (this.getColumnDimension() != m.getRowDimension()) { throw new IllegalArgumentException("Matrices are not multiplication compatible."); } - int nRows = this.getRowDimension(); - int nCols = m.getColumnDimension(); - int nSum = this.getColumnDimension(); - BigDecimal[][] outData = new BigDecimal[nRows][nCols]; - BigDecimal sum = ZERO; + final int nRows = this.getRowDimension(); + final int nCols = m.getColumnDimension(); + final int nSum = this.getColumnDimension(); + final BigDecimal[][] outData = new BigDecimal[nRows][nCols]; for (int row = 0; row < nRows; row++) { + final BigDecimal[] dataRow = data[row]; + final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < nCols; col++) { - sum = ZERO; + BigDecimal sum = ZERO; for (int i = 0; i < nSum; i++) { - sum = sum.add(data[row][i].multiply(m.getEntry(i, col))); + sum = sum.add(dataRow[i].multiply(m.data[i][col])); } - outData[row][col] = sum; + outDataRow[col] = sum; } - } - return new BigMatrixImpl(outData); + } + return new BigMatrixImpl(outData, false); } - + /** * Returns the result premultiplying this by m. * @param m matrix to premultiply by @@ -326,7 +467,7 @@ public class BigMatrixImpl implements BigMatrix, Serializable { public BigMatrix preMultiply(BigMatrix m) throws IllegalArgumentException { return m.multiply(this); } - + /** * Returns matrix entries as a two-dimensional array. *

@@ -347,11 +488,11 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @return 2-dimensional array of entries */ public double[][] getDataAsDoubleArray() { - int nRows = getRowDimension(); - int nCols = getColumnDimension(); - double d[][] = new double[nRows][nCols]; + final int nRows = getRowDimension(); + final int nCols = getColumnDimension(); + final double d[][] = new double[nRows][nCols]; for (int i = 0; i < nRows; i++) { - for (int j=0; j endRow || endRow > data.length || - startColumn < 0 || startColumn > endColumn || - endColumn > data[0].length ) { - throw new MatrixIndexException( - "invalid row or column index selection"); + public BigMatrix getSubMatrix(int startRow, int endRow, + int startColumn, int endColumn) + throws MatrixIndexException { + + checkRowIndex(startRow); + checkRowIndex(endRow); + if (startRow > endRow) { + throw new MatrixIndexException("initial row {0} after final row {1}", + new Object[] { startRow, endRow }); } - BigMatrixImpl subMatrix = new BigMatrixImpl(endRow - startRow+1, - endColumn - startColumn+1); - BigDecimal[][] subMatrixData = subMatrix.getDataRef(); + + checkColumnIndex(startColumn); + checkColumnIndex(endColumn); + if (startColumn > endColumn) { + throw new MatrixIndexException("initial column {0} after final column {1}", + new Object[] { startColumn, endColumn }); + } + + final BigDecimal[][] subMatrixData = + new BigDecimal[endRow - startRow + 1][endColumn - startColumn + 1]; for (int i = startRow; i <= endRow; i++) { - for (int j = startColumn; j <= endColumn; j++) { - subMatrixData[i - startRow][j - startColumn] = data[i][j]; - } + System.arraycopy(data[i], startColumn, + subMatrixData[i - startRow], 0, + endColumn - startColumn + 1); } - return subMatrix; + + return new BigMatrixImpl(subMatrixData, false); + } /** @@ -468,25 +620,36 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * are not valid */ public BigMatrix getSubMatrix(int[] selectedRows, int[] selectedColumns) - throws MatrixIndexException { + throws MatrixIndexException { + if (selectedRows.length * selectedColumns.length == 0) { - throw new MatrixIndexException( - "selected row and column index arrays must be non-empty"); + if (selectedRows.length == 0) { + throw new MatrixIndexException("empty selected row index array", null); + } + throw new MatrixIndexException("empty selected column index array", null); } - BigMatrixImpl subMatrix = new BigMatrixImpl(selectedRows.length, - selectedColumns.length); - BigDecimal[][] subMatrixData = subMatrix.getDataRef(); + + final BigDecimal[][] subMatrixData = + new BigDecimal[selectedRows.length][selectedColumns.length]; try { for (int i = 0; i < selectedRows.length; i++) { + final BigDecimal[] subI = subMatrixData[i]; + final BigDecimal[] dataSelectedI = data[selectedRows[i]]; for (int j = 0; j < selectedColumns.length; j++) { - subMatrixData[i][j] = data[selectedRows[i]][selectedColumns[j]]; + subI[j] = dataSelectedI[selectedColumns[j]]; } } + } catch (ArrayIndexOutOfBoundsException e) { + // we redo the loop with checks enabled + // in order to generate an appropriate message + for (final int row : selectedRows) { + checkRowIndex(row); + } + for (final int column : selectedColumns) { + checkColumnIndex(column); + } } - catch (ArrayIndexOutOfBoundsException e) { - throw new MatrixIndexException("matrix dimension mismatch"); - } - return subMatrix; + return new BigMatrixImpl(subMatrixData, false); } /** @@ -518,40 +681,46 @@ public class BigMatrixImpl implements BigMatrix, Serializable { */ public void setSubMatrix(BigDecimal[][] subMatrix, int row, int column) throws MatrixIndexException { - if ((row < 0) || (column < 0)){ - throw new MatrixIndexException - ("invalid row or column index selection"); - } - int nRows = subMatrix.length; + + final int nRows = subMatrix.length; + final int nCols = subMatrix[0].length; + if (nRows == 0) { - throw new IllegalArgumentException( - "Matrix must have at least one row."); + throw new IllegalArgumentException("Matrix must have at least one row."); } - int nCols = subMatrix[0].length; if (nCols == 0) { - throw new IllegalArgumentException( - "Matrix must have at least one column."); + throw new IllegalArgumentException("Matrix must have at least one column."); } + for (int r = 1; r < nRows; r++) { if (subMatrix[r].length != nCols) { - throw new IllegalArgumentException( - "All input rows must have the same length."); + throw new IllegalArgumentException("All input rows must have the same length."); } - } + } + if (data == null) { - if ((row > 0)||(column > 0)) throw new MatrixIndexException - ("matrix must be initialized to perfom this method"); + if (row > 0) { + throw MathRuntimeException.createIllegalStateException("first {0} rows are not initialized yet", + new Object[] { row }); + } + if (column > 0) { + throw MathRuntimeException.createIllegalStateException("first {0} columns are not initialized yet", + new Object[] { column }); + } data = new BigDecimal[nRows][nCols]; System.arraycopy(subMatrix, 0, data, 0, subMatrix.length); - } - if (((nRows + row) > this.getRowDimension()) || - (nCols + column > this.getColumnDimension())) - throw new MatrixIndexException( - "invalid row or column index selection"); + } else { + checkRowIndex(row); + checkColumnIndex(column); + checkRowIndex(nRows + row - 1); + checkColumnIndex(nCols + column - 1); + } for (int i = 0; i < nRows; i++) { System.arraycopy(subMatrix[i], 0, data[row + i], column, nCols); } + lu = null; + } /** @@ -563,13 +732,11 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @throws MatrixIndexException if the specified row index is invalid */ public BigMatrix getRowMatrix(int row) throws MatrixIndexException { - if ( !isValidCoordinate( row, 0)) { - throw new MatrixIndexException("illegal row argument"); - } - int ncols = this.getColumnDimension(); - BigDecimal[][] out = new BigDecimal[1][ncols]; + checkRowIndex(row); + final int ncols = this.getColumnDimension(); + final BigDecimal[][] out = new BigDecimal[1][ncols]; System.arraycopy(data[row], 0, out[0], 0, ncols); - return new BigMatrixImpl(out); + return new BigMatrixImpl(out, false); } /** @@ -581,15 +748,13 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @throws MatrixIndexException if the specified column index is invalid */ public BigMatrix getColumnMatrix(int column) throws MatrixIndexException { - if ( !isValidCoordinate( 0, column)) { - throw new MatrixIndexException("illegal column argument"); - } - int nRows = this.getRowDimension(); - BigDecimal[][] out = new BigDecimal[nRows][1]; + checkColumnIndex(column); + final int nRows = this.getRowDimension(); + final BigDecimal[][] out = new BigDecimal[nRows][1]; for (int row = 0; row < nRows; row++) { out[row][0] = data[row][column]; } - return new BigMatrixImpl(out); + return new BigMatrixImpl(out, false); } /** @@ -603,11 +768,9 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @throws MatrixIndexException if the specified row index is not valid */ public BigDecimal[] getRow(int row) throws MatrixIndexException { - if ( !isValidCoordinate( row, 0 ) ) { - throw new MatrixIndexException("illegal row argument"); - } - int ncols = this.getColumnDimension(); - BigDecimal[] out = new BigDecimal[ncols]; + checkRowIndex(row); + final int ncols = this.getColumnDimension(); + final BigDecimal[] out = new BigDecimal[ncols]; System.arraycopy(data[row], 0, out, 0, ncols); return out; } @@ -624,11 +787,9 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @throws MatrixIndexException if the specified row index is not valid */ public double[] getRowAsDoubleArray(int row) throws MatrixIndexException { - if ( !isValidCoordinate( row, 0 ) ) { - throw new MatrixIndexException("illegal row argument"); - } - int ncols = this.getColumnDimension(); - double[] out = new double[ncols]; + checkRowIndex(row); + final int ncols = this.getColumnDimension(); + final double[] out = new double[ncols]; for (int i=0;i= 0; col--) { + final BigDecimal[] bpCol = bp[col]; + final BigDecimal luDiag = lu[col][col]; for (int j = 0; j < nColB; j++) { - bp[col][j] = bp[col][j].divide(lu[col][col], scale, roundingMode); + bpCol[j] = bpCol[j].divide(luDiag, scale, roundingMode); } for (int i = 0; i < col; i++) { + final BigDecimal[] bpI = bp[i]; + final BigDecimal[] luI = lu[i]; for (int j = 0; j < nColB; j++) { - bp[i][j] = bp[i][j].subtract(bp[col][j].multiply(lu[i][col])); + bpI[j] = bpI[j].subtract(bp[col][j].multiply(luI[col])); } } } - - BigMatrixImpl outMat = new BigMatrixImpl(bp); - return outMat; + + return new BigMatrixImpl(bp, false); + } /** @@ -1021,10 +1189,10 @@ public class BigMatrixImpl implements BigMatrix, Serializable { */ public void luDecompose() throws InvalidMatrixException { - int nRows = this.getRowDimension(); - int nCols = this.getColumnDimension(); + final int nRows = this.getRowDimension(); + final int nCols = this.getColumnDimension(); if (nRows != nCols) { - throw new InvalidMatrixException("LU decomposition requires that the matrix be square."); + throw new NonSquareMatrixException(getRowDimension(), getColumnDimension()); } lu = this.getData(); @@ -1042,22 +1210,24 @@ public class BigMatrixImpl implements BigMatrix, Serializable { // upper for (int row = 0; row < col; row++) { - sum = lu[row][col]; + final BigDecimal[] luRow = lu[row]; + sum = luRow[col]; for (int i = 0; i < row; i++) { - sum = sum.subtract(lu[row][i].multiply(lu[i][col])); + sum = sum.subtract(luRow[i].multiply(lu[i][col])); } - lu[row][col] = sum; + luRow[col] = sum; } // lower int max = col; // permutation row BigDecimal largest = ZERO; for (int row = col; row < nRows; row++) { - sum = lu[row][col]; + final BigDecimal[] luRow = lu[row]; + sum = luRow[col]; for (int i = 0; i < col; i++) { - sum = sum.subtract(lu[row][i].multiply(lu[i][col])); + sum = sum.subtract(luRow[i].multiply(lu[i][col])); } - lu[row][col] = sum; + luRow[col] = sum; // maintain best permutation choice if (sum.abs().compareTo(largest) == 1) { @@ -1069,7 +1239,7 @@ public class BigMatrixImpl implements BigMatrix, Serializable { // Singularity check if (lu[max][col].abs().compareTo(TOO_SMALL) <= 0) { lu = null; - throw new InvalidMatrixException("matrix is singular"); + throw new SingularMatrixException(); } // Pivot if necessary @@ -1086,9 +1256,11 @@ public class BigMatrixImpl implements BigMatrix, Serializable { parity = -parity; } - //Divide the lower elements by the "winning" diagonal elt. + // Divide the lower elements by the "winning" diagonal elt. + final BigDecimal luDiag = lu[col][col]; for (int row = col + 1; row < nRows; row++) { - lu[row][col] = lu[row][col].divide(lu[col][col], scale, roundingMode); + final BigDecimal[] luRow = lu[row]; + luRow[col] = luRow[col].divide(luDiag, scale, roundingMode); } } @@ -1104,12 +1276,14 @@ public class BigMatrixImpl implements BigMatrix, Serializable { res.append("BigMatrixImpl{"); if (data != null) { for (int i = 0; i < data.length; i++) { - if (i > 0) + if (i > 0) { res.append(","); + } res.append("{"); for (int j = 0; j < data[0].length; j++) { - if (j > 0) + if (j > 0) { res.append(","); + } res.append(data[i][j]); } res.append("}"); @@ -1135,15 +1309,16 @@ public class BigMatrixImpl implements BigMatrix, Serializable { if (object instanceof BigMatrixImpl == false) { return false; } - BigMatrix m = (BigMatrix) object; - int nRows = getRowDimension(); - int nCols = getColumnDimension(); + final BigMatrix m = (BigMatrix) object; + final int nRows = getRowDimension(); + final int nCols = getColumnDimension(); if (m.getColumnDimension() != nCols || m.getRowDimension() != nRows) { return false; } for (int row = 0; row < nRows; row++) { + final BigDecimal[] dataRow = data[row]; for (int col = 0; col < nCols; col++) { - if (!data[row][col].equals(m.getEntry(row, col))) { + if (!dataRow[col].equals(m.getEntry(row, col))) { return false; } } @@ -1158,14 +1333,15 @@ public class BigMatrixImpl implements BigMatrix, Serializable { */ public int hashCode() { int ret = 7; - int nRows = getRowDimension(); - int nCols = getColumnDimension(); + final int nRows = getRowDimension(); + final int nCols = getColumnDimension(); ret = ret * 31 + nRows; ret = ret * 31 + nCols; for (int row = 0; row < nRows; row++) { + final BigDecimal[] dataRow = data[row]; for (int col = 0; col < nCols; col++) { ret = ret * 31 + (11 * (row+1) + 17 * (col+1)) * - data[row][col].hashCode(); + dataRow[col].hashCode(); } } return ret; @@ -1173,18 +1349,6 @@ public class BigMatrixImpl implements BigMatrix, Serializable { //------------------------ Protected methods - /** - * Returns dimension x dimension identity matrix. - * - * @param dimension dimension of identity matrix to generate - * @return identity matrix - * @throws IllegalArgumentException if dimension is not positive - * @deprecated use {@link MatrixUtils#createBigIdentityMatrix} - */ - protected BigMatrix getIdentity(int dimension) { - return MatrixUtils.createBigIdentityMatrix(dimension); - } - /** * Returns the LU decomposition as a BigMatrix. * Returns a fresh copy of the cached LU matrix if this has been computed; @@ -1232,7 +1396,7 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @return the permutation */ protected int[] getPermutation() { - int[] out = new int[permutation.length]; + final int[] out = new int[permutation.length]; System.arraycopy(permutation, 0, out, 0, permutation.length); return out; } @@ -1245,8 +1409,8 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @return a copy of the underlying data array. */ private BigDecimal[][] copyOut() { - int nRows = this.getRowDimension(); - BigDecimal[][] out = new BigDecimal[nRows][this.getColumnDimension()]; + final int nRows = this.getRowDimension(); + final BigDecimal[][] out = new BigDecimal[nRows][this.getColumnDimension()]; // can't copy 2-d array in one shot, otherwise get row references for (int i = 0; i < nRows; i++) { System.arraycopy(data[i], 0, out[i], 0, data[i].length); @@ -1274,12 +1438,14 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @param in data to copy in */ private void copyIn(double[][] in) { - int nRows = in.length; - int nCols = in[0].length; + final int nRows = in.length; + final int nCols = in[0].length; data = new BigDecimal[nRows][nCols]; for (int i = 0; i < nRows; i++) { - for (int j=0; j < nCols; j++) { - data[i][j] = new BigDecimal(in[i][j]); + final BigDecimal[] dataI = data[i]; + final double[] inI = in[i]; + for (int j = 0; j < nCols; j++) { + dataI[j] = new BigDecimal(inI[j]); } } lu = null; @@ -1292,29 +1458,42 @@ public class BigMatrixImpl implements BigMatrix, Serializable { * @param in data to copy in */ private void copyIn(String[][] in) { - int nRows = in.length; - int nCols = in[0].length; + final int nRows = in.length; + final int nCols = in[0].length; data = new BigDecimal[nRows][nCols]; for (int i = 0; i < nRows; i++) { - for (int j=0; j < nCols; j++) { - data[i][j] = new BigDecimal(in[i][j]); + final BigDecimal[] dataI = data[i]; + final String[] inI = in[i]; + for (int j = 0; j < nCols; j++) { + dataI[j] = new BigDecimal(inI[j]); } } lu = null; } - + /** - * Tests a given coordinate as being valid or invalid - * - * @param row the row index. - * @param col the column index. - * @return true if the coordinate is with the current dimensions + * Check if a row index is valid. + * @param row row index to check + * @exception MatrixIndexException if index is not valid */ - private boolean isValidCoordinate(int row, int col) { - int nRows = this.getRowDimension(); - int nCols = this.getColumnDimension(); - - return !(row < 0 || row >= nRows || col < 0 || col >= nCols); + private void checkRowIndex(final int row) { + if (row < 0 || row >= getRowDimension()) { + throw new MatrixIndexException("row index {0} out of allowed range [{1}, {2}]", + new Object[] { row, 0, getRowDimension() - 1}); + } } - + + /** + * Check if a column index is valid. + * @param column column index to check + * @exception MatrixIndexException if index is not valid + */ + private void checkColumnIndex(final int column) + throws MatrixIndexException { + if (column < 0 || column >= getColumnDimension()) { + throw new MatrixIndexException("column index {0} out of allowed range [{1}, {2}]", + new Object[] { column, 0, getColumnDimension() - 1}); + } + } + } diff --git a/src/java/org/apache/commons/math/linear/DecompositionSolver.java b/src/java/org/apache/commons/math/linear/DecompositionSolver.java new file mode 100644 index 000000000..94765d019 --- /dev/null +++ b/src/java/org/apache/commons/math/linear/DecompositionSolver.java @@ -0,0 +1,107 @@ +/* + * 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.linear; + +import java.io.Serializable; + +/** + * A base interface to decomposition algorithms that can solve A × X = B. + *

This interface is the common base of decomposition algorithms like + * {@link QRDecomposition}, {@link LUDecomposition}, {@link + * SingularValueDecomposition} or {@link EigenDecomposition}. All these + * algorithms decompose an A matrix has a product of several specific matrices + * from which they can solve A × X = B in least squares sense: they find X + * such that ||A × X - B|| is minimal.

+ *

Some solvers like {@link LUDecomposition} can only find the solution for + * square matrices and when the solution is an exact linear solution, i.e. when + * ||A × X - B|| is exactly 0. Other solvers can also find solutions + * with non-square matrix A and with non-null minimal norm. If an exact linear + * solution exists it is also the minimal norm solution.

+ * + * @version $Revision$ $Date$ + * @since 2.0 + */ +public interface DecompositionSolver extends Serializable { + + /** + * Decompose a matrix. + * @param matrix matrix to decompose + * @exception InvalidMatrixException if matrix does not fulfill + * the decomposition requirements (for example non-square matrix + * for {@link LUDecomposition}) + */ + void decompose(RealMatrix matrix) + throws InvalidMatrixException; + + /** Solve the linear equation A × X = B. + *

The A matrix is implicit here. It must have + * already been provided by a previous call to {@link #decompose(RealMatrix)}.

+ * @param b right-hand side of the equation A × X = B + * @return a vector X that minimizes the two norm of A × X - B + * @exception IllegalStateException if {@link #decompose(RealMatrix) decompose} + * has not been called + * @exception IllegalArgumentException if matrices dimensions don't match + * @exception InvalidMatrixException if decomposed matrix is singular + */ + double[] solve(double[] b) + throws IllegalStateException, IllegalArgumentException, InvalidMatrixException; + + /** Solve the linear equation A × X = B. + *

The A matrix is implicit here. It must have + * already been provided by a previous call to {@link #decompose(RealMatrix)}.

+ * @param b right-hand side of the equation A × X = B + * @return a vector X that minimizes the two norm of A × X - B + * @exception IllegalStateException if {@link #decompose(RealMatrix) decompose} + * has not been called + * @exception IllegalArgumentException if matrices dimensions don't match + * @exception InvalidMatrixException if decomposed matrix is singular + */ + RealVector solve(RealVector b) + throws IllegalStateException, IllegalArgumentException, InvalidMatrixException; + + /** Solve the linear equation A × X = B. + *

The A matrix is implicit here. It must have + * already been provided by a previous call to {@link #decompose(RealMatrix)}.

+ * @param b right-hand side of the equation A × X = B + * @return a matrix X that minimizes the two norm of A × X - B + * @exception IllegalStateException if {@link #decompose(RealMatrix) decompose} + * has not been called + * @exception IllegalArgumentException if matrices dimensions don't match + * @exception InvalidMatrixException if decomposed matrix is singular + */ + RealMatrix solve(RealMatrix b) + throws IllegalStateException, IllegalArgumentException, InvalidMatrixException; + + /** + * Check if the decomposed matrix is non-singular. + * @return true if the decomposed matrix is non-singular + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + */ + boolean isNonSingular() throws IllegalStateException; + + /** Get the inverse of the decomposed matrix. + * @return inverse matrix + * @exception IllegalStateException if {@link #decompose(RealMatrix) decompose} + * has not been called + * @throws InvalidMatrixException if decomposed matrix is singular + */ + RealMatrix getInverse() + throws IllegalStateException, InvalidMatrixException; + +} diff --git a/src/java/org/apache/commons/math/linear/EigenDecomposition.java b/src/java/org/apache/commons/math/linear/EigenDecomposition.java new file mode 100644 index 000000000..f17694629 --- /dev/null +++ b/src/java/org/apache/commons/math/linear/EigenDecomposition.java @@ -0,0 +1,123 @@ +/* + * 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.linear; + +/** + * An interface to classes that implement an algorithm to calculate the + * eigen decomposition of a real symmetric matrix. + *

The eigen decomposition of matrix A is a set of two matrices: + * V and D such that A = V × D × VT. + * A, V and D are all m × m matrices.

+ *

This interface is similar in spirit to the EigenvalueDecomposition + * class from the now defunct JAMA + * library, with the following changes:

+ *
    + *
  • solve methods have been added (in the superinterface),
  • + *
  • a {@link DecompositionSolver#decompose(RealMatrix) decompose(RealMatrix)} + * method has been added (in the superinterface),
  • + *
  • a {@link DecompositionSolver#isNonSingular() isNonSingular} method has + * been added (in the superinterface),
  • + *
  • a {@link DecompositionSolver#getInverse() getInverse} method has been + * added (in the superinterface),
  • + *
  • a {@link #getVT() getVt} method has been added,
  • + *
  • a {@link #getEigenvalue(int) getEigenvalue} method to pick up a single + * eigenvalue has been added,
  • + *
  • a {@link #getEigenvector(int) getEigenvector} method to pick up a single + * eigenvector has been added,
  • + *
  • the getRealEigenvalues method has been renamed as {@link + * #getEigenValues() getEigenValues},
  • + *
  • the getImagEigenvalues method has been removed
  • + *
+ * @see MathWorld + * @see Wikipedia + * @version $Revision$ $Date$ + * @since 2.0 + */ +public interface EigenDecomposition extends DecompositionSolver { + + /** + * Returns the matrix V of the decomposition. + *

V is an orthogonal matrix, i.e. its transpose is also its inverse.

+ *

The columns of V are the eigenvectors of the original matrix.

+ * @return the V matrix + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + */ + RealMatrix getV() throws IllegalStateException; + + /** + * Returns the diagonal matrix D of the decomposition. + *

D is a diagonal matrix.

+ *

The values on the diagonal are the eigenvalues of the original matrix.

+ * @return the D matrix + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + * @see #getEigenValues() + */ + RealMatrix getD() throws IllegalStateException; + + /** + * Returns the transpose of the matrix V of the decomposition. + *

V is an orthogonal matrix, i.e. its transpose is also its inverse.

+ *

The columns of V are the eigenvectors of the original matrix.

+ * @return the transpose of the V matrix + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + */ + RealMatrix getVT() throws IllegalStateException; + + /** + * Returns a copy of the eigenvalues of the original matrix. + * @return a copy of the eigenvalues of the original matrix + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + * @see #getD() + */ + double[] getEigenvalues() throws IllegalStateException; + + /** + * Returns the ith eigenvalue of the original matrix. + * @param i index of the eigenvalue (counting from 0) + * @return ith eigenvalue of the original matrix + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + * @exception ArrayIndexOutOfBoundsException if i is not + * @see #getD() + */ + double getEigenvalue(int i) throws IllegalStateException; + + /** + * Returns a copy of the ith eigenvector of the original matrix. + * @param i index of the eigenvector (counting from 0) + * @return copy of the ith eigenvector of the original matrix + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + * @see #getD() + */ + RealVector getEigenvector(int i) throws IllegalStateException; + + /** + * Return the determinant of the matrix + * @return determinant of the matrix + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + * @see #isNonSingular() + */ + double getDeterminant() throws IllegalStateException; + +} diff --git a/src/java/org/apache/commons/math/linear/EigenDecompositionImpl.java b/src/java/org/apache/commons/math/linear/EigenDecompositionImpl.java new file mode 100644 index 000000000..8a7668036 --- /dev/null +++ b/src/java/org/apache/commons/math/linear/EigenDecompositionImpl.java @@ -0,0 +1,1804 @@ +/* + * 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.linear; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.MaxIterationsExceededException; +import org.apache.commons.math.util.MathUtils; + +/** + * Calculates the eigen decomposition of a symmetric matrix. + *

The eigen decomposition of symmetric matrix A is a set of two matrices: + * V and D such that A = V D VT. A, V and D are all m × m + * matrices.

+ *

This implementation only uses the upper part of the matrix, the part below the + * diagonal is not accessed at all.

+ *

Eigenvalues are computed as soon as the matrix is decomposed, but eigenvectors + * are computed only when required, i.e. only when one of the {@link #getEigenvector(int)}, + * {@link #getV()}, {@link #getVT()}, {@link #getInverse()}, {@link #solve(double[])}, + * {@link #solve(RealMatrix)}, {@link #solve(RealVector)} or {@link #solve(RealVectorImpl)} + * methods is called.

+ *

This implementation is based on Inderjit Singh Dhillon thesis + * A + * New O(n2) Algorithm for the Symmetric Tridiagonal Eigenvalue/Eigenvector + * Problem, on Beresford N. Parlett and Osni A. Marques paper An Implementation of the + * dqds Algorithm (Positive Case) and on the corresponding LAPACK routines (DLARRE, + * DLASQ2, DLAZQ3, DLAZQ4, DLASQ5 and DLASQ6).

+ * @author Beresford Parlett, University of California, Berkeley, USA (fortran version) + * @author Jim Demmel, University of California, Berkeley, USA (fortran version) + * @author Inderjit Dhillon, University of Texas, Austin, USA(fortran version) + * @author Osni Marques, LBNL/NERSC, USA (fortran version) + * @author Christof Voemel, University of California, Berkeley, USA(fortran version) + * @version $Revision$ $Date$ + * @since 2.0 + */ +public class EigenDecompositionImpl implements EigenDecomposition { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -4976315828448620858L; + + /** Tolerance. */ + private static final double TOLERANCE = 100 * MathUtils.EPSILON; + + /** Squared tolerance. */ + private static final double TOLERANCE_2 = TOLERANCE * TOLERANCE; + + /** Split tolerance. */ + private double splitTolerance; + + /** Main diagonal of the tridiagonal matrix. */ + private double[] main; + + /** Secondary diagonal of the tridiagonal matrix. */ + private double[] secondary; + + /** Squared secondary diagonal of the tridiagonal matrix. */ + private double[] squaredSecondary; + + /** Orthogonal matrix of tridiagonal transformation. */ + private RealMatrix orthoTridiag; + + /** Lower bound of spectra. */ + private double lowerSpectra; + + /** Upper bound of spectra. */ + private double upperSpectra; + + /** Minimum pivot in the Sturm sequence. */ + private double minPivot; + + /** Current shift. */ + private double sigma; + + /** Low part of the current shift. */ + private double sigmaLow; + + /** Shift increment to apply. */ + private double tau; + + /** Work array for all decomposition algorithms. */ + private double[] work; + + /** Shift within qd array for ping-pong implementation. */ + private int pingPong; + + /** Max value of diagonal elements in current segment. */ + private double qMax; + + /** Min value of off-diagonal elements in current segment. */ + private double eMin; + + /** Type of the last dqds shift. */ + private int tType; + + /** Minimal value on current state of the diagonal. */ + private double dMin; + + /** Minimal value on current state of the diagonal, excluding last element. */ + private double dMin1; + + /** Minimal value on current state of the diagonal, excluding last two elements. */ + private double dMin2; + + /** Last value on current state of the diagonal. */ + private double dN; + + /** Last but one value on current state of the diagonal. */ + private double dN1; + + /** Last but two on current state of the diagonal. */ + private double dN2; + + /** Shift ratio with respect to dMin used when tType == 6. */ + private double g; + + /** Eigenvalues. */ + private double[] eigenvalues; + + /** Eigenvectors. */ + private RealVectorImpl[] eigenvectors; + + /** Cached value of V. */ + private RealMatrix cachedV; + + /** Cached value of D. */ + private RealMatrix cachedD; + + /** Cached value of Vt. */ + private RealMatrix cachedVt; + + /** + * Build a new instance. + *

Note that {@link #decompose(RealMatrix)} must be called + * before any of the {@link #getV()}, {@link #getD()}, {@link #getVT()}, + * {@link #getEignevalues()}, {@link #solve(double[])}, {@link #solve(RealMatrix)}, + * {@link #solve(RealVector)} or {@link #solve(RealVectorImpl)} methods can be + * called.

+ * @see #decompose(RealMatrix) + */ + public EigenDecompositionImpl() { + setRelativeAccuracySplitTolerance(MathUtils.SAFE_MIN); + } + + /** + * Calculates the eigen decomposition of the given symmetric matrix. + *

Calling this constructor is equivalent to first call the no-arguments + * constructor and then call {@link #decompose(RealMatrix)}.

+ *

The specified matrix is assumed to be symmetrical without any check. + * Only the upper triangular part of the matrix is used.

+ * @param matrix The symmetric matrix to decompose. + * @exception InvalidMatrixException (wrapping a {@link ConvergenceException} + * if algorithm fails to converge + */ + public EigenDecompositionImpl(final RealMatrix matrix) + throws InvalidMatrixException { + setRelativeAccuracySplitTolerance(MathUtils.SAFE_MIN); + decompose(matrix); + } + + /** + * Set split tolerance based on absolute off-diagonal elements. + * @param tolerance tolerance to set + */ + public void setAbsoluteSplitTolerance(final double tolerance) { + splitTolerance = -Math.abs(tolerance); + } + + /** + * Set split tolerance preserving relative accuracy. + * @param tolerance tolerance to set + */ + public void setRelativeAccuracySplitTolerance(final double tolerance) { + splitTolerance = Math.abs(tolerance); + } + + /** + * Decompose a symmetric matrix. + *

The specified matrix is assumed to be symmetrical without any check. + * Only the upper triangular part of the matrix is used.

+ * @param matrix symmetric matrix to decompose + * @exception InvalidMatrixException if matrix cannot be diagonalized + */ + public void decompose(final RealMatrix matrix) + throws InvalidMatrixException { + + cachedV = null; + cachedD = null; + cachedVt = null; + work = new double[6 * matrix.getRowDimension()]; + + // compute tridiagonal representation of the initial matrix + transformToTridiagonal(matrix); + computeGershgorinCircles(); + + // find all the eigenvalues + findEigenvalues(); + + // we will search for eigenvectors only if required + eigenvectors = null; + + } + + /** {@inheritDoc} */ + public RealMatrix getV() + throws InvalidMatrixException { + + if (cachedV == null) { + cachedV = getVT().transpose(); + } + + // return the cached matrix + return cachedV; + + } + + /** {@inheritDoc} */ + public RealMatrix getD() + throws InvalidMatrixException { + + if (cachedD == null) { + + checkDecomposed(); + + final int m = eigenvalues.length; + final double[][] sData = new double[m][m]; + for (int i = 0; i < m; ++i) { + sData[i][i] = eigenvalues[i]; + } + + // cache the matrix for subsequent calls + cachedD = new RealMatrixImpl(sData, false); + + } + return cachedD; + } + + /** {@inheritDoc} */ + public RealMatrix getVT() + throws InvalidMatrixException { + + if (cachedVt == null) { + + checkDecomposed(); + if (eigenvectors == null) { + findEigenVectors(); + } + + final double[][] vtData = new double[eigenvectors.length][]; + for (int k = 0; k < eigenvectors.length; ++k) { + vtData[k] = eigenvectors[k].getData(); + } + + // cache the matrix for subsequent calls + cachedVt = new RealMatrixImpl(vtData, false); + + } + + // return the cached matrix + return cachedVt; + + } + + /** {@inheritDoc} */ + public double[] getEigenvalues() + throws InvalidMatrixException { + checkDecomposed(); + return eigenvalues.clone(); + } + + /** {@inheritDoc} */ + public double getEigenvalue(final int i) + throws InvalidMatrixException, ArrayIndexOutOfBoundsException { + checkDecomposed(); + return eigenvalues[i]; + } + + /** {@inheritDoc} */ + public RealVector getEigenvector(final int i) + throws InvalidMatrixException, ArrayIndexOutOfBoundsException { + checkDecomposed(); + if (eigenvectors == null) { + findEigenVectors(); + } + return eigenvectors[i].copy(); + } + + /** {@inheritDoc} */ + public boolean isNonSingular() + throws IllegalStateException { + for (double lambda : eigenvalues) { + if (lambda == 0) { + return false; + } + } + return true; + } + + /** {@inheritDoc} */ + public double[] solve(final double[] b) + throws IllegalArgumentException, InvalidMatrixException { + + checkNonSingular(); + + final int m = eigenvalues.length; + if (b.length != m) { + throw new IllegalArgumentException("constant vector has wrong length"); + } + + if (eigenvectors == null) { + findEigenVectors(); + } + + final double[] bp = new double[m]; + for (int i = 0; i < m; ++i) { + final RealVectorImpl v = eigenvectors[i]; + final double s = v.dotProduct(b) / eigenvalues[i]; + final double[] vData = v.getDataRef(); + for (int j = 0; j < m; ++j) { + bp[j] += s * vData[j]; + } + } + + return bp; + + } + + /** {@inheritDoc} */ + public RealVector solve(final RealVector b) + throws IllegalArgumentException, InvalidMatrixException { + try { + return solve((RealVectorImpl) b); + } catch (ClassCastException cce) { + + checkNonSingular(); + + final int m = eigenvalues.length; + if (b.getDimension() != m) { + throw new IllegalArgumentException("constant vector has wrong length"); + } + + if (eigenvectors == null) { + findEigenVectors(); + } + + final double[] bp = new double[m]; + for (int i = 0; i < m; ++i) { + final RealVectorImpl v = eigenvectors[i]; + final double s = v.dotProduct(b) / eigenvalues[i]; + final double[] vData = v.getDataRef(); + for (int j = 0; j < m; ++j) { + bp[j] += s * vData[j]; + } + } + + return new RealVectorImpl(bp, false); + + } + } + + /** + * Solve the linear equation A × X = B. + *

The A matrix is implicit here. It must have + * already been provided by a previous call to {@link #decompose(RealMatrix)}.

+ * @param b right-hand side of the equation A × X = B + * @return a vector X such that A × X = B + * @throws IllegalArgumentException if matrices dimensions don't match + * @throws InvalidMatrixException if decomposed matrix is singular + */ + public RealVectorImpl solve(final RealVectorImpl b) + throws IllegalArgumentException, InvalidMatrixException { + return new RealVectorImpl(solve(b.getDataRef()), false); + } + + /** {@inheritDoc} */ + public RealMatrix solve(final RealMatrix b) + throws IllegalArgumentException, InvalidMatrixException { + + checkNonSingular(); + + final int m = eigenvalues.length; + if (b.getRowDimension() != m) { + throw new IllegalArgumentException("Incorrect row dimension"); + } + + if (eigenvectors == null) { + findEigenVectors(); + } + + final int nColB = b.getColumnDimension(); + final double[][] bp = new double[m][nColB]; + for (int k = 0; k < nColB; ++k) { + for (int i = 0; i < m; ++i) { + final double[] vData = eigenvectors[i].getDataRef(); + double s = 0; + for (int j = 0; j < m; ++j) { + s += vData[j] * b.getEntry(j, k); + } + s /= eigenvalues[i]; + for (int j = 0; j < m; ++j) { + bp[j][k] += s * vData[j]; + } + } + } + + return new RealMatrixImpl(bp, false); + + } + + /** {@inheritDoc} */ + public RealMatrix getInverse() + throws IllegalStateException, InvalidMatrixException { + + checkNonSingular(); + final int m = eigenvalues.length; + final double[][] invData = new double[m][m]; + + if (eigenvectors == null) { + findEigenVectors(); + } + + for (int i = 0; i < m; ++i) { + final double[] invI = invData[i]; + for (int j = 0; j < m; ++j) { + double invIJ = 0; + for (int k = 0; k < m; ++k) { + final double[] vK = eigenvectors[k].getDataRef(); + invIJ += vK[i] * vK[j] / eigenvalues[k]; + } + invI[j] = invIJ; + } + } + return new RealMatrixImpl(invData, false); + + } + + /** {@inheritDoc} */ + public double getDeterminant() + throws IllegalStateException { + double determinant = 1; + for (double lambda : eigenvalues) { + determinant *= lambda; + } + return determinant; + } + + /** + * Transform matrix to tridiagonal. + * @param matrix matrix to transform + */ + private void transformToTridiagonal(final RealMatrix matrix) { + + // transform the matrix to tridiagonal + TriDiagonalTransformer transformer = new TriDiagonalTransformer(matrix); + main = transformer.getMainDiagonalRef(); + secondary = transformer.getSecondaryDiagonalRef(); + + // pre-compute some elements + squaredSecondary = new double[secondary.length]; + for (int i = 0; i < squaredSecondary.length; ++i) { + final double s = secondary[i]; + squaredSecondary[i] = s * s; + } + + orthoTridiag = transformer.getQ(); + + } + + /** + * Compute the Gershgorin circles for all rows. + */ + private void computeGershgorinCircles() { + + final int m = main.length; + final int lowerStart = 4 * m; + final int upperStart = 5 * m; + lowerSpectra = Double.POSITIVE_INFINITY; + upperSpectra = Double.NEGATIVE_INFINITY; + double eMax = 0; + + double eCurrent = 0; + for (int i = 0; i < m - 1; ++i) { + + final double dCurrent = main[i]; + final double ePrevious = eCurrent; + eCurrent = Math.abs(secondary[i]); + eMax = Math.max(eMax, eCurrent); + final double radius = ePrevious + eCurrent; + + final double lower = dCurrent - radius; + work[lowerStart + i] = lower; + lowerSpectra = Math.min(lowerSpectra, lower); + + final double upper = dCurrent + radius; + work[upperStart + i] = upper; + upperSpectra = Math.max(upperSpectra, upper); + + } + + final double dCurrent = main[m - 1]; + work[lowerStart + m - 1] = dCurrent - eCurrent; + work[upperStart + m - 1] = dCurrent + eCurrent; + minPivot = MathUtils.SAFE_MIN * Math.max(1.0, eMax * eMax); + + } + + /** + * Find the eigenvalues. + * @exception InvalidMatrixException if a block cannot be diagonalized + */ + private void findEigenvalues() + throws InvalidMatrixException { + + // compute splitting points + List splitIndices = computeSplits(); + + // find eigenvalues in each block + eigenvalues = new double[main.length]; + int begin = 0; + for (final int end : splitIndices) { + final int n = end - begin; + switch (n) { + + case 1: + // apply dedicated method for dimension 1 + process1RowBlock(begin); + break; + + case 2: + // apply dedicated method for dimension 2 + process2RowsBlock(begin); + break; + + case 3: + // apply dedicated method for dimension 3 + process3RowsBlock(begin); + break; + + default: + + // choose an initial shift for LDLT decomposition + final double[] range = eigenvaluesRange(begin, n); + final double oneFourth = 0.25 * (3 * range[0] + range[1]); + final int oneFourthCount = countEigenValues(oneFourth, begin, n); + final double threeFourth = 0.25 * (range[0] + 3 * range[1]); + final int threeFourthCount = countEigenValues(threeFourth, begin, n); + final boolean chooseLeft = (oneFourthCount - 1) >= (n - threeFourthCount); + final double lambda = chooseLeft ? range[0] : range[1]; + + tau = (range[1] - range[0]) * MathUtils.EPSILON * n + 2 * minPivot; + + // decompose TλI as LDLT + ldlTDecomposition(lambda, begin, n); + + // apply general dqd/dqds method + processGeneralBlock(n); + + // extract eigenvalues + if (chooseLeft) { + for (int i = 0; i < n; ++i) { + eigenvalues[begin + i] = lambda + work[4 * i]; + } + } else { + for (int i = 0; i < n; ++i) { + eigenvalues[begin + i] = lambda - work[4 * i]; + } + } + + } + begin = end; + } + + // sort the eigenvalues in decreasing order + Arrays.sort(eigenvalues); + for (int i = 0, j = eigenvalues.length - 1; i < j; ++i, --j) { + final double tmp = eigenvalues[i]; + eigenvalues[i] = eigenvalues[j]; + eigenvalues[j] = tmp; + } + + } + + /** + * Compute splitting points. + * @return list of indices after matrix can be split + */ + private List computeSplits() { + + final List list = new ArrayList(); + + if (splitTolerance < 0) { + // splitting based on absolute off-diagonal value + final double max = Math.abs(splitTolerance) * (upperSpectra - lowerSpectra); + for (int i = 0; i < secondary.length; ++i) { + if (Math.abs(secondary[i]) <= max) { + list.add(i + 1); + secondary[i] = 0; + squaredSecondary[i] = 0; + } + } + } else { + // splitting preserving relative accuracy + double absDCurrent = Math.abs(0); + for (int i = 0; i < secondary.length; ++i) { + final double absDPrevious = absDCurrent; + absDCurrent = Math.abs(i + 1); + final double max = splitTolerance * Math.sqrt(absDPrevious * absDCurrent); + if (Math.abs(secondary[i]) <= max) { + list.add(i + 1); + secondary[i] = 0; + squaredSecondary[i] = 0; + } + } + } + + list.add(secondary.length + 1); + return list; + + } + + /** + * Find eigenvalue in a block with 1 row. + *

In low dimensions, we simply solve the characteristic polynomial.

+ * @param index index of the first row of the block + */ + private void process1RowBlock(final int index) { + eigenvalues[index] = main[index]; + } + + /** + * Find eigenvalues in a block with 2 rows. + *

In low dimensions, we simply solve the characteristic polynomial.

+ * @param index index of the first row of the block + * @exception InvalidMatrixException if characteristic polynomial cannot be solved + */ + private void process2RowsBlock(final int index) + throws InvalidMatrixException { + + // the characteristic polynomial is + // X^2 - (q0 + q1) X + q0 q1 - e1^2 + final double q0 = main[index]; + final double q1 = main[index + 1]; + final double e12 = squaredSecondary[index]; + + final double s = q0 + q1; + final double p = q0 * q1 - e12; + final double delta = s * s - 4 * p; + if (delta < 0) { + throw new InvalidMatrixException("cannot solve degree {0} equation", new Object[] { 2 }); + } + + final double largestRoot = 0.5 * (s + Math.sqrt(delta)); + eigenvalues[index] = largestRoot; + eigenvalues[index + 1] = p / largestRoot; + + } + + /** + * Find eigenvalues in a block with 3 rows. + *

In low dimensions, we simply solve the characteristic polynomial.

+ * @param index index of the first row of the block + * @exception InvalidMatrixException if diagonal elements are not positive + */ + private void process3RowsBlock(final int index) + throws InvalidMatrixException { + + // the characteristic polynomial is + // X^3 - (q0 + q1 + q2) X^2 + (q0 q1 + q0 q2 + q1 q2 - e1^2 - e2^2) X + q0 e2^2 + q2 e1^2 - q0 q1 q2 + final double q0 = main[index]; + final double q1 = main[index + 1]; + final double q2 = main[index + 2]; + final double e12 = squaredSecondary[index]; + final double q1q2Me22 = q1 * q2 - squaredSecondary[index + 1]; + + // compute coefficients of the cubic equation as: x^3 + b x^2 + c x + d = 0 + final double b = -(q0 + q1 + q2); + final double c = q0 * q1 + q0 * q2 + q1q2Me22 - e12; + final double d = q2 * e12 - q0 * q1q2Me22; + + // solve cubic equation + final double b2 = b * b; + final double q = (3 * c - b2) / 9; + final double r = ((9 * c - 2 * b2) * b - 27 * d) / 54; + final double delta = q * q * q + r * r; + if (delta >= 0) { + // in fact, there are solutions to the equation, but in the context + // of symmetric eigenvalues problem, there should be three distinct + // real roots, so we throw an error if this condition is not met + throw new InvalidMatrixException("cannot solve degree {0} equation", new Object[] { 3 }); + } + final double sqrtMq = Math.sqrt(-q); + final double theta = Math.acos(r / (-q * sqrtMq)); + final double alpha = 2 * sqrtMq; + final double beta = b / 3; + + double z0 = alpha * Math.cos(theta / 3) - beta; + double z1 = alpha * Math.cos((theta + 2 * Math.PI) / 3) - beta; + double z2 = alpha * Math.cos((theta + 4 * Math.PI) / 3) - beta; + if (z0 < z1) { + final double t = z0; + z0 = z1; + z1 = t; + } + if (z1 < z2) { + final double t = z1; + z1 = z2; + z2 = t; + } + if (z0 < z1) { + final double t = z0; + z0 = z1; + z1 = t; + } + eigenvalues[index] = z0; + eigenvalues[index + 1] = z1; + eigenvalues[index + 2] = z2; + + } + + /** + * Find eigenvalues using dqd/dqds algorithms. + *

This implementation is based on Beresford N. Parlett + * and Osni A. Marques paper An + * Implementation of the dqds Algorithm (Positive Case) and on the + * corresponding LAPACK routine DLASQ2.

+ * @param n number of rows of the block + * @exception InvalidMatrixException if block cannot be diagonalized + * after 30 * n iterations + */ + private void processGeneralBlock(final int n) + throws InvalidMatrixException { + + // check decomposed matrix data range + final int fourN1 = 4 * (n - 1); + double sumDiag = 0; + double sumOffDiag = 0; +// qMax = Double.NEGATIVE_INFINITY; +// eMin = Double.POSITIVE_INFINITY; + for (int i = 0; i < n - 1; ++i) { + final int fourI = 4 * i; + final double qi = work[fourI]; + final double ei = work[fourI + 2]; +// qMax = Math.max(qMax, qi); +// eMin = Math.min(eMin, ei); + sumDiag += qi; + sumOffDiag += ei; + } + final double qi = work[fourN1]; +// qMax = Math.max(qMax, qi); + sumDiag += qi; + + if (sumOffDiag == 0) { + // matrix is already diagonal + return; + } + + // initial checks for splits (see Parlett & Marques section 3.3) + flipIfWarranted(n, 2); + + // two iterations with Li's test for initial splits + initialSplits(n); + + // initialize parameters used by goodStep + tType = 0; + dMin1 = 0; + dMin2 = 0; + dN = 0; + dN1 = 0; + dN2 = 0; + tau = 0; + + // process split segments + int i0 = 0; + int n0 = n; + while (n0 > 0) { + + // retrieve shift that was temporarily stored as a negative off-diagonal element + sigma = (n0 == n) ? 0 : -work[4 * n0 - 2]; + sigmaLow = 0; + + // find start of a new split segment to process + double eMin = (i0 == n0) ? 0 : work[4 * n0 - 6]; + double eMax = 0; + double qMax = work[4 * n0 - 4]; + double qMin = qMax; + i0 = 0; + for (int i = 4 * (n0 - 2); i >= 0; i -= 4) { + if (work[i + 2] <= 0) { + i0 = 1 + i / 4; + break; + } + if (qMin >= 4 * eMax) { + qMin = Math.min(qMin, work[i + 4]); + eMax = Math.max(eMax, work[i + 2]); + } + qMax = Math.max(qMax, work[i] + work[i + 2]); + eMin = Math.min(eMin, work[i + 2]); + } + work[4 * n0 - 2] = eMin; + + // lower bound of Gershgorin disk + dMin = -Math.max(0, qMin - 2 * Math.sqrt(qMin * eMax)); + + pingPong = 0; + int maxIter = 30 * (n0 - i0); + for (int k = 0; i0 < n0; ++k) { + if (k >= maxIter) { + throw new InvalidMatrixException(new MaxIterationsExceededException(maxIter)); + } + + // perform one step + n0 = goodStep(i0, n0); + pingPong = 1 - pingPong; + + // check for new splits after "ping" steps + // when the last elements of qd array are very small + if ((pingPong == 0) && (n0 - i0 > 3) && + (work[4 * n0 - 1] <= TOLERANCE_2 * qMax) && + (work[4 * n0 - 2] <= TOLERANCE_2 * sigma)) { + int split = i0 - 1; + qMax = work[4 * i0]; + eMin = work[4 * i0 + 2]; + double previousEMin = work[4 * i0 + 3]; + for (int i = 4 * i0; i < 4 * n0 - 11; i += 4) { + if ((work[i + 3] <= TOLERANCE_2 * work[i]) && + (work[i + 2] <= TOLERANCE_2 * sigma)) { + // insert a split here + work[i + 2] = -sigma; + split = i / 4; + qMax = 0; + eMin = work[i + 6]; + previousEMin = work[i + 7]; + } else { + qMax = Math.max(qMax, work[i + 4]); + eMin = Math.min(eMin, work[i + 2]); + previousEMin = Math.min(previousEMin, work[i + 3]); + } + } + work[4 * n0 - 2] = eMin; + work[4 * n0 - 1] = previousEMin; + i0 = split + 1; + } + } + + } + + } + + /** + * Perform two iterations with Li's tests for initial splits. + * @param n number of rows of the matrix to process + */ + private void initialSplits(final int n) { + + pingPong = 0; + for (int k = 0; k < 2; ++k) { + + // apply Li's reverse test + double d = work[4 * (n - 1) + pingPong]; + for (int i = 4 * (n - 2) + pingPong; i >= 0; i -= 4) { + if (work[i + 2] <= TOLERANCE_2 * d) { + work[i + 2] = -0.0; + d = work[i]; + } else { + d *= work[i] / (d + work[i + 2]); + } + } + + // apply dqd plus Li's forward test. +// eMin = work[4 + pingPong]; + d = work[pingPong]; + for (int i = 2 + pingPong; i < 4 * n - 2; i += 4) { + final int j = i - 2 * pingPong - 1; + work[j] = d + work[i]; + if (work[i] <= TOLERANCE_2 * d) { + work[i] = -0.0; + work[j] = d; + work[j + 2] = 0.0; + d = work[i + 2]; + } else if ((MathUtils.SAFE_MIN * work[i + 2] < work[j]) && + (MathUtils.SAFE_MIN * work[j] < work[i + 2])) { + final double tmp = work[i + 2] / work[j]; + work[j + 2] = work[i] * tmp; + d *= tmp; + } else { + work[j + 2] = work[i + 2] * (work[i] / work[j]); + d *= work[i + 2] / work[j]; + } +// eMin = Math.min(eMin, work[j + 2]); + } + work[4 * n - 3 - pingPong] = d; + +// // find qMax +// qMax = Double.NEGATIVE_INFINITY; +// for (int i = 1 - pingPong; i < 4 * n; i += 4) { +// qMax = Math.max(qMax, work[i]); +// } + + // from ping to pong + pingPong = 1 - pingPong; + + } + + } + + /** + * Perform one "good" dqd/dqds step. + *

This implementation is based on Beresford N. Parlett + * and Osni A. Marques paper An + * Implementation of the dqds Algorithm (Positive Case) and on the + * corresponding LAPACK routine DLAZQ3.

+ * @param start start index + * @param end end index + * @return new end (maybe deflated) + */ + private int goodStep(final int start, final int end) { + + g = 0.0; + + // step 1: accepting eigenvalues + int deflatedEnd = end; + for (boolean deflating = true; deflating;) { + + if (start >= deflatedEnd) { + // the array has been completely deflated + return deflatedEnd; + } + + final int k = 4 * deflatedEnd + pingPong - 1; + + if ((start == deflatedEnd - 1) || + ((start != deflatedEnd - 2) && + ((work[k - 5] <= TOLERANCE_2 * (sigma + work[k - 3])) || + (work[k - 2 * pingPong - 4] <= TOLERANCE_2 * work[k - 7])))) { + + // one eigenvalue found, deflate array + work[4 * deflatedEnd - 4] = sigma + work[4 * deflatedEnd - 4 + pingPong]; + deflatedEnd -= 1; + + } else if ((start == deflatedEnd - 2) || + (work[k - 9] <= TOLERANCE_2 * sigma) || + (work[k - 2 * pingPong - 8] <= TOLERANCE_2 * work[k - 11])) { + + // two eigenvalues found, deflate array + if (work[k - 3] > work[k - 7]) { + final double tmp = work[k - 3]; + work[k - 3] = work[k - 7]; + work[k - 7] = tmp; + } + + if (work[k - 5] > TOLERANCE_2 * work[k - 3]) { + double t = 0.5 * ((work[k - 7] - work[k - 3]) + work[k - 5]); + double s = work[k - 3] * (work[k - 5] / t); + if (s <= t) { + s = work[k - 3] * work[k - 5] / (t * (1 + Math.sqrt(1 + s / t))); + } else { + s = work[k - 3] * work[k - 5] / (t + Math.sqrt(t * (t + s))); + } + t = work[k - 7] + (s + work[k - 5]); + work[k - 3] *= work[k - 7] / t; + work[k - 7] = t; + } + work[4 * deflatedEnd - 8] = sigma + work[k - 7]; + work[4 * deflatedEnd - 4] = sigma + work[k - 3]; + deflatedEnd -= 2; + } else { + + // no more eigenvalues found, we need to iterate + deflating = false; + + } + + } + + final int l = 4 * deflatedEnd + pingPong - 1; + + // step 2: flip array if needed + if ((dMin <= 0) || (deflatedEnd < end)) { + if (flipIfWarranted(deflatedEnd, 1)) { + dMin2 = Math.min(dMin2, work[l - 1]); + work[l - 1] = + Math.min(work[l - 1], + Math.min(work[3 + pingPong], work[7 + pingPong])); + work[l - 2 * pingPong] = + Math.min(work[l - 2 * pingPong], + Math.min(work[6 + pingPong], work[6 + pingPong])); + qMax = Math.max(qMax, Math.max(work[3 + pingPong], work[7 + pingPong])); + dMin = -0.0; + } + } + + if ((dMin < 0) || + (MathUtils.SAFE_MIN * qMax < Math.min(work[l - 1], + Math.min(work[l - 9], + dMin2 + work[l - 2 * pingPong])))) { + // step 3: choose a shift + computeShiftIncrement(start, deflatedEnd, end - deflatedEnd); + + // step 4a: dqds + for (boolean loop = true; loop;) { + + // perform one dqds step with the chosen shift + dqds(start, deflatedEnd); + + // check result of the dqds step + if ((dMin >= 0) && (dMin1 > 0)) { + // the shift was good + updateSigma(tau); + return deflatedEnd; + } else if ((dMin < 0.0) && + (dMin1 > 0.0) && + (work[4 * deflatedEnd - 5 - pingPong] < TOLERANCE * (sigma + dN1)) && + (Math.abs(dN) < TOLERANCE * sigma)) { + // convergence hidden by negative DN. + work[4 * deflatedEnd - 3 - pingPong] = 0.0; + dMin = 0.0; + updateSigma(tau); + return deflatedEnd; + } else if (dMin < 0.0) { + // tau too big. Select new tau and try again. + if (tType < -22) { + // failed twice. Play it safe. + tau = 0.0; + } else if (dMin1 > 0.0) { + // late failure. Gives excellent shift. + tau = (tau + dMin) * (1.0 - 2.0 * MathUtils.EPSILON); + tType -= 11; + } else { + // early failure. Divide by 4. + tau *= 0.25; + tType -= 12; + } + } else if (Double.isNaN(dMin)) { + tau = 0.0; + } else { + // possible underflow. Play it safe. + loop = false; + } + } + + } + + // perform a dqd step (i.e. no shift) + dqd(start, deflatedEnd); + + return deflatedEnd; + + } + + /** + * Flip qd array if warranted. + * @param n number of rows in the block + * @param step within the array (1 for flipping all elements, 2 for flipping + * only every other element) + * @return true if qd array was flipped + */ + private boolean flipIfWarranted(final int n, final int step) { + if (1.5 * work[pingPong] < work[4 * (n - 1) + pingPong]) { + // flip array + for (int i = 0, j = 4 * n - 1; i < j; i += 4, j -= 4) { + for (int k = 0; k < 4; k += step) { + final double tmp = work[i + k]; + work[i + k] = work[j - k]; + work[j - k] = tmp; + } + } + return true; + } + return false; + } + + /** + * Compute an interval containing all eigenvalues of a block. + * @param index index of the first row of the block + * @param n number of rows of the block + * @return an interval containing the eigenvalues + */ + private double[] eigenvaluesRange(final int index, final int n) { + + // find the bounds of the spectra of the local block + final int lowerStart = 4 * main.length; + final int upperStart = 5 * main.length; + double lower = Double.POSITIVE_INFINITY; + double upper = Double.NEGATIVE_INFINITY; + for (int i = 0; i < n; ++i) { + lower = Math.min(lower, work[lowerStart + index +i]); + upper = Math.max(upper, work[upperStart + index +i]); + } + + // set thresholds + final double tNorm = Math.max(Math.abs(lower), Math.abs(upper)); + final double relativeTolerance = Math.sqrt(MathUtils.EPSILON); + final double absoluteTolerance = 4 * minPivot; + final int maxIter = + 2 + (int) ((Math.log(tNorm + minPivot) - Math.log(minPivot)) / Math.log(2.0)); + final double margin = 2 * (tNorm * MathUtils.EPSILON * n + 2 * minPivot); + + // search lower eigenvalue + double left = lower - margin; + double right = upper + margin; + for (int i = 0; i < maxIter; ++i) { + + final double range = right - left; + if ((range < absoluteTolerance) || + (range < relativeTolerance * Math.max(Math.abs(left), Math.abs(right)))) { + // search has converged + break; + } + + final double middle = 0.5 * (left + right); + if (countEigenValues(middle, index, n) >= 1) { + right = middle; + } else { + left = middle; + } + + } + lower = Math.max(lower, left - 100 * MathUtils.EPSILON * Math.abs(left)); + + // search upper eigenvalue + left = lower - margin; + right = upper + margin; + for (int i = 0; i < maxIter; ++i) { + + final double range = right - left; + if ((range < absoluteTolerance) || + (range < relativeTolerance * Math.max(Math.abs(left), Math.abs(right)))) { + // search has converged + break; + } + + final double middle = 0.5 * (left + right); + if (countEigenValues(middle, index, n) >= n) { + right = middle; + } else { + left = middle; + } + + } + upper = Math.min(upper, right + 100 * MathUtils.EPSILON * Math.abs(right)); + + return new double[] { lower, upper }; + + } + + /** + * Count the number of eigenvalues below a point. + * @param t value below which we must count the number of eigenvalues + * @param index index of the first row of the block + * @param n number of rows of the block + * @return number of eigenvalues smaller than t + */ + private int countEigenValues(final double t, final int index, final int n) { + double ratio = main[index] - t; + int count = (ratio > 0) ? 0 : 1; + for (int i = 1; i < n; ++i) { + ratio = main[index + i] - squaredSecondary[index + i - 1] / ratio - t; + if (ratio <= 0) { + ++count; + } + } + return count; + } + + /** + * Decompose the shifted tridiagonal matrix T-λI as LDLT. + *

A shifted symmetric tridiagonal matrix T can be decomposed as + * LDLT where L is a lower bidiagonal matrix with unit diagonal + * and D is a diagonal matrix. This method is an implementation of + * algorithm 4.4.7 from Dhillon's thesis.

+ * @param lambda shift to add to the matrix before decomposing it + * to ensure it is positive definite + * @param index index of the first row of the block + * @param n number of rows of the block + */ + private void ldlTDecomposition(final double lambda, final int index, final int n) { + double di = main[index] - lambda; + work[0] = Math.abs(di); + for (int i = 1; i < n; ++i) { + final int fourI = 4 * i; + final double eiM1 = secondary[index + i - 1]; + final double ratio = eiM1 / di; + work[fourI - 2] = ratio * ratio * Math.abs(di); + di = (main[index + i] - lambda) - eiM1 * ratio; + work[fourI] = Math.abs(di); + } + } + + /** + * Perform a dqds step, using current shift increment. + *

This implementation is a translation of the LAPACK routine DLASQ5.

+ * @param start start index + * @param end end index + */ + private void dqds(final int start, final int end) { + + eMin = work[4 * start + pingPong + 4]; + double d = work[4 * start + pingPong] - tau; + dMin = d; + dMin1 = -work[4 * start + pingPong]; + + if (pingPong == 0) { + for (int j4 = 4 * start + 3; j4 <= 4 * (end - 3); j4 += 4) { + work[j4 - 2] = d + work[j4 - 1]; + final double tmp = work[j4 + 1] / work[j4 - 2]; + d = d * tmp - tau; + dMin = Math.min(dMin, d); + work[j4] = work[j4 - 1] * tmp; + eMin = Math.min(work[j4], eMin); + } + } else { + for (int j4 = 4 * start + 3; j4 <= 4 * (end - 3); j4 += 4) { + work[j4 - 3] = d + work[j4]; + final double tmp = work[j4 + 2] / work[j4 - 3]; + d = d * tmp - tau; + dMin = Math.min(dMin, d); + work[j4 - 1] = work[j4] * tmp; + eMin = Math.min(work[j4 - 1], eMin); + } + } + + // unroll last two steps. + dN2 = d; + dMin2 = dMin; + int j4 = 4 * (end - 2) - pingPong - 1; + int j4p2 = j4 + 2 * pingPong - 1; + work[j4 - 2] = dN2 + work[j4p2]; + work[j4] = work[j4p2 + 2] * (work[j4p2] / work[j4 - 2]); + dN1 = work[j4p2 + 2] * (dN2 / work[j4 - 2]) - tau; + dMin = Math.min(dMin, dN1); + + dMin1 = dMin; + j4 = j4 + 4; + j4p2 = j4 + 2 * pingPong - 1; + work[j4 - 2] = dN1 + work[j4p2]; + work[j4] = work[j4p2 + 2] * (work[j4p2] / work[j4 - 2]); + dN = work[j4p2 + 2] * (dN1 / work[j4 - 2]) - tau; + dMin = Math.min(dMin, dN); + + work[j4 + 2] = dN; + work[4 * end - pingPong - 1] = eMin; + + } + + + /** + * Perform a dqd step. + *

This implementation is a translation of the LAPACK routine DLASQ6.

+ * @param start start index + * @param end end index + */ + private void dqd(final int start, final int end) { + + eMin = work[4 * start + pingPong + 4]; + double d = work[4 * start + pingPong]; + dMin = d; + + if (pingPong == 0) { + for (int j4 = 4 * start + 3; j4 < 4 * (end - 3); j4 += 4) { + work[j4 - 2] = d + work[j4 - 1]; + if (work[j4 - 2] == 0.0) { + work[j4] = 0.0; + d = work[j4 + 1]; + dMin = d; + eMin = 0.0; + } else if ((MathUtils.SAFE_MIN * work[j4 + 1] < work[j4 - 2]) && + (MathUtils.SAFE_MIN * work[j4 - 2] < work[j4 + 1])) { + final double tmp = work[j4 + 1] / work[j4 - 2]; + work[j4] = work[j4 - 1] * tmp; + d *= tmp; + } else { + work[j4] = work[j4 + 1] * (work[j4 - 1] / work[j4 - 2]); + d *= work[j4 + 1] / work[j4 - 2]; + } + dMin = Math.min(dMin, d); + eMin = Math.min(eMin, work[j4]); + } + } else { + for (int j4 = 4 * start + 3; j4 < 4 * (end - 3); j4 += 4) { + work[j4 - 3] = d + work[j4]; + if (work[j4 - 3] == 0.0) { + work[j4 - 1] = 0.0; + d = work[j4 + 2]; + dMin = d; + eMin = 0.0; + } else if ((MathUtils.SAFE_MIN * work[j4 + 2] < work[j4 - 3]) && + (MathUtils.SAFE_MIN * work[j4 - 3] < work[j4 + 2])) { + final double tmp = work[j4 + 2] / work[j4 - 3]; + work[j4 - 1] = work[j4] * tmp; + d *= tmp; + } else { + work[j4 - 1] = work[j4 + 2] * (work[j4] / work[j4 - 3]); + d *= work[j4 + 2] / work[j4 - 3]; + } + dMin = Math.min(dMin, d); + eMin = Math.min(eMin, work[j4 - 1]); + } + } + + // Unroll last two steps + dN2 = d; + dMin2 = dMin; + int j4 = 4 * (end - 2) - pingPong - 1; + int j4p2 = j4 + 2 * pingPong - 1; + work[j4 - 2] = dN2 + work[j4p2]; + if (work[j4 - 2] == 0.0) { + work[j4] = 0.0; + dN1 = work[j4p2 + 2]; + dMin = dN1; + eMin = 0.0; + } else if ((MathUtils.SAFE_MIN * work[j4p2 + 2] < work[j4 - 2]) && + (MathUtils.SAFE_MIN * work[j4 - 2] < work[j4p2 + 2])) { + final double tmp = work[j4p2 + 2] / work[j4 - 2]; + work[j4] = work[j4p2] * tmp; + dN1 = dN2 * tmp; + } else { + work[j4] = work[j4p2 + 2] * (work[j4p2] / work[j4 - 2]); + dN1 = work[j4p2 + 2] * (dN2 / work[j4 - 2]); + } + dMin = Math.min(dMin, dN1); + + dMin1 = dMin; + j4 = j4 + 4; + j4p2 = j4 + 2 * pingPong - 1; + work[j4 - 2] = dN1 + work[j4p2]; + if (work[j4 - 2] == 0.0) { + work[j4] = 0.0; + dN = work[j4p2 + 2]; + dMin = dN; + eMin = 0.0; + } else if ((MathUtils.SAFE_MIN * work[j4p2 + 2] < work[j4 - 2]) && + (MathUtils.SAFE_MIN * work[j4 - 2] < work[j4p2 + 2])) { + final double tmp = work[j4p2 + 2] / work[j4 - 2]; + work[j4] = work[j4p2] * tmp; + dN = dN1 * tmp; + } else { + work[j4] = work[j4p2 + 2] * (work[j4p2] / work[j4 - 2]); + dN = work[j4p2 + 2] * (dN1 / work[j4 - 2]); + } + dMin = Math.min(dMin, dN); + + work[j4 + 2] = dN; + work[4 * end - pingPong - 1] = eMin; + + } + + /** + * Compute the shift increment as an estimate of the smallest eigenvalue. + *

This implementation is a translation of the LAPACK routine DLAZQ4.

+ * @param start start index + * @param end end index + * @param deflated number of eigenvalues just deflated + */ + private void computeShiftIncrement(final int start, final int end, final int deflated) { + + final double cnst1 = 0.563; + final double cnst2 = 1.010; + final double cnst3 = 1.05; + + // a negative dMin forces the shift to take that absolute value + // tType records the type of shift. + if (dMin <= 0.0) { + tau = -dMin; + tType = -1; + return; + } + + int nn = 4 * end + pingPong - 1; + switch (deflated) { + + case 0 : // no eigenvalues deflated. + if (dMin == dN || dMin == dN1) { + + double b1 = Math.sqrt(work[nn - 3]) * Math.sqrt(work[nn - 5]); + double b2 = Math.sqrt(work[nn - 7]) * Math.sqrt(work[nn - 9]); + double a2 = work[nn - 7] + work[nn - 5]; + + if (dMin == dN && dMin1 == dN1) { + // cases 2 and 3. + final double gap2 = dMin2 - a2 - dMin2 * 0.25; + final double gap1 = a2 - dN - ((gap2 > 0.0 && gap2 > b2) ? (b2 / gap2) * b2 : (b1 + b2)); + if (gap1 > 0.0 && gap1 > b1) { + tau = Math.max(dN - (b1 / gap1) * b1, 0.5 * dMin); + tType = -2; + } else { + double s = 0.0; + if (dN > b1) { + s = dN - b1; + } + if (a2 > (b1 + b2)) { + s = Math.min(s, a2 - (b1 + b2)); + } + tau = Math.max(s, 0.333 * dMin); + tType = -3; + } + } else { + // case 4. + tType = -4; + double s = 0.25 * dMin; + double gam; + int np; + if (dMin == dN) { + gam = dN; + a2 = 0.0; + if (work[nn - 5] > work[nn - 7]) { + return; + } + b2 = work[nn - 5] / work[nn - 7]; + np = nn - 9; + } else { + np = nn - 2 * pingPong; + b2 = work[np - 2]; + gam = dN1; + if (work[np - 4] > work[np - 2]) { + return; + } + a2 = work[np - 4] / work[np - 2]; + if (work[nn - 9] > work[nn - 11]) { + return; + } + b2 = work[nn - 9] / work[nn - 11]; + np = nn - 13; + } + + // approximate contribution to norm squared from i < nn-1. + a2 = a2 + b2; + for (int i4 = np; i4 >= 4 * start + 2 + pingPong; i4 -= 4) { + if(b2 == 0.0) { + break; + } + b1 = b2; + if (work[i4] > work[i4 - 2]) { + return; + } + b2 = b2 * (work[i4] / work[i4 - 2]); + a2 = a2 + b2; + if (100 * Math.max(b2, b1) < a2 || cnst1 < a2) { + break; + } + } + a2 = cnst3 * a2; + + // rayleigh quotient residual bound. + if (a2 < cnst1) { + s = gam * (1 - Math.sqrt(a2)) / (1 + a2); + } + tau = s; + + } + } else if (dMin == dN2) { + + // case 5. + tType = -5; + double s = 0.25 * dMin; + + // compute contribution to norm squared from i > nn-2. + final int np = nn - 2 * pingPong; + double b1 = work[np - 2]; + double b2 = work[np - 6]; + final double gam = dN2; + if (work[np - 8] > b2 || work[np - 4] > b1) { + return; + } + double a2 = (work[np - 8] / b2) * (1 + work[np - 4] / b1); + + // approximate contribution to norm squared from i < nn-2. + if (end - start > 2) { + b2 = work[nn - 13] / work[nn - 15]; + a2 = a2 + b2; + for (int i4 = nn - 17; i4 >= 4 * start + 2 + pingPong; i4 -= 4) { + if (b2 == 0.0) { + break; + } + b1 = b2; + if (work[i4] > work[i4 - 2]) { + return; + } + b2 = b2 * (work[i4] / work[i4 - 2]); + a2 = a2 + b2; + if (100 * Math.max(b2, b1) < a2 || cnst1 < a2) { + break; + } + } + a2 = cnst3 * a2; + } + + if (a2 < cnst1) { + tau = gam * (1 - Math.sqrt(a2)) / (1 + a2); + } else { + tau = s; + } + + } else { + + // case 6, no information to guide us. + if (tType == -6) { + g += 0.333 * (1 - g); + } else if (tType == -18) { + g = 0.25 * 0.333; + } else { + g = 0.25; + } + tau = g * dMin; + tType = -6; + + } + break; + + case 1 : // one eigenvalue just deflated. use dMin1, dN1 for dMin and dN. + if (dMin1 == dN1 && dMin2 == dN2) { + + // cases 7 and 8. + tType = -7; + double s = 0.333 * dMin1; + if (work[nn - 5] > work[nn - 7]) { + return; + } + double b1 = work[nn - 5] / work[nn - 7]; + double b2 = b1; + if (b2 != 0.0) { + for (int i4 = 4 * end - 10 + pingPong; i4 >= 4 * start + 2 + pingPong; i4 -= 4) { + final double oldB1 = b1; + if (work[i4] > work[i4 - 2]) { + return; + } + b1 = b1 * (work[i4] / work[i4 - 2]); + b2 = b2 + b1; + if (100 * Math.max(b1, oldB1) < b2) { + break; + } + } + } + b2 = Math.sqrt(cnst3 * b2); + final double a2 = dMin1 / (1 + b2 * b2); + final double gap2 = 0.5 * dMin2 - a2; + if (gap2 > 0.0 && gap2 > b2 * a2) { + tau = Math.max(s, a2 * (1 - cnst2 * a2 * (b2 / gap2) * b2)); + } else { + tau = Math.max(s, a2 * (1 - cnst2 * b2)); + tType = -8; + } + } else { + + // case 9. + tau = 0.25 * dMin1; + if (dMin1 == dN1) { + tau = 0.5 * dMin1; + } + tType = -9; + } + break; + + case 2 : // two eigenvalues deflated. use dMin2, dN2 for dMin and dN. + + // cases 10 and 11. + if (dMin2 == dN2 && 2 * work[nn - 5] < work[nn - 7]) { + tType = -10; + final double s = 0.333 * dMin2; + if (work[nn - 5] > work[nn - 7]) { + return; + } + double b1 = work[nn - 5] / work[nn - 7]; + double b2 = b1; + if (b2 != 0.0){ + for (int i4 = 4 * end - 9 + pingPong; i4 >= 4 * start + 2 + pingPong; i4 -= 4) { + if (work[i4] > work[i4 - 2]) { + return; + } + b1 *= work[i4] / work[i4 - 2]; + b2 += b1; + if (100 * b1 < b2) { + break; + } + } + } + b2 = Math.sqrt(cnst3 * b2); + final double a2 = dMin2 / (1 + b2 * b2); + final double gap2 = work[nn - 7] + work[nn - 9] - + Math.sqrt(work[nn - 11]) * Math.sqrt(work[nn - 9]) - a2; + if (gap2 > 0.0 && gap2 > b2 * a2) { + tau = Math.max(s, a2 * (1 - cnst2 * a2 * (b2 / gap2) * b2)); + } else { + tau = Math.max(s, a2 * (1 - cnst2 * b2)); + } + } else { + tau = 0.25 * dMin2; + tType = -11; + } + break; + + default : // case 12, more than two eigenvalues deflated. no information. + tau = 0.0; + tType = -12; + } + + } + + /** + * Update sigma. + * @param tau shift to apply to sigma + */ + private void updateSigma(final double tau) { + // BEWARE: do NOT attempt to simplify the following statements + // the expressions below take care to accumulate the part of sigma + // that does not fit within a double variable into sigmaLow + if (tau < sigma) { + sigmaLow += tau; + final double t = sigma + sigmaLow; + sigmaLow -= t - sigma; + sigma = t; + } else { + final double t = sigma + tau; + sigmaLow += sigma - (t - tau); + sigma = t; + } + } + + /** + * Find eigenvectors. + */ + private void findEigenVectors() { + + final int m = main.length; + eigenvectors = new RealVectorImpl[m]; + + // perform an initial non-shifted LDLt decomposition + final double[] d = new double[m]; + final double[] l = new double[m - 1]; + double di = main[0]; + d[0] = di; + for (int i = 1; i < m; ++i) { + final double eiM1 = secondary[i - 1]; + final double ratio = eiM1 / di; + di = main[i] - eiM1 * ratio; + l[i - 1] = ratio; + d[i] = di; + } + + // compute eigenvectors + for (int i = 0; i < m; ++i) { + eigenvectors[i] = findEigenvector(eigenvalues[i], d, l); + } + + } + + /** + * Find an eigenvector corresponding to an eigenvalue, using bidiagonals. + *

This method corresponds to algorithm X from Dhillon's thesis.

+ * + * @param eigenvalue eigenvalue for which eigenvector is desired + * @param d diagonal elements of the initial non-shifted D matrix + * @param l off-diagonal elements of the initial non-shifted L matrix + * @return an eigenvector + */ + private RealVectorImpl findEigenvector(final double eigenvalue, + final double[] d, final double[] l) { + + // compute the LDLt and UDUt decompositions of the + // perfectly shifted tridiagonal matrix + final int m = main.length; + stationaryQuotientDifferenceWithShift(d, l, eigenvalue); + progressiveQuotientDifferenceWithShift(d, l, eigenvalue); + + // select the twist index leading to + // the least diagonal element in the twisted factorization + int r = m - 1; + double minG = Math.abs(work[6 * r] + work[6 * r + 3] + eigenvalue); + for (int i = 0, sixI = 0; i < m - 1; ++i, sixI += 6) { + final double g = work[sixI] + d[i] * work[sixI + 9] / work[sixI + 10]; + final double absG = Math.abs(g); + if (absG < minG) { + r = i; + minG = absG; + } + } + + // solve the singular system by ignoring the equation + // at twist index and propagating upwards and downwards + double[] eigenvector = new double[m]; + double n2 = 1; + eigenvector[r] = 1; + double z = 1; + for (int i = r - 1; i >= 0; --i) { + z *= -work[6 * i + 2]; + eigenvector[i] = z; + n2 += z * z; + } + z = 1; + for (int i = r + 1; i < m; ++i) { + z *= -work[6 * i - 1]; + eigenvector[i] = z; + n2 += z * z; + } + + // normalize vector + final double inv = 1.0 / Math.sqrt(n2); + for (int i = 0; i < m; ++i) { + eigenvector[i] *= inv; + } + + return new RealVectorImpl(orthoTridiag.operate(eigenvector), true); + + } + + /** + * Decompose matrix LDLT - λ I as + * L+D+L+T. + *

This method corresponds to algorithm 4.4.3 (dstqds) from Dhillon's thesis.

+ * @param d diagonal elements of D, + * @param l off-diagonal elements of L + * @param lambda shift to apply + */ + private void stationaryQuotientDifferenceWithShift(final double[] d, final double[] l, + final double lambda) { + final int nM1 = d.length - 1; + double si = -lambda; + for (int i = 0, sixI = 0; i < nM1; ++i, sixI += 6) { + final double di = d[i]; + final double li = l[i]; + final double diP1 = di + si; + final double liP1 = li * di / diP1; + work[sixI] = si; + work[sixI + 1] = diP1; + work[sixI + 2] = liP1; + si = li * liP1 * si - lambda; + } + work[6 * nM1 + 1] = d[nM1] + si; + work[6 * nM1] = si; + } + + /** + * Decompose matrix LDLT - λ I as + * U-D-U-T. + *

This method corresponds to algorithm 4.4.5 (dqds) from Dhillon's thesis.

+ * @param d diagonal elements of D + * @param l off-diagonal elements of L + * @param lambda shift to apply + */ + private void progressiveQuotientDifferenceWithShift(final double[] d, final double[] l, + final double lambda) { + final int nM1 = d.length - 1; + double pi = d[nM1] - lambda; + for (int i = nM1 - 1, sixI = 6 * i; i >= 0; --i, sixI -= 6) { + final double di = d[i]; + final double li = l[i]; + final double diP1 = di * li * li + pi; + final double t = di / diP1; + work[sixI + 9] = pi; + work[sixI + 10] = diP1; + work[sixI + 5] = li * t; + pi = pi * t - lambda; + } + work[3] = pi; + work[4] = pi; + } + + /** + * Check if decomposition has been performed. + * @exception IllegalStateException if {@link #decompose(RealMatrix) decompose} + * has not been called + */ + private void checkDecomposed() + throws IllegalStateException { + if (eigenvalues == null) { + throw MathRuntimeException.createIllegalStateException("no matrix have been decomposed yet", null); + } + } + + /** + * Check if decomposed matrix is non singular. + * @exception IllegalStateException if {@link #decompose(RealMatrix) decompose} + * has not been called + * @exception SingularMatrixException if decomposed matrix is singular + */ + private void checkNonSingular() + throws IllegalStateException, SingularMatrixException { + checkDecomposed(); + if (!isNonSingular()) { + throw new SingularMatrixException(); + } + } + +} diff --git a/src/java/org/apache/commons/math/linear/GershgorinCirclesUnion.java b/src/java/org/apache/commons/math/linear/GershgorinCirclesUnion.java new file mode 100644 index 000000000..e89e52b51 --- /dev/null +++ b/src/java/org/apache/commons/math/linear/GershgorinCirclesUnion.java @@ -0,0 +1,94 @@ +/* + * 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.linear; + +/** Class representing a union of Gershgorin circles. + *

Gershgorin circles are bounding areas where eigenvalues must lie. + * They are used as starting values for eigen decomposition algorithms. + * In the real case, Gershgorin circles are simple intervals.

+ * @see EigenDecompositionImpl + * @version $Revision$ $Date$ + * @since 2.0 + */ +class GershgorinCirclesUnion implements Comparable { + + /** Lower bound of the interval. */ + private double low; + + /** Higher bound of the interval. */ + private double high; + + /** Create a simple Gershgorin circle. + * @param d diagonal element of the current row + * @param sum sum of the absolute values of the off-diagonal elements + * of the current row + */ + public GershgorinCirclesUnion(final double d, final double sum) { + low = d - sum; + high = d + sum; + } + + /** + * Get the lower bound of the interval. + * @return lower bound of the interval + */ + public double getLow() { + return low; + } + + /** + * Get the higher bound of the interval. + * @return higher bound of the interval + */ + public double getHigh() { + return high; + } + + /** + * Check if a Gershgorin circles union intersects instance. + * @param other Gershgorin circles union to test against instance + * @return true if the other Gershgorin circles union intersects instance + */ + public boolean intersects(final GershgorinCirclesUnion other) { + return (other.low <= this.high) && (other.high >= this.low); + } + + /** + * Swallow another Gershgorin circles union. + *

Swallowing another Gershgorin circles union changes the + * instance such that it contains everything that was formerly in + * either circles union. It is mainly intended for circles unions + * that {@link #intersects(GershgorinCirclesUnion) intersect} + * each other beforehand.

+ * @param other Gershgorin circles union to swallow + */ + public void swallow(final GershgorinCirclesUnion other) { + low = Math.min(low, other.low); + high = Math.max(high, other.high); + } + + /** Compare another Gershgorin circles union in interval start order. + * @param other Gershgorin circles union to compare to instance + * @return a negative, zero or positive value depending on the other + * union starting before, at same location or after instance + */ + public int compareTo(GershgorinCirclesUnion other) { + return Double.compare(low, other.low); + } + +} diff --git a/src/java/org/apache/commons/math/linear/InvalidMatrixException.java b/src/java/org/apache/commons/math/linear/InvalidMatrixException.java index e0ac2a28e..f9e905102 100644 --- a/src/java/org/apache/commons/math/linear/InvalidMatrixException.java +++ b/src/java/org/apache/commons/math/linear/InvalidMatrixException.java @@ -17,30 +17,37 @@ package org.apache.commons.math.linear; +import org.apache.commons.math.MathRuntimeException; + /** * Thrown when a system attempts an operation on a matrix, and * that matrix does not satisfy the preconditions for the * aforementioned operation. * @version $Revision$ $Date$ */ -public class InvalidMatrixException extends RuntimeException { +public class InvalidMatrixException extends MathRuntimeException { - /** Serializable version identifier */ - private static final long serialVersionUID = 5318837237354354107L; + /** Serializable version identifier. */ + private static final long serialVersionUID = 1135533765052675495L; /** - * Default constructor. + * Construct an exception with the given message. + * @param pattern format specifier + * @param arguments format arguments + * @since 2.0 */ - public InvalidMatrixException() { - this(null); + public InvalidMatrixException(final String pattern, final Object[] arguments) { + super(pattern, arguments); } /** * Construct an exception with the given message. - * @param message descriptive error message. + * @param cause the exception or error that caused this exception + * to be thrown. + * @since 2.0 */ - public InvalidMatrixException(String message) { - super(message); + public InvalidMatrixException(final Throwable cause) { + super(cause); } } diff --git a/src/java/org/apache/commons/math/linear/LUDecomposition.java b/src/java/org/apache/commons/math/linear/LUDecomposition.java new file mode 100644 index 000000000..62893ee26 --- /dev/null +++ b/src/java/org/apache/commons/math/linear/LUDecomposition.java @@ -0,0 +1,113 @@ +/* + * 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.linear; + +/** + * An interface to classes that implement an algorithm to calculate the + * LU-decomposition of a real matrix. + *

The LU-decomposition of matrix A is a set of three matrices: P, L and U + * such that P×A = L×U. P is a rows permutation matrix that is used + * to rearrange the rows of A before so that it can be decomposed. L is a lower + * triangular matrix with unit diagonal terms and U is an upper triangular matrix.

+ *

This interface is based on the class with similar name from the now defunct + * JAMA library, with the + * following changes:

+ *
    + *
  • several signatures have been added for the solve methods + * (in the superinterface),
  • + *
  • a {@link DecompositionSolver#decompose(RealMatrix) decompose(RealMatrix)} + * method has been added (in the superinterface),
  • + *
  • a {@link DecompositionSolver#isNonSingular() isNonSingular} method has + * been added (in the superinterface),
  • + *
  • a {@link DecompositionSolver#getInverse() getInverse} method has been + * added (in the superinterface),
  • + *
  • the det method has been renamed as {@link #getDeterminant() getDeterminant}.
  • + *
+ * + * @see MathWorld + * @see Wikipedia + * @version $Revision$ $Date$ + * @since 2.0 + */ +public interface LUDecomposition extends DecompositionSolver { + + /** + * Computes a new + * + * LU decomposition for this matrix, storing the result for use by other methods. + *

+ * Implementation Note:
+ * Uses + * Crout's algorithm, with partial pivoting.

+ * @param matrix The matrix to decompose. + * @param singularityThreshold threshold (based on partial row norm) + * under which a matrix is considered singular + * @exception InvalidMatrixException if matrix is not square + */ + void decompose(RealMatrix matrix, double singularityThreshold); + + /** + * Returns the matrix L of the decomposition. + *

L is an lower-triangular matrix

+ * @return the L matrix (or null if decomposed matrix is singular) + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + */ + RealMatrix getL() throws IllegalStateException; + + /** + * Returns the matrix U of the decomposition. + *

U is an upper-triangular matrix

+ * @return the U matrix (or null if decomposed matrix is singular) + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + */ + RealMatrix getU() throws IllegalStateException; + + /** + * Returns the P rows permutation matrix. + *

P is a sparse matrix with exactly one element set to 1.0 in + * each row and each column, all other elements being set to 0.0.

+ *

The positions of the 1 elements are given by the {@link #getPivot() + * pivot permutation vector}.

+ * @return the P rows permutation matrix (or null if decomposed matrix is singular) + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + * @see #getPivot() + */ + RealMatrix getP() throws IllegalStateException; + + /** + * Returns the pivot permutation vector. + * @return the pivot permutation vector + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + * @see #getPermutation() + */ + int[] getPivot() throws IllegalStateException; + + /** + * Return the determinant of the matrix + * @return determinant of the matrix + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + * @see #isNonSingular() + */ + double getDeterminant() throws IllegalStateException; + +} diff --git a/src/java/org/apache/commons/math/linear/LUDecompositionImpl.java b/src/java/org/apache/commons/math/linear/LUDecompositionImpl.java new file mode 100644 index 000000000..e63da7e33 --- /dev/null +++ b/src/java/org/apache/commons/math/linear/LUDecompositionImpl.java @@ -0,0 +1,440 @@ +/* + * 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.linear; + +import org.apache.commons.math.MathRuntimeException; + +/** + * Calculates the LUP-decomposition of a square matrix. + *

The LUP-decomposition of a matrix A consists of three matrices + * L, U and P that satisfy: A = LUP, L is lower triangular, and U is + * upper triangular and P is a permutation matrix. All matrices are + * m×m.

+ *

As shown by the presence of the P matrix, this decomposition is + * implemented using partial pivoting.

+ * + * @version $Revision$ $Date$ + * @since 2.0 + */ +public class LUDecompositionImpl implements LUDecomposition { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 3446121671437672843L; + + /** Entries of LU decomposition. */ + private double lu[][]; + + /** Pivot permutation associated with LU decomposition */ + private int[] pivot; + + /** Parity of the permutation associated with the LU decomposition */ + private int parity; + + /** Singularity indicator. */ + private boolean singular; + + /** Cached value of L. */ + private RealMatrix cachedL; + + /** Cached value of U. */ + private RealMatrix cachedU; + + /** Cached value of P. */ + private RealMatrix cachedP; + + /** Default bound to determine effective singularity in LU decomposition */ + private static final double DEFAULT_TOO_SMALL = 10E-12; + + /** + * Build a new instance. + *

Note that either {@link #decompose(RealMatrix)} or + * {@link #decompose(RealMatrix, double)} must be called + * before any of the {@link #getP()}, {@link #getPivot()}, {@link #getL()}, + * {@link #getU()}, {@link #getDeterminant()}, {@link #isNonSingular()}, + * {@link #solve(double[])}, {@link #solve(RealMatrix)}, {@link #solve(RealVector)} + * or {@link #solve(RealVectorImpl)} methods can be called.

+ * @see #decompose(RealMatrix) + * @see #decompose(RealMatrix, double) + */ + public LUDecompositionImpl() { + } + + /** + * Calculates the LU-decomposition of the given matrix. + *

Calling this constructor is equivalent to first call the no-arguments + * constructor and then call {@link #decompose(RealMatrix)}.

+ * @param matrix The matrix to decompose. + * @exception InvalidMatrixException if matrix is not square + */ + public LUDecompositionImpl(RealMatrix matrix) + throws InvalidMatrixException { + decompose(matrix); + } + + /** + * Calculates the LU-decomposition of the given matrix. + *

Calling this constructor is equivalent to first call the no-arguments + * constructor and then call {@link #decompose(RealMatrix, double)}.

+ * @param matrix The matrix to decompose. + * @param singularityThreshold threshold (based on partial row norm) + * under which a matrix is considered singular + * @exception InvalidMatrixException if matrix is not square + */ + public LUDecompositionImpl(RealMatrix matrix, double singularityThreshold) + throws InvalidMatrixException { + decompose(matrix, singularityThreshold); + } + + /** {@inheritDoc} */ + public void decompose(RealMatrix matrix) + throws InvalidMatrixException { + decompose(matrix, DEFAULT_TOO_SMALL); + } + + /** {@inheritDoc} */ + public void decompose(RealMatrix matrix, double singularityThreshold) + throws InvalidMatrixException { + if (!matrix.isSquare()) { + throw new NonSquareMatrixException(matrix.getRowDimension(), matrix.getColumnDimension()); + } + final int m = matrix.getColumnDimension(); + lu = matrix.getData(); + pivot = new int[m]; + cachedL = null; + cachedU = null; + cachedP = null; + + // Initialize permutation array and parity + for (int row = 0; row < m; row++) { + pivot[row] = row; + } + parity = 1; + singular = false; + + // Loop over columns + for (int col = 0; col < m; col++) { + + double sum = 0; + + // upper + for (int row = 0; row < col; row++) { + final double[] luRow = lu[row]; + sum = luRow[col]; + for (int i = 0; i < row; i++) { + sum -= luRow[i] * lu[i][col]; + } + luRow[col] = sum; + } + + // lower + int max = col; // permutation row + double largest = Double.NEGATIVE_INFINITY; + for (int row = col; row < m; row++) { + final double[] luRow = lu[row]; + sum = luRow[col]; + for (int i = 0; i < col; i++) { + sum -= luRow[i] * lu[i][col]; + } + luRow[col] = sum; + + // maintain best permutation choice + if (Math.abs(sum) > largest) { + largest = Math.abs(sum); + max = row; + } + } + + // Singularity check + if (Math.abs(lu[max][col]) < singularityThreshold) { + singular = true; + return; + } + + // Pivot if necessary + if (max != col) { + double tmp = 0; + for (int i = 0; i < m; i++) { + tmp = lu[max][i]; + lu[max][i] = lu[col][i]; + lu[col][i] = tmp; + } + int temp = pivot[max]; + pivot[max] = pivot[col]; + pivot[col] = temp; + parity = -parity; + } + + // Divide the lower elements by the "winning" diagonal elt. + final double luDiag = lu[col][col]; + for (int row = col + 1; row < m; row++) { + lu[row][col] /= luDiag; + } + } + + } + + /** {@inheritDoc} */ + public RealMatrix getL() + throws IllegalStateException { + checkDecomposed(); + if ((cachedL == null) && !singular) { + final int m = pivot.length; + final double[][] lData = new double[m][m]; + for (int i = 0; i < m; ++i) { + System.arraycopy(lu[i], 0, lData[i], 0, i); + lData[i][i] = 1.0; + } + cachedL = new RealMatrixImpl(lData, false); + } + return cachedL; + } + + /** {@inheritDoc} */ + public RealMatrix getU() + throws IllegalStateException { + checkDecomposed(); + if ((cachedU == null) && !singular) { + final int m = pivot.length; + final double[][] uData = new double[m][m]; + for (int i = 0; i < m; ++i) { + System.arraycopy(lu[i], i, uData[i], i, m - i); + } + cachedU = new RealMatrixImpl(uData, false); + } + return cachedU; + } + + /** {@inheritDoc} */ + public RealMatrix getP() + throws IllegalStateException { + checkDecomposed(); + if ((cachedP == null) && !singular) { + final int m = pivot.length; + final double[][] pData = new double[m][m]; + for (int i = 0; i < m; ++i) { + pData[i][pivot[i]] = 1.0; + } + cachedP = new RealMatrixImpl(pData, false); + } + return cachedP; + } + + /** {@inheritDoc} */ + public int[] getPivot() + throws IllegalStateException { + checkDecomposed(); + return pivot.clone(); + } + + /** {@inheritDoc} */ + public boolean isNonSingular() + throws IllegalStateException { + checkDecomposed(); + return !singular; + } + + /** {@inheritDoc} */ + public double getDeterminant() + throws IllegalStateException { + checkDecomposed(); + if (singular) { + return 0; + } else { + final int m = pivot.length; + double determinant = parity; + for (int i = 0; i < m; i++) { + determinant *= lu[i][i]; + } + return determinant; + } + } + + /** {@inheritDoc} */ + public double[] solve(double[] b) + throws IllegalStateException, IllegalArgumentException, InvalidMatrixException { + + checkDecomposed(); + final int m = pivot.length; + if (b.length != m) { + throw new IllegalArgumentException("constant vector has wrong length"); + } + if (singular) { + throw new SingularMatrixException(); + } + + final double[] bp = new double[m]; + + // Apply permutations to b + for (int row = 0; row < m; row++) { + bp[row] = b[pivot[row]]; + } + + // Solve LY = b + for (int col = 0; col < m; col++) { + for (int i = col + 1; i < m; i++) { + bp[i] -= bp[col] * lu[i][col]; + } + } + + // Solve UX = Y + for (int col = m - 1; col >= 0; col--) { + bp[col] /= lu[col][col]; + for (int i = 0; i < col; i++) { + bp[i] -= bp[col] * lu[i][col]; + } + } + + return bp; + + } + + /** {@inheritDoc} */ + public RealVector solve(RealVector b) + throws IllegalStateException, IllegalArgumentException, InvalidMatrixException { + try { + return solve((RealVectorImpl) b); + } catch (ClassCastException cce) { + + checkDecomposed(); + final int m = pivot.length; + if (b.getDimension() != m) { + throw new IllegalArgumentException("constant vector has wrong length"); + } + if (singular) { + throw new SingularMatrixException(); + } + + final double[] bp = new double[m]; + + // Apply permutations to b + for (int row = 0; row < m; row++) { + bp[row] = b.getEntry(pivot[row]); + } + + // Solve LY = b + for (int col = 0; col < m; col++) { + for (int i = col + 1; i < m; i++) { + bp[i] -= bp[col] * lu[i][col]; + } + } + + // Solve UX = Y + for (int col = m - 1; col >= 0; col--) { + bp[col] /= lu[col][col]; + for (int i = 0; i < col; i++) { + bp[i] -= bp[col] * lu[i][col]; + } + } + + return new RealVectorImpl(bp, false); + + } + } + + /** Solve the linear equation A × X = B. + *

The A matrix is implicit here. It is

+ * @param b right-hand side of the equation A × X = B + * @return a vector X such that A × X = B + * @exception IllegalStateException if {@link #decompose(RealMatrix) decompose} + * has not been called + * @exception IllegalArgumentException if matrices dimensions don't match + * @exception InvalidMatrixException if decomposed matrix is singular + */ + public RealVectorImpl solve(RealVectorImpl b) + throws IllegalStateException, IllegalArgumentException, InvalidMatrixException { + return new RealVectorImpl(solve(b.getDataRef()), false); + } + + /** {@inheritDoc} */ + public RealMatrix solve(RealMatrix b) + throws IllegalStateException, IllegalArgumentException, InvalidMatrixException { + + checkDecomposed(); + final int m = pivot.length; + if (b.getRowDimension() != m) { + throw new IllegalArgumentException("Incorrect row dimension"); + } + if (singular) { + throw new SingularMatrixException(); + } + + final int nColB = b.getColumnDimension(); + + // Apply permutations to b + final double[][] bp = new double[m][nColB]; + for (int row = 0; row < m; row++) { + final double[] bpRow = bp[row]; + final int pRow = pivot[row]; + for (int col = 0; col < nColB; col++) { + bpRow[col] = b.getEntry(pRow, col); + } + } + + // Solve LY = b + for (int col = 0; col < m; col++) { + final double[] bpCol = bp[col]; + for (int i = col + 1; i < m; i++) { + final double[] bpI = bp[i]; + final double luICol = lu[i][col]; + for (int j = 0; j < nColB; j++) { + bpI[j] -= bpCol[j] * luICol; + } + } + } + + // Solve UX = Y + for (int col = m - 1; col >= 0; col--) { + final double[] bpCol = bp[col]; + final double luDiag = lu[col][col]; + for (int j = 0; j < nColB; j++) { + bpCol[j] /= luDiag; + } + for (int i = 0; i < col; i++) { + final double[] bpI = bp[i]; + final double luICol = lu[i][col]; + for (int j = 0; j < nColB; j++) { + bpI[j] -= bpCol[j] * luICol; + } + } + } + + return new RealMatrixImpl(bp, false); + + } + + /** {@inheritDoc} */ + public RealMatrix getInverse() + throws IllegalStateException, InvalidMatrixException { + checkDecomposed(); + return solve(MatrixUtils.createRealIdentityMatrix(pivot.length)); + } + + /** + * Check if either {@link #decompose(RealMatrix)} or {@link + * #decompose(RealMatrix, double) has been called. + * @exception IllegalStateException if {@link #decompose(RealMatrix) decompose} + * has not been called + */ + private void checkDecomposed() + throws IllegalStateException { + if (lu == null) { + throw MathRuntimeException.createIllegalStateException("no matrix have been decomposed yet", null); + } + } + +} diff --git a/src/java/org/apache/commons/math/linear/MatrixIndexException.java b/src/java/org/apache/commons/math/linear/MatrixIndexException.java index 85518d0b1..b951f7f84 100644 --- a/src/java/org/apache/commons/math/linear/MatrixIndexException.java +++ b/src/java/org/apache/commons/math/linear/MatrixIndexException.java @@ -17,30 +17,25 @@ package org.apache.commons.math.linear; +import org.apache.commons.math.MathRuntimeException; + /** - * Thrown when an operation addresses a matrix coordinate (row,col) + * Thrown when an operation addresses a matrix coordinate (row, col) * which is outside of the dimensions of a matrix. * @version $Revision$ $Date$ */ -public class MatrixIndexException extends RuntimeException { +public class MatrixIndexException extends MathRuntimeException { /** Serializable version identifier */ - private static final long serialVersionUID = -1341109412864309526L; + private static final long serialVersionUID = -2382324504109300625L; /** - * Default constructor. - * @deprecated as of 1.2 replaced by #MatrixIndexException(String) + * Constructs a new instance with specified formatted detail message. + * @param pattern format specifier + * @param arguments format arguments */ - public MatrixIndexException() { - this(null); - } - - /** - * Construct an exception with the given message and root cause. - * @param message descriptive error message. - */ - public MatrixIndexException(String message) { - super(message); + public MatrixIndexException(final String pattern, final Object[] arguments) { + super(pattern, arguments); } } diff --git a/src/java/org/apache/commons/math/linear/MatrixUtils.java b/src/java/org/apache/commons/math/linear/MatrixUtils.java index 6f08dd0a1..94296fa3e 100644 --- a/src/java/org/apache/commons/math/linear/MatrixUtils.java +++ b/src/java/org/apache/commons/math/linear/MatrixUtils.java @@ -18,6 +18,7 @@ package org.apache.commons.math.linear; import java.math.BigDecimal; +import java.util.Arrays; /** * A collection of static methods that operate on or return matrices. @@ -41,12 +42,33 @@ public class MatrixUtils { * @return RealMatrix containing the values of the array * @throws IllegalArgumentException if data is not rectangular * (not all rows have the same length) or empty - * @throws NullPointerException if data is null + * @throws NullPointerException if data is null + * @see #createRealMatrix(double[][], boolean) */ public static RealMatrix createRealMatrix(double[][] data) { return new RealMatrixImpl(data); } - + + /** + * Returns a {@link RealMatrix} whose entries are the the values in the + * the input array. + *

If an array is built specially in order to be embedded in a + * RealMatrix and not used directly, the copyArray may be + * set to false + * @param data data for new matrix + * @param copyArray if true, the input array will be copied, otherwise + * it will be referenced + * @return RealMatrix containing the values of the array + * @throws IllegalArgumentException if data is not rectangular + * (not all rows have the same length) or empty + * @throws NullPointerException if data is null + * @see #createRealMatrix(double[][]) + */ + public static RealMatrix createRealMatrix(double[][] data, boolean copyArray) { + return new RealMatrixImpl(data, copyArray); + } + /** * Returns dimension x dimension identity matrix. * @@ -56,14 +78,11 @@ public class MatrixUtils { * @since 1.1 */ public static RealMatrix createRealIdentityMatrix(int dimension) { - RealMatrixImpl out = new RealMatrixImpl(dimension, dimension); - double[][] d = out.getDataRef(); + double[][] d = new double[dimension][dimension]; for (int row = 0; row < dimension; row++) { - for (int col = 0; col < dimension; col++) { - d[row][col] = row == col ? 1d : 0d; - } + d[row][row] = 1d; } - return out; + return new RealMatrixImpl(d, false); } /** @@ -93,7 +112,27 @@ public class MatrixUtils { public static BigMatrix createBigMatrix(BigDecimal[][] data) { return new BigMatrixImpl(data); } - + + /** + * Returns a {@link BigMatrix} whose entries are the the values in the + * the input array. + *

If an array is built specially in order to be embedded in a + * BigMatrix and not used directly, the copyArray may be + * set to false + * @param data data for new matrix + * @param copyArray if true, the input array will be copied, otherwise + * it will be referenced + * @return BigMatrix containing the values of the array + * @throws IllegalArgumentException if data is not rectangular + * (not all rows have the same length) or empty + * @throws NullPointerException if data is null + * @see #createRealMatrix(double[][]) + */ + public static BigMatrix createBigMatrix(BigDecimal[][] data, boolean copyArray) { + return new BigMatrixImpl(data, copyArray); + } + /** * Returns a {@link BigMatrix} whose entries are the the values in the * the input array. The input array is copied, not referenced. @@ -108,6 +147,18 @@ public class MatrixUtils { return new BigMatrixImpl(data); } + /** + * Creates a {@link RealVector} using the data from the input array. + * + * @param data the input data + * @return a data.length RealVector + * @throws IllegalArgumentException if data is empty + * @throws NullPointerException if datais null + */ + public static RealVector createRealVector(double[] data) { + return new RealVectorImpl(data, true); + } + /** * Creates a row {@link RealMatrix} using the data from the input * array. @@ -118,10 +169,10 @@ public class MatrixUtils { * @throws NullPointerException if rowDatais null */ public static RealMatrix createRowRealMatrix(double[] rowData) { - int nCols = rowData.length; - double[][] data = new double[1][nCols]; + final int nCols = rowData.length; + final double[][] data = new double[1][nCols]; System.arraycopy(rowData, 0, data[0], 0, nCols); - return new RealMatrixImpl(data); + return new RealMatrixImpl(data, false); } /** @@ -134,10 +185,12 @@ public class MatrixUtils { * @throws NullPointerException if rowDatais null */ public static BigMatrix createRowBigMatrix(double[] rowData) { - int nCols = rowData.length; - double[][] data = new double[1][nCols]; - System.arraycopy(rowData, 0, data[0], 0, nCols); - return new BigMatrixImpl(data); + final int nCols = rowData.length; + final BigDecimal[][] data = new BigDecimal[1][nCols]; + for (int i = 0; i < nCols; ++i) { + data[0][i] = new BigDecimal(rowData[i]); + } + return new BigMatrixImpl(data, false); } /** @@ -150,10 +203,10 @@ public class MatrixUtils { * @throws NullPointerException if rowDatais null */ public static BigMatrix createRowBigMatrix(BigDecimal[] rowData) { - int nCols = rowData.length; - BigDecimal[][] data = new BigDecimal[1][nCols]; + final int nCols = rowData.length; + final BigDecimal[][] data = new BigDecimal[1][nCols]; System.arraycopy(rowData, 0, data[0], 0, nCols); - return new BigMatrixImpl(data); + return new BigMatrixImpl(data, false); } /** @@ -166,10 +219,12 @@ public class MatrixUtils { * @throws NullPointerException if rowDatais null */ public static BigMatrix createRowBigMatrix(String[] rowData) { - int nCols = rowData.length; - String[][] data = new String[1][nCols]; - System.arraycopy(rowData, 0, data[0], 0, nCols); - return new BigMatrixImpl(data); + final int nCols = rowData.length; + final BigDecimal[][] data = new BigDecimal[1][nCols]; + for (int i = 0; i < nCols; ++i) { + data[0][i] = new BigDecimal(rowData[i]); + } + return new BigMatrixImpl(data, false); } /** @@ -182,12 +237,12 @@ public class MatrixUtils { * @throws NullPointerException if columnDatais null */ public static RealMatrix createColumnRealMatrix(double[] columnData) { - int nRows = columnData.length; - double[][] data = new double[nRows][1]; + final int nRows = columnData.length; + final double[][] data = new double[nRows][1]; for (int row = 0; row < nRows; row++) { data[row][0] = columnData[row]; } - return new RealMatrixImpl(data); + return new RealMatrixImpl(data, false); } /** @@ -200,12 +255,12 @@ public class MatrixUtils { * @throws NullPointerException if columnDatais null */ public static BigMatrix createColumnBigMatrix(double[] columnData) { - int nRows = columnData.length; - double[][] data = new double[nRows][1]; + final int nRows = columnData.length; + final BigDecimal[][] data = new BigDecimal[nRows][1]; for (int row = 0; row < nRows; row++) { - data[row][0] = columnData[row]; + data[row][0] = new BigDecimal(columnData[row]); } - return new BigMatrixImpl(data); + return new BigMatrixImpl(data, false); } /** @@ -218,12 +273,12 @@ public class MatrixUtils { * @throws NullPointerException if columnDatais null */ public static BigMatrix createColumnBigMatrix(BigDecimal[] columnData) { - int nRows = columnData.length; - BigDecimal[][] data = new BigDecimal[nRows][1]; + final int nRows = columnData.length; + final BigDecimal[][] data = new BigDecimal[nRows][1]; for (int row = 0; row < nRows; row++) { data[row][0] = columnData[row]; } - return new BigMatrixImpl(data); + return new BigMatrixImpl(data, false); } /** @@ -237,11 +292,11 @@ public class MatrixUtils { */ public static BigMatrix createColumnBigMatrix(String[] columnData) { int nRows = columnData.length; - String[][] data = new String[nRows][1]; + final BigDecimal[][] data = new BigDecimal[nRows][1]; for (int row = 0; row < nRows; row++) { - data[row][0] = columnData[row]; + data[row][0] = new BigDecimal(columnData[row]); } - return new BigMatrixImpl(data); + return new BigMatrixImpl(data, false); } /** @@ -253,14 +308,13 @@ public class MatrixUtils { * @since 1.1 */ public static BigMatrix createBigIdentityMatrix(int dimension) { - BigMatrixImpl out = new BigMatrixImpl(dimension, dimension); - BigDecimal[][] d = out.getDataRef(); + final BigDecimal[][] d = new BigDecimal[dimension][dimension]; for (int row = 0; row < dimension; row++) { - for (int col = 0; col < dimension; col++) { - d[row][col] = row == col ? BigMatrixImpl.ONE : BigMatrixImpl.ZERO; - } + final BigDecimal[] dRow = d[row]; + Arrays.fill(dRow, BigMatrixImpl.ZERO); + dRow[row] = BigMatrixImpl.ONE; } - return out; + return new BigMatrixImpl(d, false); } } diff --git a/src/java/org/apache/commons/math/stat/inference/TestFactoryImpl.java b/src/java/org/apache/commons/math/linear/NonSquareMatrixException.java similarity index 52% rename from src/java/org/apache/commons/math/stat/inference/TestFactoryImpl.java rename to src/java/org/apache/commons/math/linear/NonSquareMatrixException.java index 15a56c512..95778828f 100644 --- a/src/java/org/apache/commons/math/stat/inference/TestFactoryImpl.java +++ b/src/java/org/apache/commons/math/linear/NonSquareMatrixException.java @@ -14,42 +14,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.commons.math.stat.inference; + +package org.apache.commons.math.linear; /** - * A concrete inference test factory. This is the default factory used by - * Commons-Math. - * - * @deprecated as of 1.2, pluggability of test instances is now provided through - * constructors and setters. - * @since 1.1 + * Thrown when an operation defined only for square matrices is applied to non-square ones. * @version $Revision$ $Date$ + * @since 2.0 */ -public class TestFactoryImpl extends TestFactory { +public class NonSquareMatrixException extends InvalidMatrixException { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 8996207526636673730L; /** - * Default constructor. + * Construct an exception with the given message. + * @param rows number of rows of the faulty matrix + * @param columns number of columns of the faulty matrix */ - public TestFactoryImpl() { - super(); + public NonSquareMatrixException(final int rows, final int columns) { + super("a {0}x{1} matrix was provided instead of a square matrix", + new Object[] { rows, columns }); } - - /** - * Create a TTest instance. - * - * @return a new TTest instance - */ - public TTest createTTest() { - return new TTestImpl(); - } - - /** - * Create a ChiSquareTest instance. - * - * @return a new ChiSquareTest instance - */ - public ChiSquareTest createChiSquareTest() { - return new ChiSquareTestImpl(); - } - + } diff --git a/src/java/org/apache/commons/math/linear/QRDecomposition.java b/src/java/org/apache/commons/math/linear/QRDecomposition.java index 72b9b3e4f..fa12978e7 100644 --- a/src/java/org/apache/commons/math/linear/QRDecomposition.java +++ b/src/java/org/apache/commons/math/linear/QRDecomposition.java @@ -18,27 +18,65 @@ package org.apache.commons.math.linear; /** - * An interface to classes that implement a algorithm to calculate the + * An interface to classes that implement an algorithm to calculate the * QR-decomposition of a real matrix. + *

This interface is based on the class with similar name from the now defunct + * JAMA library, with the + * following changes:

+ *
    + *
  • several signatures have been added for the solve methods + * (in the superinterface),
  • + *
  • a {@link DecompositionSolver#decompose(RealMatrix) decompose(RealMatrix)} + * method has been added (in the superinterface),
  • + *
  • a {@link DecompositionSolver#getInverse() getInverse} method has been + * added (in the superinterface),
  • + *
  • the isFullRank method has been replaced by the {@link + * DecompositionSolver#isNonSingular() isNonSingular} method in the superinterface.
  • + *
* * @see MathWorld * @see Wikipedia * @version $Revision$ $Date$ * @since 1.2 */ -public interface QRDecomposition { +public interface QRDecomposition extends DecompositionSolver { /** * Returns the matrix R of the decomposition. - * + *

R is an upper-triangular matrix

* @return the R matrix + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called */ - public abstract RealMatrix getR(); + RealMatrix getR() throws IllegalStateException; /** * Returns the matrix Q of the decomposition. - * + *

Q is an orthogonal matrix

* @return the Q matrix + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called */ - public abstract RealMatrix getQ(); + RealMatrix getQ() throws IllegalStateException; + + /** + * Returns the transpose of the matrix Q of the decomposition. + *

Q is an orthogonal matrix

+ * @return the Q matrix + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + */ + RealMatrix getQT() throws IllegalStateException; + + /** + * Returns the Householder reflector vectors. + *

H is a lower trapezoidal matrix whose columns represent + * each successive Householder reflector vector. This matrix is used + * to compute Q.

+ * @return a matrix containing the Householder reflector vectors + * @exception IllegalStateException if {@link + * DecompositionSolver#decompose(RealMatrix) decompose} has not been called + */ + RealMatrix getH() throws IllegalStateException; + } diff --git a/src/java/org/apache/commons/math/linear/QRDecompositionImpl.java b/src/java/org/apache/commons/math/linear/QRDecompositionImpl.java index 5f9c05f3e..07d2f9aca 100644 --- a/src/java/org/apache/commons/math/linear/QRDecompositionImpl.java +++ b/src/java/org/apache/commons/math/linear/QRDecompositionImpl.java @@ -17,56 +17,85 @@ package org.apache.commons.math.linear; +import org.apache.commons.math.MathRuntimeException; + /** - * Calculates the QR-decomposition of a matrix. In the QR-decomposition of - * a matrix A consists of two matrices Q and R that satisfy: A = QR, Q is - * orthogonal (QTQ = I), and R is upper triangular. If A is - * m×n, Q is m×m and R m×n. - *

- * Implemented using Householder reflectors.

+ * Calculates the QR-decomposition of a matrix. + *

The QR-decomposition of a matrix A consists of two matrices Q and R + * that satisfy: A = QR, Q is orthogonal (QTQ = I), and R is + * upper triangular. If A is m×n, Q is m×m and R m×n.

+ *

This class compute the decomposition using Householder reflectors.

+ *

For efficiency purposes, the decomposition in packed form is transposed. + * This allows inner loop to iterate inside rows, which is much more cache-efficient + * in Java.

* * @see MathWorld * @see Wikipedia - * + * * @version $Revision$ $Date$ * @since 1.2 */ public class QRDecompositionImpl implements QRDecomposition { - /** - * A packed representation of the QR decomposition. The elements above the - * diagonal are the elements of R, and the columns of the lower triangle - * are the Householder reflector vectors of which an explicit form of Q can - * be calculated. - */ - private double[][] qr; + /** Serializable version identifier. */ + private static final long serialVersionUID = -5179446891802932307L; /** - * The diagonal elements of R. + * A packed TRANSPOSED representation of the QR decomposition. + *

The elements BELOW the diagonal are the elements of the UPPER triangular + * matrix R, and the rows ABOVE the diagonal are the Householder reflector vectors + * from which an explicit form of Q can be recomputed if desired.

*/ + private double[][] qrt; + + /** The diagonal elements of R. */ private double[] rDiag; - /** - * The row dimension of the given matrix. The size of Q will be m x m, the - * size of R will be m x n. - */ - private int m; + /** Cached value of Q. */ + private RealMatrix cachedQ; + + /** Cached value of QT. */ + private RealMatrix cachedQT; + + /** Cached value of R. */ + private RealMatrix cachedR; + + /** Cached value of H. */ + private RealMatrix cachedH; /** - * The column dimension of the given matrix. The size of R will be m x n. + * Build a new instance. + *

Note that {@link #decompose(RealMatrix)} must be called + * before any of the {@link #getQ()}, {@link #getR()}, {@link #getH()}, + * {@link #isFullRank()}, {@link #solve(double[])}, {@link #solve(RealMatrix)}, + * {@link #solve(RealVector)} or {@link #solve(RealVectorImpl)} methods can be + * called.

+ * @see #decompose(RealMatrix) */ - private int n; + public QRDecompositionImpl() { + } /** - * Calculates the QR decomposition of the given matrix. - * + * Calculates the QR-decomposition of the given matrix. + *

Calling this constructor is equivalent to first call the no-arguments + * constructor and then call {@link #decompose(RealMatrix)}.

* @param matrix The matrix to decompose. */ public QRDecompositionImpl(RealMatrix matrix) { - m = matrix.getRowDimension(); - n = matrix.getColumnDimension(); - qr = matrix.getData(); + decompose(matrix); + } + + /** {@inheritDoc} */ + public void decompose(RealMatrix matrix) { + + final int m = matrix.getRowDimension(); + final int n = matrix.getColumnDimension(); + qrt = matrix.transpose().getData(); rDiag = new double[n]; + cachedQ = null; + cachedQT = null; + cachedR = null; + cachedH = null; /* * The QR decomposition of a matrix A is calculated using Householder @@ -74,6 +103,9 @@ public class QRDecompositionImpl implements QRDecomposition { * A(minor,minor) of A: */ for (int minor = 0; minor < Math.min(m, n); minor++) { + + final double[] qrtMinor = qrt[minor]; + /* * Let x be the first column of the minor, and a^2 = |x|^2. * x will be in the positions qr[minor][minor] through qr[m][minor]. @@ -83,10 +115,10 @@ public class QRDecompositionImpl implements QRDecomposition { */ double xNormSqr = 0; for (int row = minor; row < m; row++) { - xNormSqr += qr[row][minor]*qr[row][minor]; + final double c = qrtMinor[row]; + xNormSqr += c * c; } - double a = Math.sqrt(xNormSqr); - if (qr[minor][minor] > 0) a = -a; + final double a = (qrtMinor[minor] > 0) ? -Math.sqrt(xNormSqr) : Math.sqrt(xNormSqr); rDiag[minor] = a; if (a != 0.0) { @@ -99,7 +131,7 @@ public class QRDecompositionImpl implements QRDecomposition { * Here is now qr[minor][minor]. * v = x-ae is stored in the column at qr: */ - qr[minor][minor] -= a; // now |v|^2 = -2a*(qr[minor][minor]) + qrtMinor[minor] -= a; // now |v|^2 = -2a*(qr[minor][minor]) /* * Transform the rest of the columns of the minor: @@ -108,85 +140,313 @@ public class QRDecompositionImpl implements QRDecomposition { * Hx = (I-2vv'/|v|^2)x = x-2vv'x/|v|^2 = x - 2/|v|^2 v. * Therefore the transformation is easily calculated by * subtracting the column vector (2/|v|^2)v from x. - * + * * Let 2/|v|^2 = alpha. From above we have * |v|^2 = -2a*(qr[minor][minor]), so * alpha = -/(a*qr[minor][minor]) */ for (int col = minor+1; col < n; col++) { + final double[] qrtCol = qrt[col]; double alpha = 0; for (int row = minor; row < m; row++) { - alpha -= qr[row][col]*qr[row][minor]; + alpha -= qrtCol[row] * qrtMinor[row]; } - alpha /= a*qr[minor][minor]; + alpha /= a * qrtMinor[minor]; // Subtract the column vector alpha*v from x. for (int row = minor; row < m; row++) { - qr[row][col] -= alpha*qr[row][minor]; + qrtCol[row] -= alpha * qrtMinor[row]; } } } } } - /** - * Returns the matrix R of the QR-decomposition. - * - * @return the R matrix - */ + /** {@inheritDoc} */ public RealMatrix getR() - { - // R is supposed to be m x n - RealMatrixImpl ret = new RealMatrixImpl(m,n); - double[][] r = ret.getDataRef(); + throws IllegalStateException { - // copy the diagonal from rDiag and the upper triangle of qr - for (int row = Math.min(m,n)-1; row >= 0; row--) { - r[row][row] = rDiag[row]; - for (int col = row+1; col < n; col++) { - r[row][col] = qr[row][col]; + if (cachedR == null) { + + checkDecomposed(); + + // R is supposed to be m x n + final int n = qrt.length; + final int m = qrt[0].length; + double[][] r = new double[m][n]; + + // copy the diagonal from rDiag and the upper triangle of qr + for (int row = Math.min(m, n) - 1; row >= 0; row--) { + double[] rRow = r[row]; + rRow[row] = rDiag[row]; + for (int col = row + 1; col < n; col++) { + rRow[col] = qrt[col][row]; + } } + + // cache the matrix for subsequent calls + cachedR = new RealMatrixImpl(r, false); + } - return ret; + + // return the cached matrix + return cachedR; + } - /** - * Returns the matrix Q of the QR-decomposition. - * - * @return the Q matrix - */ + /** {@inheritDoc} */ public RealMatrix getQ() - { - // Q is supposed to be m x m - RealMatrixImpl ret = new RealMatrixImpl(m,m); - double[][] Q = ret.getDataRef(); - - /* - * Q = Q1 Q2 ... Q_m, so Q is formed by first constructing Q_m and then - * applying the Householder transformations Q_(m-1),Q_(m-2),...,Q1 in - * succession to the result - */ - for (int minor = m-1; minor >= Math.min(m,n); minor--) { - Q[minor][minor]=1; + throws IllegalStateException { + if (cachedQ == null) { + cachedQ = getQT().transpose(); } + return cachedQ; + } - for (int minor = Math.min(m,n)-1; minor >= 0; minor--){ - Q[minor][minor] = 1; - if (qr[minor][minor] != 0.0) { - for (int col = minor; col < m; col++) { - double alpha = 0; - for (int row = minor; row < m; row++) { - alpha -= Q[row][col] * qr[row][minor]; - } - alpha /= rDiag[minor]*qr[minor][minor]; + /** {@inheritDoc} */ + public RealMatrix getQT() + throws IllegalStateException { - for (int row = minor; row < m; row++) { - Q[row][col] -= alpha*qr[row][minor]; + if (cachedQT == null) { + + checkDecomposed(); + + // QT is supposed to be m x m + final int n = qrt.length; + final int m = qrt[0].length; + double[][] qT = new double[m][m]; + + /* + * Q = Q1 Q2 ... Q_m, so Q is formed by first constructing Q_m and then + * applying the Householder transformations Q_(m-1),Q_(m-2),...,Q1 in + * succession to the result + */ + for (int minor = m - 1; minor >= Math.min(m, n); minor--) { + qT[minor][minor]=1; + } + + for (int minor = Math.min(m,n)-1; minor >= 0; minor--){ + final double[] qrtMinor = qrt[minor]; + qT[minor][minor] = 1; + if (qrtMinor[minor] != 0.0) { + for (int col = minor; col < m; col++) { + final double[] qTCol = qT[col]; + double alpha = 0; + for (int row = minor; row < m; row++) { + alpha -= qTCol[row] * qrtMinor[row]; + } + alpha /= rDiag[minor] * qrtMinor[minor]; + + for (int row = minor; row < m; row++) { + qTCol[row] -= alpha * qrtMinor[row]; + } } } } + + // cache the matrix for subsequent calls + cachedQT = new RealMatrixImpl(qT, false); + } - return ret; + // return the cached matrix + return cachedQT; + } + + /** {@inheritDoc} */ + public RealMatrix getH() + throws IllegalStateException { + + if (cachedH == null) { + + checkDecomposed(); + + final int n = qrt.length; + final int m = qrt[0].length; + double[][] hData = new double[m][n]; + for (int i = 0; i < m; ++i) { + final double[] hDataI = hData[i]; + for (int j = 0; j < Math.min(i + 1, n); ++j) { + hDataI[j] = qrt[j][i] / -rDiag[j]; + } + } + + // cache the matrix for subsequent calls + cachedH = new RealMatrixImpl(hData, false); + + } + + // return the cached matrix + return cachedH; + + } + + /** {@inheritDoc} */ + public boolean isNonSingular() + throws IllegalStateException { + + checkDecomposed(); + + for (double diag : rDiag) { + if (diag == 0) { + return false; + } + } + return true; + + } + + /** {@inheritDoc} */ + public double[] solve(double[] b) + throws IllegalStateException, IllegalArgumentException, InvalidMatrixException { + + checkDecomposed(); + + final int n = qrt.length; + final int m = qrt[0].length; + if (b.length != m) { + throw new IllegalArgumentException("Incorrect row dimension"); + } + if (!isNonSingular()) { + throw new RankDeficientMatrixException(); + } + + final double[] x = new double[n]; + final double[] y = b.clone(); + + // apply Householder transforms to solve Q.y = b + for (int minor = 0; minor < Math.min(m, n); minor++) { + + final double[] qrtMinor = qrt[minor]; + double dotProduct = 0; + for (int row = minor; row < m; row++) { + dotProduct += y[row] * qrtMinor[row]; + } + dotProduct /= rDiag[minor] * qrtMinor[minor]; + + for (int row = minor; row < m; row++) { + y[row] += dotProduct * qrtMinor[row]; + } + + } + + // solve triangular system R.x = y + for (int row = n - 1; row >= 0; --row) { + y[row] /= rDiag[row]; + final double yRow = y[row]; + final double[] qrtRow = qrt[row]; + x[row] = yRow; + for (int i = 0; i < row; i++) { + y[i] -= yRow * qrtRow[i]; + } + } + + return x; + + } + + /** {@inheritDoc} */ + public RealVector solve(RealVector b) + throws IllegalStateException, IllegalArgumentException, InvalidMatrixException { + try { + return solve((RealVectorImpl) b); + } catch (ClassCastException cce) { + checkDecomposed(); + return new RealVectorImpl(solve(b.getData()), false); + } + } + + /** Solve the linear equation A × X = B. + *

The A matrix is implicit here. It is

+ * @param b right-hand side of the equation A × X = B + * @return a vector X that minimizes the two norm of A × X - B + * @exception IllegalStateException if {@link #decompose(RealMatrix) decompose} + * has not been called + * @throws IllegalArgumentException if matrices dimensions don't match + * @throws InvalidMatrixException if decomposed matrix is singular + */ + public RealVectorImpl solve(RealVectorImpl b) + throws IllegalStateException, IllegalArgumentException, InvalidMatrixException { + return new RealVectorImpl(solve(b.getDataRef()), false); + } + + /** {@inheritDoc} */ + public RealMatrix solve(RealMatrix b) + throws IllegalStateException, IllegalArgumentException, InvalidMatrixException { + + checkDecomposed(); + + final int n = qrt.length; + final int m = qrt[0].length; + if (b.getRowDimension() != m) { + throw new IllegalArgumentException("Incorrect row dimension"); + } + if (!isNonSingular()) { + throw new RankDeficientMatrixException(); + } + + final int cols = b.getColumnDimension(); + final double[][] xData = new double[n][cols]; + final double[] y = new double[b.getRowDimension()]; + + for (int k = 0; k < cols; ++k) { + + // get the right hand side vector + for (int j = 0; j < y.length; ++j) { + y[j] = b.getEntry(j, k); + } + + // apply Householder transforms to solve Q.y = b + for (int minor = 0; minor < Math.min(m, n); minor++) { + + final double[] qrtMinor = qrt[minor]; + double dotProduct = 0; + for (int row = minor; row < m; row++) { + dotProduct += y[row] * qrtMinor[row]; + } + dotProduct /= rDiag[minor] * qrtMinor[minor]; + + for (int row = minor; row < m; row++) { + y[row] += dotProduct * qrtMinor[row]; + } + + } + + // solve triangular system R.x = y + for (int row = n - 1; row >= 0; --row) { + y[row] /= rDiag[row]; + final double yRow = y[row]; + final double[] qrtRow = qrt[row]; + xData[row][k] = yRow; + for (int i = 0; i < row; i++) { + y[i] -= yRow * qrtRow[i]; + } + } + + } + + return new RealMatrixImpl(xData, false); + + } + + /** {@inheritDoc} */ + public RealMatrix getInverse() + throws IllegalStateException, InvalidMatrixException { + checkDecomposed(); + return solve(MatrixUtils.createRealIdentityMatrix(rDiag.length)); + } + + /** + * Check if {@link #decompose(RealMatrix)} has been called. + * @exception IllegalStateException if {@link #decompose(RealMatrix) decompose} + * has not been called + */ + private void checkDecomposed() + throws IllegalStateException { + if (qrt == null) { + throw MathRuntimeException.createIllegalStateException("no matrix have been decomposed yet", null); + } + } + } diff --git a/src/java/org/apache/commons/math/stat/descriptive/SummaryStatisticsImpl.java b/src/java/org/apache/commons/math/linear/RankDeficientMatrixException.java similarity index 56% rename from src/java/org/apache/commons/math/stat/descriptive/SummaryStatisticsImpl.java rename to src/java/org/apache/commons/math/linear/RankDeficientMatrixException.java index 9d6b3101f..94f063eb7 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/SummaryStatisticsImpl.java +++ b/src/java/org/apache/commons/math/linear/RankDeficientMatrixException.java @@ -14,31 +14,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.commons.math.stat.descriptive; -import java.io.Serializable; +package org.apache.commons.math.linear; /** - * Provides a default {@link SummaryStatistics} implementation. - * - * @deprecated to be removed in commons math 2.0. Use {@link SummaryStatistics}. - * @version $Revision$ $Date$ + * Thrown when a matrix is singular. + * @version $Revision$ $Date$ + * @since 2.0 */ -public class SummaryStatisticsImpl extends SummaryStatistics implements Serializable { +public class RankDeficientMatrixException extends InvalidMatrixException { - /** Serializable version identifier */ - private static final long serialVersionUID = 8528794411480425963L; + /** Serializable version identifier. */ + private static final long serialVersionUID = 7051890448195709736L; /** - * Construct a SummaryStatistics + * Construct an exception with a default message. */ - public SummaryStatisticsImpl() { - super(); + public RankDeficientMatrixException() { + super("matrix is rank-deficient", null); } - /** Resets all statistics and storage. */ - public void clear() { - super.clear(); - } - -} \ No newline at end of file +} diff --git a/src/java/org/apache/commons/math/linear/RealMatrix.java b/src/java/org/apache/commons/math/linear/RealMatrix.java index 4c23d0ed1..b74270219 100644 --- a/src/java/org/apache/commons/math/linear/RealMatrix.java +++ b/src/java/org/apache/commons/math/linear/RealMatrix.java @@ -148,6 +148,26 @@ public interface RealMatrix { * @throws MatrixIndexException if the specified column index is invalid */ RealMatrix getColumnMatrix(int column) throws MatrixIndexException; + + /** + * Returns the entries in row number row + * as a vector. Row indices start at 0. + * + * @param row the row to be fetched + * @return row vector + * @throws MatrixIndexException if the specified row index is invalid + */ + RealVector getRowVector(int row) throws MatrixIndexException; + + /** + * Returns the entries in column number column + * as a column vector. Column indices start at 0. + * + * @param column the column to be fetched + * @return column vector + * @throws MatrixIndexException if the specified column index is invalid + */ + RealVector getColumnVector(int column) throws MatrixIndexException; /** * Returns the entries in row number row as an array. @@ -202,14 +222,18 @@ public interface RealMatrix { * * @return inverse matrix * @throws InvalidMatrixException if this is not invertible + * @deprecated as of release 2.0, replaced by {@link DecompositionSolver#getInverse()} */ + @Deprecated RealMatrix inverse() throws InvalidMatrixException; /** * Returns the determinant of this matrix. * * @return determinant + * @deprecated as of release 2.0, replaced by {@link LUDecomposition#getDeterminant()} */ + @Deprecated double getDeterminant(); /** @@ -221,7 +245,10 @@ public interface RealMatrix { /** * Is this a singular matrix? * @return true if the matrix is singular + * @deprecated as of release 2.0, replaced by the boolean negation of + * {@link DecompositionSolver#isNonSingular()} */ + @Deprecated boolean isSingular(); /** @@ -255,6 +282,15 @@ public interface RealMatrix { */ double[] operate(double[] v) throws IllegalArgumentException; + /** + * Returns the result of multiplying this by the vector v. + * + * @param v the vector to operate on + * @return this*v + * @throws IllegalArgumentException if columnDimension != v.size() + */ + RealVector operate(RealVector v) throws IllegalArgumentException; + /** * Returns the (row) vector result of premultiplying this by the vector v. * @@ -264,6 +300,15 @@ public interface RealMatrix { */ double[] preMultiply(double[] v) throws IllegalArgumentException; + /** + * Returns the (row) vector result of premultiplying this by the vector v. + * + * @param v the row vector to premultiply by + * @return v*this + * @throws IllegalArgumentException if rowDimension != v.size() + */ + RealVector preMultiply(RealVector v) throws IllegalArgumentException; + /** * Returns the solution vector for a linear system with coefficient * matrix = this and constant vector = b. @@ -272,7 +317,9 @@ public interface RealMatrix { * @return vector of solution values to AX = b, where A is *this * @throws IllegalArgumentException if this.rowDimension != b.length * @throws InvalidMatrixException if this matrix is not square or is singular + * @deprecated as of release 2.0, replaced by {@link DecompositionSolver#solve(double[])} */ + @Deprecated double[] solve(double[] b) throws IllegalArgumentException, InvalidMatrixException; /** @@ -285,7 +332,9 @@ public interface RealMatrix { * @return matrix of solution vectors * @throws IllegalArgumentException if this.rowDimension != row dimension * @throws InvalidMatrixException if this matrix is not square or is singular + * @deprecated as of release 2.0, replaced by {@link DecompositionSolver#solve(RealMatrix)} */ + @Deprecated RealMatrix solve(RealMatrix b) throws IllegalArgumentException, InvalidMatrixException; -} +} diff --git a/src/java/org/apache/commons/math/linear/RealMatrixImpl.java b/src/java/org/apache/commons/math/linear/RealMatrixImpl.java index 5a4cc3181..6822884f7 100644 --- a/src/java/org/apache/commons/math/linear/RealMatrixImpl.java +++ b/src/java/org/apache/commons/math/linear/RealMatrixImpl.java @@ -18,6 +18,8 @@ package org.apache.commons.math.linear; import java.io.Serializable; + +import org.apache.commons.math.MathRuntimeException; import org.apache.commons.math.util.MathUtils; @@ -52,24 +54,15 @@ import org.apache.commons.math.util.MathUtils; public class RealMatrixImpl implements RealMatrix, Serializable { /** Serializable version identifier */ - private static final long serialVersionUID = 4237564493130426188L; + private static final long serialVersionUID = 4970229902484487012L; /** Entries of the matrix */ - private double data[][] = null; + protected double data[][]; - /** Entries of cached LU decomposition. - * All updates to data (other than luDecompose()) *must* set this to null + /** Cached LU decomposition. + * @deprecated as of release 2.0, since all methods using this are deprecated */ - private double lu[][] = null; - - /** Permutation associated with LU decomposition */ - private int[] permutation = null; - - /** Parity of the permutation associated with the LU decomposition */ - private int parity = 1; - - /** Bound to determine effective singularity in LU decomposition */ - protected static double TOO_SMALL = 10E-12; + private LUDecomposition lu; /** * Creates a matrix with no data @@ -97,16 +90,58 @@ public class RealMatrixImpl implements RealMatrix, Serializable { /** * Create a new RealMatrix using the input array as the underlying * data array. - *

- * The input array is copied, not referenced.

+ *

The input array is copied, not referenced. This constructor has + * the same effect as calling {@link #RealMatrixImpl(double[][], boolean)} + * with the second argument set to true.

* * @param d data for new matrix * @throws IllegalArgumentException if d is not rectangular * (not all rows have the same length) or empty * @throws NullPointerException if d is null + * @see #RealMatrixImpl(double[][], boolean) */ public RealMatrixImpl(double[][] d) { - this.copyIn(d); + copyIn(d); + lu = null; + } + + /** + * Create a new RealMatrix using the input array as the underlying + * data array. + *

If an array is built specially in order to be embedded in a + * RealMatrix and not used directly, the copyArray may be + * set to false + * @param d data for new matrix + * @param copyArray if true, the input array will be copied, otherwise + * it will be referenced + * @throws IllegalArgumentException if d is not rectangular + * (not all rows have the same length) or empty + * @throws NullPointerException if d is null + * @see #RealMatrixImpl(double[][]) + */ + public RealMatrixImpl(double[][] d, boolean copyArray) { + if (copyArray) { + copyIn(d); + } else { + if (d == null) { + throw new NullPointerException(); + } + final int nRows = d.length; + if (nRows == 0) { + throw new IllegalArgumentException("Matrix must have at least one row."); + } + final int nCols = d[0].length; + if (nCols == 0) { + throw new IllegalArgumentException("Matrix must have at least one column."); + } + for (int r = 1; r < nRows; r++) { + if (d[r].length != nCols) { + throw new IllegalArgumentException("All input rows must have the same length."); + } + } + data = d; + } lu = null; } @@ -114,26 +149,43 @@ public class RealMatrixImpl implements RealMatrix, Serializable { * Create a new (column) RealMatrix using v as the * data for the unique column of the v.length x 1 matrix * created. - *

- * The input array is copied, not referenced.

+ *

The input array is copied, not referenced.

* * @param v column vector holding data for new matrix */ public RealMatrixImpl(double[] v) { - int nRows = v.length; + final int nRows = v.length; data = new double[nRows][1]; for (int row = 0; row < nRows; row++) { data[row][0] = v[row]; } } - /** - * Create a new RealMatrix which is a copy of this. - * - * @return the cloned matrix - */ + /** {@inheritDoc} */ public RealMatrix copy() { - return new RealMatrixImpl(this.copyOut()); + return new RealMatrixImpl(copyOut(), false); + } + + /** {@inheritDoc} */ + public RealMatrix add(RealMatrix m) throws IllegalArgumentException { + try { + return add((RealMatrixImpl) m); + } catch (ClassCastException cce) { + final int rowCount = getRowDimension(); + final int columnCount = getColumnDimension(); + if (columnCount != m.getColumnDimension() || rowCount != m.getRowDimension()) { + throw new IllegalArgumentException("matrix dimension mismatch"); + } + final double[][] outData = new double[rowCount][columnCount]; + for (int row = 0; row < rowCount; row++) { + final double[] dataRow = data[row]; + final double[] outDataRow = outData[row]; + for (int col = 0; col < columnCount; col++) { + outDataRow[col] = dataRow[col] + m.getEntry(row, col); + } + } + return new RealMatrixImpl(outData, false); + } } /** @@ -143,20 +195,44 @@ public class RealMatrixImpl implements RealMatrix, Serializable { * @return this + m * @throws IllegalArgumentException if m is not the same size as this */ - public RealMatrix add(RealMatrix m) throws IllegalArgumentException { - if (this.getColumnDimension() != m.getColumnDimension() || - this.getRowDimension() != m.getRowDimension()) { + public RealMatrixImpl add(RealMatrixImpl m) throws IllegalArgumentException { + final int rowCount = getRowDimension(); + final int columnCount = getColumnDimension(); + if (columnCount != m.getColumnDimension() || rowCount != m.getRowDimension()) { throw new IllegalArgumentException("matrix dimension mismatch"); } - int rowCount = this.getRowDimension(); - int columnCount = this.getColumnDimension(); - double[][] outData = new double[rowCount][columnCount]; + final double[][] outData = new double[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { + final double[] dataRow = data[row]; + final double[] mRow = m.data[row]; + final double[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { - outData[row][col] = data[row][col] + m.getEntry(row, col); + outDataRow[col] = dataRow[col] + mRow[col]; } } - return new RealMatrixImpl(outData); + return new RealMatrixImpl(outData, false); + } + + /** {@inheritDoc} */ + public RealMatrix subtract(RealMatrix m) throws IllegalArgumentException { + try { + return subtract((RealMatrixImpl) m); + } catch (ClassCastException cce) { + final int rowCount = getRowDimension(); + final int columnCount = getColumnDimension(); + if (columnCount != m.getColumnDimension() || rowCount != m.getRowDimension()) { + throw new IllegalArgumentException("matrix dimension mismatch"); + } + final double[][] outData = new double[rowCount][columnCount]; + for (int row = 0; row < rowCount; row++) { + final double[] dataRow = data[row]; + final double[] outDataRow = outData[row]; + for (int col = 0; col < columnCount; col++) { + outDataRow[col] = dataRow[col] - m.getEntry(row, col); + } + } + return new RealMatrixImpl(outData, false); + } } /** @@ -166,55 +242,79 @@ public class RealMatrixImpl implements RealMatrix, Serializable { * @return this + m * @throws IllegalArgumentException if m is not the same size as this */ - public RealMatrix subtract(RealMatrix m) throws IllegalArgumentException { - if (this.getColumnDimension() != m.getColumnDimension() || - this.getRowDimension() != m.getRowDimension()) { + public RealMatrixImpl subtract(RealMatrixImpl m) throws IllegalArgumentException { + final int rowCount = getRowDimension(); + final int columnCount = getColumnDimension(); + if (columnCount != m.getColumnDimension() || rowCount != m.getRowDimension()) { throw new IllegalArgumentException("matrix dimension mismatch"); } - int rowCount = this.getRowDimension(); - int columnCount = this.getColumnDimension(); - double[][] outData = new double[rowCount][columnCount]; + final double[][] outData = new double[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { + final double[] dataRow = data[row]; + final double[] mRow = m.data[row]; + final double[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { - outData[row][col] = data[row][col] - m.getEntry(row, col); - } + outDataRow[col] = dataRow[col] - mRow[col]; + } } - return new RealMatrixImpl(outData); + return new RealMatrixImpl(outData, false); } - /** - * Returns the result of adding d to each entry of this. - * - * @param d value to be added to each entry - * @return d + this - */ + /** {@inheritDoc} */ public RealMatrix scalarAdd(double d) { - int rowCount = this.getRowDimension(); - int columnCount = this.getColumnDimension(); - double[][] outData = new double[rowCount][columnCount]; + final int rowCount = getRowDimension(); + final int columnCount = getColumnDimension(); + final double[][] outData = new double[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { + final double[] dataRow = data[row]; + final double[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { - outData[row][col] = data[row][col] + d; + outDataRow[col] = dataRow[col] + d; } } - return new RealMatrixImpl(outData); + return new RealMatrixImpl(outData, false); } - /** - * Returns the result multiplying each entry of this by d - * @param d value to multiply all entries by - * @return d * this - */ + /** {@inheritDoc} */ public RealMatrix scalarMultiply(double d) { - int rowCount = this.getRowDimension(); - int columnCount = this.getColumnDimension(); - double[][] outData = new double[rowCount][columnCount]; + final int rowCount = getRowDimension(); + final int columnCount = getColumnDimension(); + final double[][] outData = new double[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { + final double[] dataRow = data[row]; + final double[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { - outData[row][col] = data[row][col] * d; + outDataRow[col] = dataRow[col] * d; } } - return new RealMatrixImpl(outData); + return new RealMatrixImpl(outData, false); + } + + /** {@inheritDoc} */ + public RealMatrix multiply(RealMatrix m) throws IllegalArgumentException { + try { + return multiply((RealMatrixImpl) m); + } catch (ClassCastException cce) { + if (this.getColumnDimension() != m.getRowDimension()) { + throw new IllegalArgumentException("Matrices are not multiplication compatible."); + } + final int nRows = this.getRowDimension(); + final int nCols = m.getColumnDimension(); + final int nSum = this.getColumnDimension(); + final double[][] outData = new double[nRows][nCols]; + for (int row = 0; row < nRows; row++) { + final double[] dataRow = data[row]; + final double[] outDataRow = outData[row]; + for (int col = 0; col < nCols; col++) { + double sum = 0; + for (int i = 0; i < nSum; i++) { + sum += dataRow[i] * m.getEntry(i, col); + } + outDataRow[col] = sum; + } + } + return new RealMatrixImpl(outData, false); + } } /** @@ -224,45 +324,34 @@ public class RealMatrixImpl implements RealMatrix, Serializable { * @throws IllegalArgumentException * if columnDimension(this) != rowDimension(m) */ - public RealMatrix multiply(RealMatrix m) throws IllegalArgumentException { + public RealMatrixImpl multiply(RealMatrixImpl m) throws IllegalArgumentException { if (this.getColumnDimension() != m.getRowDimension()) { throw new IllegalArgumentException("Matrices are not multiplication compatible."); } - int nRows = this.getRowDimension(); - int nCols = m.getColumnDimension(); - int nSum = this.getColumnDimension(); - double[][] outData = new double[nRows][nCols]; - double sum = 0; + final int nRows = this.getRowDimension(); + final int nCols = m.getColumnDimension(); + final int nSum = this.getColumnDimension(); + final double[][] outData = new double[nRows][nCols]; for (int row = 0; row < nRows; row++) { + final double[] dataRow = data[row]; + final double[] outDataRow = outData[row]; for (int col = 0; col < nCols; col++) { - sum = 0; + double sum = 0; for (int i = 0; i < nSum; i++) { - sum += data[row][i] * m.getEntry(i, col); + sum += dataRow[i] * m.data[i][col]; } - outData[row][col] = sum; + outDataRow[col] = sum; } - } - return new RealMatrixImpl(outData); + } + return new RealMatrixImpl(outData, false); } - /** - * Returns the result premultiplying this by m. - * @param m matrix to premultiply by - * @return m * this - * @throws IllegalArgumentException - * if rowDimension(this) != columnDimension(m) - */ + /** {@inheritDoc} */ public RealMatrix preMultiply(RealMatrix m) throws IllegalArgumentException { return m.multiply(this); } - /** - * Returns matrix entries as a two-dimensional array. - *

- * Makes a fresh copy of the underlying data.

- * - * @return 2-dimensional array of entries - */ + /** {@inheritDoc} */ public double[][] getData() { return copyOut(); } @@ -270,7 +359,7 @@ public class RealMatrixImpl implements RealMatrix, Serializable { /** * Returns a reference to the underlying data array. *

- * Does not make a fresh copy of the underlying data.

+ * Does not make a fresh copy of the underlying data.

* * @return 2-dimensional array of entries */ @@ -278,10 +367,7 @@ public class RealMatrixImpl implements RealMatrix, Serializable { return data; } - /** - * - * @return norm - */ + /** {@inheritDoc} */ public double getNorm() { double maxColSum = 0; for (int col = 0; col < this.getColumnDimension(); col++) { @@ -294,68 +380,67 @@ public class RealMatrixImpl implements RealMatrix, Serializable { return maxColSum; } - /** - * Gets a submatrix. Rows and columns are indicated - * counting from 0 to n-1. - * - * @param startRow Initial row index - * @param endRow Final row index - * @param startColumn Initial column index - * @param endColumn Final column index - * @return The subMatrix containing the data of the - * specified rows and columns - * @exception MatrixIndexException if row or column selections are not valid - */ - public RealMatrix getSubMatrix(int startRow, int endRow, int startColumn, - int endColumn) throws MatrixIndexException { - if (startRow < 0 || startRow > endRow || endRow > data.length || - startColumn < 0 || startColumn > endColumn || - endColumn > data[0].length ) { - throw new MatrixIndexException( - "invalid row or column index selection"); + /** {@inheritDoc} */ + public RealMatrix getSubMatrix(int startRow, int endRow, + int startColumn, int endColumn) + throws MatrixIndexException { + + checkRowIndex(startRow); + checkRowIndex(endRow); + if (startRow > endRow) { + throw new MatrixIndexException("initial row {0} after final row {1}", + new Object[] { startRow, endRow }); } - RealMatrixImpl subMatrix = new RealMatrixImpl(endRow - startRow+1, - endColumn - startColumn+1); - double[][] subMatrixData = subMatrix.getDataRef(); + + checkColumnIndex(startColumn); + checkColumnIndex(endColumn); + if (startColumn > endColumn) { + throw new MatrixIndexException("initial column {0} after final column {1}", + new Object[] { startColumn, endColumn }); + } + + final double[][] subMatrixData = + new double[endRow - startRow + 1][endColumn - startColumn + 1]; for (int i = startRow; i <= endRow; i++) { - for (int j = startColumn; j <= endColumn; j++) { - subMatrixData[i - startRow][j - startColumn] = data[i][j]; - } - } - return subMatrix; + System.arraycopy(data[i], startColumn, + subMatrixData[i - startRow], 0, + endColumn - startColumn + 1); + } + return new RealMatrixImpl(subMatrixData, false); } - /** - * Gets a submatrix. Rows and columns are indicated - * counting from 0 to n-1. - * - * @param selectedRows Array of row indices must be non-empty - * @param selectedColumns Array of column indices must be non-empty - * @return The subMatrix containing the data in the - * specified rows and columns - * @exception MatrixIndexException if supplied row or column index arrays - * are not valid - */ + /** {@inheritDoc} */ public RealMatrix getSubMatrix(int[] selectedRows, int[] selectedColumns) - throws MatrixIndexException { + throws MatrixIndexException { + if (selectedRows.length * selectedColumns.length == 0) { - throw new MatrixIndexException( - "selected row and column index arrays must be non-empty"); + if (selectedRows.length == 0) { + throw new MatrixIndexException("empty selected row index array", null); + } + throw new MatrixIndexException("empty selected column index array", null); } - RealMatrixImpl subMatrix = new RealMatrixImpl(selectedRows.length, - selectedColumns.length); - double[][] subMatrixData = subMatrix.getDataRef(); + + final double[][] subMatrixData = + new double[selectedRows.length][selectedColumns.length]; try { for (int i = 0; i < selectedRows.length; i++) { + final double[] subI = subMatrixData[i]; + final double[] dataSelectedI = data[selectedRows[i]]; for (int j = 0; j < selectedColumns.length; j++) { - subMatrixData[i][j] = data[selectedRows[i]][selectedColumns[j]]; + subI[j] = dataSelectedI[selectedColumns[j]]; } } + } catch (ArrayIndexOutOfBoundsException e) { + // we redo the loop with checks enabled + // in order to generate an appropriate message + for (final int row : selectedRows) { + checkRowIndex(row); + } + for (final int column : selectedColumns) { + checkColumnIndex(column); + } } - catch (ArrayIndexOutOfBoundsException e) { - throw new MatrixIndexException("matrix dimension mismatch"); - } - return subMatrix; + return new RealMatrixImpl(subMatrixData, false); } /** @@ -387,234 +472,169 @@ public class RealMatrixImpl implements RealMatrix, Serializable { */ public void setSubMatrix(double[][] subMatrix, int row, int column) throws MatrixIndexException { - if ((row < 0) || (column < 0)){ - throw new MatrixIndexException - ("invalid row or column index selection"); - } - int nRows = subMatrix.length; + + final int nRows = subMatrix.length; if (nRows == 0) { - throw new IllegalArgumentException( - "Matrix must have at least one row."); + throw new IllegalArgumentException("Matrix must have at least one row."); } - int nCols = subMatrix[0].length; + + final int nCols = subMatrix[0].length; if (nCols == 0) { - throw new IllegalArgumentException( - "Matrix must have at least one column."); + throw new IllegalArgumentException("Matrix must have at least one column."); } + for (int r = 1; r < nRows; r++) { if (subMatrix[r].length != nCols) { - throw new IllegalArgumentException( - "All input rows must have the same length."); + throw new IllegalArgumentException("All input rows must have the same length."); } - } + } + if (data == null) { - if ((row > 0)||(column > 0)) throw new MatrixIndexException - ("matrix must be initialized to perfom this method"); + if (row > 0) { + throw MathRuntimeException.createIllegalStateException("first {0} rows are not initialized yet", + new Object[] { row }); + } + if (column > 0) { + throw MathRuntimeException.createIllegalStateException("first {0} columns are not initialized yet", + new Object[] { column }); + } data = new double[nRows][nCols]; System.arraycopy(subMatrix, 0, data, 0, subMatrix.length); - } - if (((nRows + row) > this.getRowDimension()) || - (nCols + column > this.getColumnDimension())) - throw new MatrixIndexException( - "invalid row or column index selection"); + } else { + checkRowIndex(row); + checkColumnIndex(column); + checkRowIndex(nRows + row - 1); + checkColumnIndex(nCols + column - 1); + } + for (int i = 0; i < nRows; i++) { System.arraycopy(subMatrix[i], 0, data[row + i], column, nCols); } + lu = null; + } - - /** - * Returns the entries in row number row as a row matrix. - * Row indices start at 0. - * - * @param row the row to be fetched - * @return row matrix - * @throws MatrixIndexException if the specified row index is invalid - */ + + /** {@inheritDoc} */ public RealMatrix getRowMatrix(int row) throws MatrixIndexException { - if ( !isValidCoordinate( row, 0)) { - throw new MatrixIndexException("illegal row argument"); - } - int ncols = this.getColumnDimension(); - double[][] out = new double[1][ncols]; + checkRowIndex(row); + final int ncols = this.getColumnDimension(); + final double[][] out = new double[1][ncols]; System.arraycopy(data[row], 0, out[0], 0, ncols); - return new RealMatrixImpl(out); + return new RealMatrixImpl(out, false); } - /** - * Returns the entries in column number column - * as a column matrix. Column indices start at 0. - * - * @param column the column to be fetched - * @return column matrix - * @throws MatrixIndexException if the specified column index is invalid - */ + /** {@inheritDoc} */ public RealMatrix getColumnMatrix(int column) throws MatrixIndexException { - if ( !isValidCoordinate( 0, column)) { - throw new MatrixIndexException("illegal column argument"); - } - int nRows = this.getRowDimension(); - double[][] out = new double[nRows][1]; + checkColumnIndex(column); + final int nRows = this.getRowDimension(); + final double[][] out = new double[nRows][1]; for (int row = 0; row < nRows; row++) { out[row][0] = data[row][column]; } - return new RealMatrixImpl(out); + return new RealMatrixImpl(out, false); } - /** - * Returns the entries in row number row as an array. - *

- * Row indices start at 0. A MatrixIndexException is thrown - * unless 0 <= row < rowDimension.

- * - * @param row the row to be fetched - * @return array of entries in the row - * @throws MatrixIndexException if the specified row index is not valid - */ + /** {@inheritDoc} */ + public RealVector getColumnVector(int column) throws MatrixIndexException { + return new RealVectorImpl(getColumn(column), false); + } + + /** {@inheritDoc} */ + public RealVector getRowVector(int row) throws MatrixIndexException { + return new RealVectorImpl(getRow(row), false); + } + + /** {@inheritDoc} */ public double[] getRow(int row) throws MatrixIndexException { - if ( !isValidCoordinate( row, 0 ) ) { - throw new MatrixIndexException("illegal row argument"); - } - int ncols = this.getColumnDimension(); - double[] out = new double[ncols]; + checkRowIndex(row); + final int ncols = this.getColumnDimension(); + final double[] out = new double[ncols]; System.arraycopy(data[row], 0, out, 0, ncols); return out; } - /** - * Returns the entries in column number col as an array. - *

- * Column indices start at 0. A MatrixIndexException is thrown - * unless 0 <= column < columnDimension.

- * - * @param col the column to be fetched - * @return array of entries in the column - * @throws MatrixIndexException if the specified column index is not valid - */ + /** {@inheritDoc} */ public double[] getColumn(int col) throws MatrixIndexException { - if ( !isValidCoordinate(0, col) ) { - throw new MatrixIndexException("illegal column argument"); - } - int nRows = this.getRowDimension(); - double[] out = new double[nRows]; + checkColumnIndex(col); + final int nRows = this.getRowDimension(); + final double[] out = new double[nRows]; for (int row = 0; row < nRows; row++) { out[row] = data[row][col]; } return out; } - /** - * Returns the entry in the specified row and column. - *

- * Row and column indices start at 0 and must satisfy - *

    - *
  • 0 <= row < rowDimension
  • - *
  • 0 <= column < columnDimension
  • - *
- * otherwise a MatrixIndexException is thrown.

- * - * @param row row location of entry to be fetched - * @param column column location of entry to be fetched - * @return matrix entry in row,column - * @throws MatrixIndexException if the row or column index is not valid - */ + /** {@inheritDoc} */ public double getEntry(int row, int column) throws MatrixIndexException { - if (!isValidCoordinate(row,column)) { - throw new MatrixIndexException("matrix entry does not exist"); + try { + return data[row][column]; + } catch (ArrayIndexOutOfBoundsException e) { + throw new MatrixIndexException("no entry at indices ({0}, {1}) in a {2}x{3} matrix", + new Object[] { + row, column, + getRowDimension(), getColumnDimension() + }); } - return data[row][column]; } - /** - * Returns the transpose matrix. - * - * @return transpose matrix - */ + /** {@inheritDoc} */ public RealMatrix transpose() { - int nRows = this.getRowDimension(); - int nCols = this.getColumnDimension(); - RealMatrixImpl out = new RealMatrixImpl(nCols, nRows); - double[][] outData = out.getDataRef(); + final int nRows = getRowDimension(); + final int nCols = getColumnDimension(); + final double[][] outData = new double[nCols][nRows]; for (int row = 0; row < nRows; row++) { + final double[] dataRow = data[row]; for (int col = 0; col < nCols; col++) { - outData[col][row] = data[row][col]; + outData[col][row] = dataRow[col]; } } - return out; + return new RealMatrixImpl(outData, false); } - /** - * Returns the inverse matrix if this matrix is invertible. - * - * @return inverse matrix - * @throws InvalidMatrixException if this is not invertible - */ + /** {@inheritDoc} */ public RealMatrix inverse() throws InvalidMatrixException { - return solve(MatrixUtils.createRealIdentityMatrix - (this.getRowDimension())); + if (lu == null) { + lu = new LUDecompositionImpl(this); + } + return lu.getInverse(); } - /** - * @return determinant - * @throws InvalidMatrixException if matrix is not square - */ + /** {@inheritDoc} */ + @Deprecated public double getDeterminant() throws InvalidMatrixException { - if (!isSquare()) { - throw new InvalidMatrixException("matrix is not square"); - } - if (isSingular()) { // note: this has side effect of attempting LU decomp if lu == null - return 0d; - } else { - double det = parity; - for (int i = 0; i < this.getRowDimension(); i++) { - det *= lu[i][i]; - } - return det; + if (lu == null) { + lu = new LUDecompositionImpl(this); } + return lu.getDeterminant(); } - /** - * @return true if the matrix is square (rowDimension = columnDimension) - */ + /** {@inheritDoc} */ public boolean isSquare() { return (this.getColumnDimension() == this.getRowDimension()); } - /** - * @return true if the matrix is singular - */ + /** {@inheritDoc} */ + @Deprecated public boolean isSingular() { if (lu == null) { - try { - luDecompose(); - return false; - } catch (InvalidMatrixException ex) { - return true; - } - } else { // LU decomp must have been successfully performed - return false; // so the matrix is not singular + lu = new LUDecompositionImpl(this); } + return !lu.isNonSingular(); } - /** - * @return rowDimension - */ + /** {@inheritDoc} */ public int getRowDimension() { return data.length; } - /** - * @return columnDimension - */ + /** {@inheritDoc} */ public int getColumnDimension() { return data[0].length; } - /** - * @return trace - * @throws IllegalArgumentException if the matrix is not square - */ + /** {@inheritDoc} */ public double getTrace() throws IllegalArgumentException { if (!isSquare()) { throw new IllegalArgumentException("matrix is not square"); @@ -626,40 +646,67 @@ public class RealMatrixImpl implements RealMatrix, Serializable { return trace; } - /** - * @param v vector to operate on - * @throws IllegalArgumentException if columnDimension != v.length - * @return resulting vector - */ + /** {@inheritDoc} */ public double[] operate(double[] v) throws IllegalArgumentException { - if (v.length != this.getColumnDimension()) { + final int nRows = this.getRowDimension(); + final int nCols = this.getColumnDimension(); + if (v.length != nCols) { throw new IllegalArgumentException("vector has wrong length"); } - int nRows = this.getRowDimension(); - int nCols = this.getColumnDimension(); - double[] out = new double[v.length]; + final double[] out = new double[nRows]; for (int row = 0; row < nRows; row++) { + final double[] dataRow = data[row]; double sum = 0; for (int i = 0; i < nCols; i++) { - sum += data[row][i] * v[i]; + sum += dataRow[i] * v[i]; } out[row] = sum; } return out; } + /** {@inheritDoc} */ + public RealVector operate(RealVector v) throws IllegalArgumentException { + try { + return operate((RealVectorImpl) v); + } catch (ClassCastException cce) { + final int nRows = this.getRowDimension(); + final int nCols = this.getColumnDimension(); + if (v.getDimension() != nCols) { + throw new IllegalArgumentException("vector has wrong length"); + } + final double[] out = new double[nRows]; + for (int row = 0; row < nRows; row++) { + final double[] dataRow = data[row]; + double sum = 0; + for (int i = 0; i < nCols; i++) { + sum += dataRow[i] * v.getEntry(i); + } + out[row] = sum; + } + return new RealVectorImpl(out, false); + } + } + /** - * @param v vector to premultiply by - * @throws IllegalArgumentException if rowDimension != v.length - * @return resulting matrix + * Returns the result of multiplying this by the vector v. + * + * @param v the vector to operate on + * @return this*v + * @throws IllegalArgumentException if columnDimension != v.size() */ + public RealVectorImpl operate(RealVectorImpl v) throws IllegalArgumentException { + return new RealVectorImpl(operate(v.getDataRef()), false); + } + + /** {@inheritDoc} */ public double[] preMultiply(double[] v) throws IllegalArgumentException { - int nRows = this.getRowDimension(); + final int nRows = this.getRowDimension(); if (v.length != nRows) { throw new IllegalArgumentException("vector has wrong length"); } - int nCols = this.getColumnDimension(); - double[] out = new double[nCols]; + final int nCols = this.getColumnDimension(); + final double[] out = new double[nCols]; for (int col = 0; col < nCols; col++) { double sum = 0; for (int i = 0; i < nRows; i++) { @@ -670,88 +717,55 @@ public class RealMatrixImpl implements RealMatrix, Serializable { return out; } - /** - * Returns a matrix of (column) solution vectors for linear systems with - * coefficient matrix = this and constant vectors = columns of - * b. - * - * @param b array of constant forming RHS of linear systems to - * to solve - * @return solution array - * @throws IllegalArgumentException if this.rowDimension != row dimension - * @throws InvalidMatrixException if this matrix is not square or is singular - */ - public double[] solve(double[] b) throws IllegalArgumentException, InvalidMatrixException { - int nRows = this.getRowDimension(); - if (b.length != nRows) { - throw new IllegalArgumentException("constant vector has wrong length"); + /** {@inheritDoc} */ + public RealVector preMultiply(RealVector v) throws IllegalArgumentException { + try { + return preMultiply((RealVectorImpl) v); + } catch (ClassCastException cce) { + final int nRows = this.getRowDimension(); + if (v.getDimension() != nRows) { + throw new IllegalArgumentException("vector has wrong length"); + } + final int nCols = this.getColumnDimension(); + final double[] out = new double[nCols]; + for (int col = 0; col < nCols; col++) { + double sum = 0; + for (int i = 0; i < nRows; i++) { + sum += data[i][col] * v.getEntry(i); + } + out[col] = sum; + } + return new RealVectorImpl(out, false); } - RealMatrix bMatrix = new RealMatrixImpl(b); - double[][] solution = ((RealMatrixImpl) (solve(bMatrix))).getDataRef(); - double[] out = new double[nRows]; - for (int row = 0; row < nRows; row++) { - out[row] = solution[row][0]; - } - return out; } /** - * Returns a matrix of (column) solution vectors for linear systems with - * coefficient matrix = this and constant vectors = columns of - * b. + * Returns the (row) vector result of premultiplying this by the vector v. * - * @param b matrix of constant vectors forming RHS of linear systems to - * to solve - * @return matrix of solution vectors - * @throws IllegalArgumentException if this.rowDimension != row dimension - * @throws InvalidMatrixException if this matrix is not square or is singular + * @param v the row vector to premultiply by + * @return v*this + * @throws IllegalArgumentException if rowDimension != v.size() */ + RealVectorImpl preMultiply(RealVectorImpl v) throws IllegalArgumentException { + return new RealVectorImpl(preMultiply(v.getDataRef()), false); + } + + /** {@inheritDoc} */ + @Deprecated + public double[] solve(double[] b) throws IllegalArgumentException, InvalidMatrixException { + if (lu == null) { + lu = new LUDecompositionImpl(this); + } + return lu.solve(b); + } + + /** {@inheritDoc} */ + @Deprecated public RealMatrix solve(RealMatrix b) throws IllegalArgumentException, InvalidMatrixException { - if (b.getRowDimension() != this.getRowDimension()) { - throw new IllegalArgumentException("Incorrect row dimension"); + if (lu == null) { + lu = new LUDecompositionImpl(this); } - if (!this.isSquare()) { - throw new InvalidMatrixException("coefficient matrix is not square"); - } - if (this.isSingular()) { // side effect: compute LU decomp - throw new InvalidMatrixException("Matrix is singular."); - } - - int nCol = this.getColumnDimension(); - int nColB = b.getColumnDimension(); - int nRowB = b.getRowDimension(); - - // Apply permutations to b - double[][] bp = new double[nRowB][nColB]; - for (int row = 0; row < nRowB; row++) { - for (int col = 0; col < nColB; col++) { - bp[row][col] = b.getEntry(permutation[row], col); - } - } - - // Solve LY = b - for (int col = 0; col < nCol; col++) { - for (int i = col + 1; i < nCol; i++) { - for (int j = 0; j < nColB; j++) { - bp[i][j] -= bp[col][j] * lu[i][col]; - } - } - } - - // Solve UX = Y - for (int col = nCol - 1; col >= 0; col--) { - for (int j = 0; j < nColB; j++) { - bp[col][j] /= lu[col][col]; - } - for (int i = 0; i < col; i++) { - for (int j = 0; j < nColB; j++) { - bp[i][j] -= bp[col][j] * lu[i][col]; - } - } - } - - RealMatrixImpl outMat = new RealMatrixImpl(bp); - return outMat; + return lu.solve(b); } /** @@ -771,78 +785,12 @@ public class RealMatrixImpl implements RealMatrix, Serializable { * automatically.

* * @throws InvalidMatrixException if the matrix is non-square or singular. + * @deprecated as of release 2.0, replaced by {@link LUDecomposition} */ + @Deprecated public void luDecompose() throws InvalidMatrixException { - - int nRows = this.getRowDimension(); - int nCols = this.getColumnDimension(); - if (nRows != nCols) { - throw new InvalidMatrixException("LU decomposition requires that the matrix be square."); - } - lu = this.getData(); - - // Initialize permutation array and parity - permutation = new int[nRows]; - for (int row = 0; row < nRows; row++) { - permutation[row] = row; - } - parity = 1; - - // Loop over columns - for (int col = 0; col < nCols; col++) { - - double sum = 0; - - // upper - for (int row = 0; row < col; row++) { - sum = lu[row][col]; - for (int i = 0; i < row; i++) { - sum -= lu[row][i] * lu[i][col]; - } - lu[row][col] = sum; - } - - // lower - int max = col; // permutation row - double largest = 0d; - for (int row = col; row < nRows; row++) { - sum = lu[row][col]; - for (int i = 0; i < col; i++) { - sum -= lu[row][i] * lu[i][col]; - } - lu[row][col] = sum; - - // maintain best permutation choice - if (Math.abs(sum) > largest) { - largest = Math.abs(sum); - max = row; - } - } - - // Singularity check - if (Math.abs(lu[max][col]) < TOO_SMALL) { - lu = null; - throw new InvalidMatrixException("matrix is singular"); - } - - // Pivot if necessary - if (max != col) { - double tmp = 0; - for (int i = 0; i < nCols; i++) { - tmp = lu[max][i]; - lu[max][i] = lu[col][i]; - lu[col][i] = tmp; - } - int temp = permutation[max]; - permutation[max] = permutation[col]; - permutation[col] = temp; - parity = -parity; - } - - //Divide the lower elements by the "winning" diagonal elt. - for (int row = col + 1; row < nRows; row++) { - lu[row][col] /= lu[col][col]; - } + if (lu == null) { + lu = new LUDecompositionImpl(this); } } @@ -855,12 +803,14 @@ public class RealMatrixImpl implements RealMatrix, Serializable { res.append("RealMatrixImpl{"); if (data != null) { for (int i = 0; i < data.length; i++) { - if (i > 0) + if (i > 0) { res.append(","); + } res.append("{"); for (int j = 0; j < data[0].length; j++) { - if (j > 0) + if (j > 0) { res.append(","); + } res.append(data[i][j]); } res.append("}"); @@ -873,8 +823,7 @@ public class RealMatrixImpl implements RealMatrix, Serializable { /** * Returns true iff object is a * RealMatrixImpl instance with the same dimensions as this - * and all corresponding matrix entries are equal. Corresponding entries - * are compared using {@link java.lang.Double#doubleToLongBits(double)} + * and all corresponding matrix entries are equal. * * @param object the object to test equality against. * @return true if object equals this @@ -887,15 +836,15 @@ public class RealMatrixImpl implements RealMatrix, Serializable { return false; } RealMatrix m = (RealMatrix) object; - int nRows = getRowDimension(); - int nCols = getColumnDimension(); + final int nRows = getRowDimension(); + final int nCols = getColumnDimension(); if (m.getColumnDimension() != nCols || m.getRowDimension() != nRows) { return false; } for (int row = 0; row < nRows; row++) { + final double[] dataRow = data[row]; for (int col = 0; col < nCols; col++) { - if (Double.doubleToLongBits(data[row][col]) != - Double.doubleToLongBits(m.getEntry(row, col))) { + if (dataRow[col] != m.getEntry(row, col)) { return false; } } @@ -910,85 +859,20 @@ public class RealMatrixImpl implements RealMatrix, Serializable { */ public int hashCode() { int ret = 7; - int nRows = getRowDimension(); - int nCols = getColumnDimension(); + final int nRows = getRowDimension(); + final int nCols = getColumnDimension(); ret = ret * 31 + nRows; ret = ret * 31 + nCols; for (int row = 0; row < nRows; row++) { - for (int col = 0; col < nCols; col++) { + final double[] dataRow = data[row]; + for (int col = 0; col < nCols; col++) { ret = ret * 31 + (11 * (row+1) + 17 * (col+1)) * - MathUtils.hash(data[row][col]); + MathUtils.hash(dataRow[col]); } } return ret; } - //------------------------ Protected methods - - /** - * Returns dimension x dimension identity matrix. - * - * @param dimension dimension of identity matrix to generate - * @return identity matrix - * @throws IllegalArgumentException if dimension is not positive - * @deprecated use {@link MatrixUtils#createRealIdentityMatrix} - */ - protected RealMatrix getIdentity(int dimension) { - return MatrixUtils.createRealIdentityMatrix(dimension); - } - - /** - * Returns the LU decomposition as a RealMatrix. - * Returns a fresh copy of the cached LU matrix if this has been computed; - * otherwise the composition is computed and cached for use by other methods. - * Since a copy is returned in either case, changes to the returned matrix do not - * affect the LU decomposition property. - *

- * The matrix returned is a compact representation of the LU decomposition. - * Elements below the main diagonal correspond to entries of the "L" matrix; - * elements on and above the main diagonal correspond to entries of the "U" - * matrix.

- *

- * Example:

-     *
-     *     Returned matrix                L                  U
-     *         2  3  1                   1  0  0            2  3  1
-     *         5  4  6                   5  1  0            0  4  6
-     *         1  7  8                   1  7  1            0  0  8
-     * 
- * - * The L and U matrices satisfy the matrix equation LU = permuteRows(this),
- * where permuteRows reorders the rows of the matrix to follow the order determined - * by the permutation property.

- * - * @return LU decomposition matrix - * @throws InvalidMatrixException if the matrix is non-square or singular. - */ - protected RealMatrix getLUMatrix() throws InvalidMatrixException { - if (lu == null) { - luDecompose(); - } - return new RealMatrixImpl(lu); - } - - /** - * Returns the permutation associated with the lu decomposition. - * The entries of the array represent a permutation of the numbers 0, ... , nRows - 1. - *

- * Example: - * permutation = [1, 2, 0] means current 2nd row is first, current third row is second - * and current first row is last.

- *

- * Returns a fresh copy of the array.

- * - * @return the permutation - */ - protected int[] getPermutation() { - int[] out = new int[permutation.length]; - System.arraycopy(permutation, 0, out, 0, permutation.length); - return out; - } - //------------------------ Private methods /** @@ -997,8 +881,8 @@ public class RealMatrixImpl implements RealMatrix, Serializable { * @return a copy of the underlying data array. */ private double[][] copyOut() { - int nRows = this.getRowDimension(); - double[][] out = new double[nRows][this.getColumnDimension()]; + final int nRows = this.getRowDimension(); + final double[][] out = new double[nRows][this.getColumnDimension()]; // can't copy 2-d array in one shot, otherwise get row references for (int i = 0; i < nRows; i++) { System.arraycopy(data[i], 0, out[i], 0, data[i].length); @@ -1017,21 +901,32 @@ public class RealMatrixImpl implements RealMatrix, Serializable { * @throws NullPointerException if input array is null */ private void copyIn(double[][] in) { - setSubMatrix(in,0,0); + setSubMatrix(in, 0, 0); } /** - * Tests a given coordinate as being valid or invalid - * - * @param row the row index. - * @param col the column index. - * @return true if the coordinate is with the current dimensions + * Check if a row index is valid. + * @param row row index to check + * @exception MatrixIndexException if index is not valid */ - private boolean isValidCoordinate(int row, int col) { - int nRows = this.getRowDimension(); - int nCols = this.getColumnDimension(); + private void checkRowIndex(final int row) { + if (row < 0 || row >= getRowDimension()) { + throw new MatrixIndexException("row index {0} out of allowed range [{1}, {2}]", + new Object[] { row, 0, getRowDimension() - 1}); + } + } - return !(row < 0 || row > nRows - 1 || col < 0 || col > nCols -1); + /** + * Check if a column index is valid. + * @param column column index to check + * @exception MatrixIndexException if index is not valid + */ + private void checkColumnIndex(final int column) + throws MatrixIndexException { + if (column < 0 || column >= getColumnDimension()) { + throw new MatrixIndexException("column index {0} out of allowed range [{1}, {2}]", + new Object[] { column, 0, getColumnDimension() - 1}); + } } } diff --git a/src/java/org/apache/commons/math/linear/RealVector.java b/src/java/org/apache/commons/math/linear/RealVector.java new file mode 100644 index 000000000..9c449e326 --- /dev/null +++ b/src/java/org/apache/commons/math/linear/RealVector.java @@ -0,0 +1,799 @@ +/* + * 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.linear; + +/** + * Interface defining a real-valued vector with basic algebraic operations. + *

+ * vector element indexing is 0-based -- e.g., getEntry(0) + * returns the first element of the vector. + *

+ *

+ * The various mapXxx and mapXxxToSelf methods operate + * on vectors element-wise, i.e. they perform the same operation (adding a scalar, + * applying a function ...) on each element in turn. The mapXxx + * versions create a new vector to hold the result and do not change the instance. + * The mapXxxToSelf versions use the instance itself to store the + * results, so the instance is changed by these methods. In both cases, the result + * vector is returned by the methods, this allows to use the fluent API + * style, like this: + *

+ *
+ *   RealVector result = v.mapAddToSelf(3.0).mapTanToSelf().mapSquareToSelf();
+ * 
+ * + * @version $Revision$ $Date$ + * @since 2.0 + */ +public interface RealVector { + + /** + * Returns a (deep) copy of this. + * @return vector copy + */ + RealVector copy(); + + /** + * Compute the sum of this and v. + * @param v vector to be added + * @return this + v + * @throws IllegalArgumentException if v is not the same size as this + */ + RealVector add(RealVector v) + throws IllegalArgumentException; + + /** + * Compute the sum of this and v. + * @param v vector to be added + * @return this + v + * @throws IllegalArgumentException if v is not the same size as this + */ + RealVector add(double[] v) + throws IllegalArgumentException; + + /** + * Compute this minus v. + * @param v vector to be subtracted + * @return this + v + * @throws IllegalArgumentException if v is not the same size as this + */ + RealVector subtract(RealVector v) + throws IllegalArgumentException; + + /** + * Compute this minus v. + * @param v vector to be subtracted + * @return this + v + * @throws IllegalArgumentException if v is not the same size as this + */ + RealVector subtract(double[] v) + throws IllegalArgumentException; + + /** + * Map an addition operation to each entry. + * @param d value to be added to each entry + * @return this + d + */ + RealVector mapAdd(double d); + + /** + * Map an addition operation to each entry. + *

The instance is changed by this method.

+ * @param d value to be added to each entry + * @return for convenience, return this + */ + RealVector mapAddToSelf(double d); + + /** + * Map a subtraction operation to each entry. + * @param d value to be subtracted to each entry + * @return this - d + */ + RealVector mapSubtract(double d); + + /** + * Map a subtraction operation to each entry. + *

The instance is changed by this method.

+ * @param d value to be subtracted to each entry + * @return for convenience, return this + */ + RealVector mapSubtractToSelf(double d); + + /** + * Map a multiplication operation to each entry. + * @param d value to multiply all entries by + * @return this * d + */ + RealVector mapMultiply(double d); + + /** + * Map a multiplication operation to each entry. + *

The instance is changed by this method.

+ * @param d value to multiply all entries by + * @return for convenience, return this + */ + RealVector mapMultiplyToSelf(double d); + + /** + * Map a division operation to each entry. + * @param d value to divide all entries by + * @return this / d + */ + RealVector mapDivide(double d); + + /** + * Map a division operation to each entry. + *

The instance is changed by this method.

+ * @param d value to divide all entries by + * @return for convenience, return this + */ + RealVector mapDivideToSelf(double d); + + /** + * Map a power operation to each entry. + * @param d value to raise all entries to + * @return this ^ d + */ + RealVector mapPow(double d); + + /** + * Map a power operation to each entry. + *

The instance is changed by this method.

+ * @param d value to raise all entries to + * @return for convenience, return this + */ + RealVector mapPowToSelf(double d); + + /** + * Map the {@link Math#exp(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapExp(); + + /** + * Map the {@link Math#exp(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapExpToSelf(); + + /** + * Map the {@link Math#expm1(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapExpm1(); + + /** + * Map the {@link Math#expm1(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapExpm1ToSelf(); + + /** + * Map the {@link Math#log(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapLog(); + + /** + * Map the {@link Math#log(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapLogToSelf(); + + /** + * Map the {@link Math#log10(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapLog10(); + + /** + * Map the {@link Math#log10(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapLog10ToSelf(); + + /** + * Map the {@link Math#log1p(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapLog1p(); + + /** + * Map the {@link Math#log1p(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapLog1pToSelf(); + + /** + * Map the {@link Math#cosh(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapCosh(); + + /** + * Map the {@link Math#cosh(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapCoshToSelf(); + + /** + * Map the {@link Math#sinh(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapSinh(); + + /** + * Map the {@link Math#sinh(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapSinhToSelf(); + + /** + * Map the {@link Math#tanh(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapTanh(); + + /** + * Map the {@link Math#tanh(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapTanhToSelf(); + + /** + * Map the {@link Math#cos(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapCos(); + + /** + * Map the {@link Math#cos(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapCosToSelf(); + + /** + * Map the {@link Math#sin(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapSin(); + + /** + * Map the {@link Math#sin(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapSinToSelf(); + + /** + * Map the {@link Math#tan(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapTan(); + + /** + * Map the {@link Math#tan(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapTanToSelf(); + + /** + * Map the {@link Math#acos(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapAcos(); + + /** + * Map the {@link Math#acos(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapAcosToSelf(); + + /** + * Map the {@link Math#asin(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapAsin(); + + /** + * Map the {@link Math#asin(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapAsinToSelf(); + + /** + * Map the {@link Math#atan(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapAtan(); + + /** + * Map the {@link Math#atan(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapAtanToSelf(); + + /** + * Map the 1/x function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapInv(); + + /** + * Map the 1/x function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapInvToSelf(); + + /** + * Map the {@link Math#abs(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapAbs(); + + /** + * Map the {@link Math#abs(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapAbsToSelf(); + + /** + * Map the {@link Math#sqrt(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapSqrt(); + + /** + * Map the {@link Math#sqrt(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapSqrtToSelf(); + + /** + * Map the {@link Math#cbrt(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapCbrt(); + + /** + * Map the {@link Math#cbrt(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapCbrtToSelf(); + + /** + * Map the {@link Math#ceil(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapCeil(); + + /** + * Map the {@link Math#ceil(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapCeilToSelf(); + + /** + * Map the {@link Math#floor(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapFloor(); + + /** + * Map the {@link Math#floor(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapFloorToSelf(); + + /** + * Map the {@link Math#rint(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapRint(); + + /** + * Map the {@link Math#rint(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapRintToSelf(); + + /** + * Map the {@link Math#signum(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapSignum(); + + /** + * Map the {@link Math#signum(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapSignumToSelf(); + + /** + * Map the {@link Math#ulp(double)} function to each entry. + * @return a vector containing the result of applying the function to each entry + */ + RealVector mapUlp(); + + /** + * Map the {@link Math#ulp(double)} function to each entry. + *

The instance is changed by this method.

+ * @return for convenience, return this + */ + RealVector mapUlpToSelf(); + + /** + * Element-by-element multiplication. + * @param v vector by which instance elements must be multiplied + * @return a vector containing this[i] * v[i] for all i + * @throws IllegalArgumentException if v is not the same size as this + */ + public RealVector ebeMultiply(RealVector v) + throws IllegalArgumentException; + + /** + * Element-by-element multiplication. + * @param v vector by which instance elements must be multiplied + * @return a vector containing this[i] * v[i] for all i + * @throws IllegalArgumentException if v is not the same size as this + */ + public RealVector ebeMultiply(double[] v) + throws IllegalArgumentException; + + /** + * Element-by-element division. + * @param v vector by which instance elements must be divided + * @return a vector containing this[i] / v[i] for all i + * @throws IllegalArgumentException if v is not the same size as this + */ + public RealVector ebeDivide(RealVector v) + throws IllegalArgumentException; + + /** + * Element-by-element division. + * @param v vector by which instance elements must be divided + * @return a vector containing this[i] / v[i] for all i + * @throws IllegalArgumentException if v is not the same size as this + */ + public RealVector ebeDivide(double[] v) + throws IllegalArgumentException; + + /** + * Returns vector entries as a double array. + * @return double array of entries + */ + double[] getData(); + + /** + * Compute the dot product. + * @param v vector with which dot product should be computed + * @return the scalar dot product between instance and v + * @exception IllegalArgumentException if v is not the same size as this + */ + double dotProduct(RealVector v) + throws IllegalArgumentException; + + /** + * Compute the dot product. + * @param v vector with which dot product should be computed + * @return the scalar dot product between instance and v + * @exception IllegalArgumentException if v is not the same size as this + */ + double dotProduct(double[] v) + throws IllegalArgumentException; + + /** + * Returns the L2 norm of the vector. + *

The L2 norm is the root of the sum of + * the squared elements.

+ * @return norm + * @see #getL1Norm() + * @see #getLInfNorm() + * @see #getDistance(RealVector) + */ + double getNorm(); + + /** + * Returns the L1 norm of the vector. + *

The L1 norm is the sum of the absolute + * values of elements.

+ * @return norm + * @see #getNorm() + * @see #getLInfNorm() + * @see #getL1Distance(RealVector) + */ + double getL1Norm(); + + /** + * Returns the L&infty; norm of the vector. + *

The L&infty; norm is the max of the absolute + * values of elements.

+ * @return norm + * @see #getNorm() + * @see #getL1Norm() + * @see #getLInfDistance(RealVector) + */ + double getLInfNorm(); + + /** + * Distance between two vectors. + *

This method computes the distance consistent with the + * L2 norm, i.e. the square root of the sum of + * elements differences, or euclidian distance.

+ * @param v vector to which distance is requested + * @return distance between two vectors. + * @exception IllegalArgumentException if v is not the same size as this + * @see #getL1Distance(RealVector) + * @see #getLInfDistance(RealVector) + * @see #getNorm() + */ + double getDistance(RealVector v) + throws IllegalArgumentException; + + /** + * Distance between two vectors. + *

This method computes the distance consistent with the + * L2 norm, i.e. the square root of the sum of + * elements differences, or euclidian distance.

+ * @param v vector to which distance is requested + * @return distance between two vectors. + * @exception IllegalArgumentException if v is not the same size as this + * @see #getL1Distance(double[]) + * @see #getLInfDistance(double[]) + * @see #getNorm() + */ + double getDistance(double[] v) + throws IllegalArgumentException; + + /** + * Distance between two vectors. + *

This method computes the distance consistent with + * L1 norm, i.e. the sum of the absolute values of + * elements differences.

+ * @param v vector to which distance is requested + * @return distance between two vectors. + * @exception IllegalArgumentException if v is not the same size as this + * @see #getDistance(RealVector) + * @see #getLInfDistance(RealVector) + * @see #getL1Norm() + */ + double getL1Distance(RealVector v) + throws IllegalArgumentException; + + /** + * Distance between two vectors. + *

This method computes the distance consistent with + * L1 norm, i.e. the sum of the absolute values of + * elements differences.

+ * @param v vector to which distance is requested + * @return distance between two vectors. + * @exception IllegalArgumentException if v is not the same size as this + * @see #getDistance(double[]) + * @see #getLInfDistance(double[]) + * @see #getL1Norm() + */ + double getL1Distance(double[] v) + throws IllegalArgumentException; + + /** + * Distance between two vectors. + *

This method computes the distance consistent with + * L&infty; norm, i.e. the max of the absolute values of + * elements differences.

+ * @param v vector to which distance is requested + * @return distance between two vectors. + * @exception IllegalArgumentException if v is not the same size as this + * @see #getDistance(RealVector) + * @see #getL1Distance(RealVector) + * @see #getLInfNorm() + */ + double getLInfDistance(RealVector v) + throws IllegalArgumentException; + + /** + * Distance between two vectors. + *

This method computes the distance consistent with + * L&infty; norm, i.e. the max of the absolute values of + * elements differences.

+ * @param v vector to which distance is requested + * @return distance between two vectors. + * @exception IllegalArgumentException if v is not the same size as this + * @see #getDistance(double[]) + * @see #getL1Distance(double[]) + * @see #getLInfNorm() + */ + double getLInfDistance(double[] v) + throws IllegalArgumentException; + + /** Creates a unit vector pointing in the direction of this vector. + *

The instance is not changed by this method.

+ * @return a unit vector pointing in direction of this vector + * @exception ArithmeticException if the norm is null + */ + RealVector unitVector(); + + /** Converts this vector into a unit vector. + *

The instance itself is changed by this method.

+ * @exception ArithmeticException if the norm is null + */ + void unitize(); + + /** Find the orthogonal projection of this vector onto another vector. + * @param v vector onto which instance must be projected + * @return projection of the instance onto v + * @throws IllegalArgumentException if v is not the same size as this + */ + RealVector projection(RealVector v) + throws IllegalArgumentException; + + /** Find the orthogonal projection of this vector onto another vector. + * @param v vector onto which instance must be projected + * @return projection of the instance onto v + * @throws IllegalArgumentException if v is not the same size as this + */ + RealVector projection(double[] v) + throws IllegalArgumentException; + + /** + * Compute the outer product. + * @param v vector with which outer product should be computed + * @return the square matrix outer product between instance and v + * @exception IllegalArgumentException if v is not the same size as this + */ + RealMatrix outerProduct(RealVector v) + throws IllegalArgumentException; + + /** + * Compute the outer product. + * @param v vector with which outer product should be computed + * @return the square matrix outer product between instance and v + * @exception IllegalArgumentException if v is not the same size as this + */ + RealMatrix outerProduct(double[] v) + throws IllegalArgumentException; + + /** + * Returns the entry in the specified index. + *

+ * The index start at 0 and must be lesser than the size, + * otherwise a {@link MatrixIndexException} is thrown. + *

+ * @param index index location of entry to be fetched + * @return vector entry at index + * @throws MatrixIndexException if the index is not valid + */ + double getEntry(int index) + throws MatrixIndexException; + + /** + * Returns the size of the vector. + * @return size + */ + int getDimension(); + + /** + * Construct a vector by appending a vector to this vector. + * @param v vector to append to this one. + * @return a new vector + */ + RealVector append(RealVector v); + + /** + * Construct a vector by appending a double to this vector. + * @param d double to append. + * @return a new vector + */ + RealVector append(double d); + + /** + * Construct a vector by appending a double array to this vector. + * @param a double array to append. + * @return a new vector + */ + RealVector append(double[] a); + + /** + * Get a subvector from consecutive elements. + * @param index index of first element. + * @param n number of elements to be retrieved. + * @return a vector containing n elements. + * @exception MatrixIndexException if the index is + * inconsistent with vector size + */ + RealVector get(int index, int n) + throws MatrixIndexException; + + /** + * Set a single element. + * @param index element index. + * @param value new value for the element. + * @exception MatrixIndexException if the index is + * inconsistent with vector size + */ + void set(int index, double value) + throws MatrixIndexException; + + /** + * Set a set of consecutive elements. + * @param index index of first element to be set. + * @param v vector containing the values to set. + * @exception MatrixIndexException if the index is + * inconsistent with vector size + */ + void set(int index, RealVector v) + throws MatrixIndexException; + + /** + * Set a set of consecutive elements. + * @param index index of first element to be set. + * @param v vector containing the values to set. + * @exception MatrixIndexException if the index is + * inconsistent with vector size + */ + void set(int index, double[] v) + throws MatrixIndexException; + + /** + * Set all elements to a single value. + * @param value single value to set for all elements + */ + void set(double value); + + /** + * Convert the vector to a double array. + *

The array is independent from vector data, it's elements + * are copied.

+ * @return array containing a copy of vector elements + */ + double[] toArray(); + + /** + * Returns true if any coordinate of this vector is NaN; false otherwise + * @return true if any coordinate of this vector is NaN; false otherwise + */ + public boolean isNaN(); + + /** + * Returns true if any coordinate of this vector is infinite and none are NaN; + * false otherwise + * @return true if any coordinate of this vector is infinite and none are NaN; + * false otherwise + */ + public boolean isInfinite(); + +} diff --git a/src/java/org/apache/commons/math/linear/RealVectorFormat.java b/src/java/org/apache/commons/math/linear/RealVectorFormat.java new file mode 100644 index 000000000..d51c204ee --- /dev/null +++ b/src/java/org/apache/commons/math/linear/RealVectorFormat.java @@ -0,0 +1,336 @@ +/* + * 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.linear; + +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.util.CompositeFormat; + +/** + * Formats a vector in components list format "{v0; v1; ...; vk-1}". + *

The prefix and suffix "{" and "}" and the separator "; " can be replaced by + * any user-defined strings. The number format for components can be configured.

+ *

White space is ignored at parse time, even if it is in the prefix, suffix + * or separator specifications. So even if the default separator does include a space + * character that is used at format time, both input string "{1;1;1}" and + * " { 1 ; 1 ; 1 } " will be parsed without error and the same vector will be + * returned. In the second case, however, the parse position after parsing will be + * just after the closing curly brace, i.e. just before the trailing space.

+ * + * @version $Revision$ $Date$ + * @since 2.0 + */ +public class RealVectorFormat extends CompositeFormat { + + /** Serializable version identifier */ + private static final long serialVersionUID = -708767813036157690L; + + /** The default prefix: "{". */ + private static final String DEFAULT_PREFIX = "{"; + + /** The default suffix: "}". */ + private static final String DEFAULT_SUFFIX = "}"; + + /** The default separator: ", ". */ + private static final String DEFAULT_SEPARATOR = "; "; + + /** Prefix. */ + private final String prefix; + + /** Suffix. */ + private final String suffix; + + /** Separator. */ + private final String separator; + + /** Trimmed prefix. */ + private final String trimmedPrefix; + + /** Trimmed suffix. */ + private final String trimmedSuffix; + + /** Trimmed separator. */ + private final String trimmedSeparator; + + /** The format used for components. */ + private NumberFormat format; + + /** + * Create an instance with default settings. + *

The instance uses the default prefix, suffix and separator: + * "{", "}", and "; " and the default number format for components.

+ */ + public RealVectorFormat() { + this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, getDefaultNumberFormat()); + } + + /** + * Create an instance with a custom number format for components. + * @param format the custom format for components. + */ + public RealVectorFormat(final NumberFormat format) { + this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format); + } + + /** + * Create an instance with custom prefix, suffix and separator. + * @param prefix prefix to use instead of the default "{" + * @param suffix suffix to use instead of the default "}" + * @param separator separator to use instead of the default "; " + */ + public RealVectorFormat(final String prefix, final String suffix, + final String separator) { + this(prefix, suffix, separator, getDefaultNumberFormat()); + } + + /** + * Create an instance with custom prefix, suffix, separator and format + * for components. + * @param prefix prefix to use instead of the default "{" + * @param suffix suffix to use instead of the default "}" + * @param separator separator to use instead of the default "; " + * @param format the custom format for components. + */ + public RealVectorFormat(final String prefix, final String suffix, + final String separator, final NumberFormat format) { + this.prefix = prefix; + this.suffix = suffix; + this.separator = separator; + trimmedPrefix = prefix.trim(); + trimmedSuffix = suffix.trim(); + trimmedSeparator = separator.trim(); + this.format = format; + } + + /** + * Get the set of locales for which real vectors formats are available. + *

This is the same set as the {@link NumberFormat} set.

+ * @return available real vector format locales. + */ + public static Locale[] getAvailableLocales() { + return NumberFormat.getAvailableLocales(); + } + + /** + * Get the format prefix. + * @return format prefix. + */ + public String getPrefix() { + return prefix; + } + + /** + * Get the format suffix. + * @return format suffix. + */ + public String getSuffix() { + return suffix; + } + + /** + * Get the format separator between components. + * @return format separator. + */ + public String getSeparator() { + return separator; + } + + /** + * Get the components format. + * @return components format. + */ + public NumberFormat getFormat() { + return format; + } + + /** + * Returns the default real vector format for the current locale. + * @return the default real vector format. + */ + public static RealVectorFormat getInstance() { + return getInstance(Locale.getDefault()); + } + + /** + * Returns the default real vector format for the given locale. + * @param locale the specific locale used by the format. + * @return the real vector format specific to the given locale. + */ + public static RealVectorFormat getInstance(final Locale locale) { + return new RealVectorFormat(getDefaultNumberFormat(locale)); + } + + /** + * This static method calls {@link #format(Object)} on a default instance of + * RealVectorFormat. + * + * @param v RealVector object to format + * @return A formatted vector + */ + public static String formatRealVector(RealVector v) { + return getInstance().format(v); + } + + /** + * Formats a {@link RealVector} object to produce a string. + * @param vector the object to format. + * @param toAppendTo where the text is to be appended + * @param pos On input: an alignment field, if desired. On output: the + * offsets of the alignment field + * @return the value passed in as toAppendTo. + */ + public StringBuffer format(RealVector vector, StringBuffer toAppendTo, + FieldPosition pos) { + + pos.setBeginIndex(0); + pos.setEndIndex(0); + + // format prefix + toAppendTo.append(prefix); + + // format components + for (int i = 0; i < vector.getDimension(); ++i) { + if (i > 0) { + toAppendTo.append(separator); + } + formatDouble(vector.getEntry(i), format, toAppendTo, pos); + } + + // format suffix + toAppendTo.append(suffix); + + return toAppendTo; + + } + + /** + * Formats a object to produce a string. + *

obj must be a {@link RealVector} object. Any other type of + * object will result in an {@link IllegalArgumentException} being thrown.

+ * @param obj the object to format. + * @param toAppendTo where the text is to be appended + * @param pos On input: an alignment field, if desired. On output: the + * offsets of the alignment field + * @return the value passed in as toAppendTo. + * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition) + * @throws IllegalArgumentException is obj is not a valid type. + */ + public StringBuffer format(Object obj, StringBuffer toAppendTo, + FieldPosition pos) { + + if (obj instanceof RealVector) { + return format( (RealVector)obj, toAppendTo, pos); + } + + throw new IllegalArgumentException("Cannot format given Object as a RealVector"); + + } + + /** + * Parses a string to produce a {@link RealVector} object. + * @param source the string to parse + * @return the parsed {@link RealVector} object. + * @exception ParseException if the beginning of the specified string + * cannot be parsed. + */ + public RealVectorImpl parse(String source) throws ParseException { + ParsePosition parsePosition = new ParsePosition(0); + RealVectorImpl result = parse(source, parsePosition); + if (parsePosition.getIndex() == 0) { + throw MathRuntimeException.createParseException("unparseable real vector: \"{0}\"", + new Object[] { source }, + parsePosition.getErrorIndex()); + } + return result; + } + + /** + * Parses a string to produce a {@link RealVector} object. + * @param source the string to parse + * @param pos input/ouput parsing parameter. + * @return the parsed {@link RealVector} object. + */ + public RealVectorImpl parse(String source, ParsePosition pos) { + int initialIndex = pos.getIndex(); + + // parse prefix + parseAndIgnoreWhitespace(source, pos); + if (!parseFixedstring(source, trimmedPrefix, pos)) { + return null; + } + + // parse components + List components = new ArrayList(); + for (boolean loop = true; loop;){ + + if (!components.isEmpty()) { + parseAndIgnoreWhitespace(source, pos); + if (!parseFixedstring(source, trimmedSeparator, pos)) { + loop = false; + } + } + + if (loop) { + parseAndIgnoreWhitespace(source, pos); + Number component = parseNumber(source, format, pos); + if (component != null) { + components.add(component); + } else { + // invalid component + // set index back to initial, error index should already be set + pos.setIndex(initialIndex); + return null; + } + } + + } + + // parse suffix + parseAndIgnoreWhitespace(source, pos); + if (!parseFixedstring(source, trimmedSuffix, pos)) { + return null; + } + + // build vector + double[] data = new double[components.size()]; + for (int i = 0; i < data.length; ++i) { + data[i] = components.get(i).doubleValue(); + } + return new RealVectorImpl(data, false); + + } + + /** + * Parses a string to produce a object. + * @param source the string to parse + * @param pos input/ouput parsing parameter. + * @return the parsed object. + * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition) + */ + public Object parseObject(String source, ParsePosition pos) { + return parse(source, pos); + } + +} diff --git a/src/java/org/apache/commons/math/linear/RealVectorImpl.java b/src/java/org/apache/commons/math/linear/RealVectorImpl.java new file mode 100644 index 000000000..c54d65ecd --- /dev/null +++ b/src/java/org/apache/commons/math/linear/RealVectorImpl.java @@ -0,0 +1,1405 @@ +/* + * 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.linear; + +import java.io.Serializable; +import java.util.Arrays; + +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.util.MathUtils; + +/** + * This class implements the {@link RealVector} interface with a double array. + * @version $Revision$ $Date$ + * @since 2.0 + */ +public class RealVectorImpl implements RealVector, Serializable { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -1097961340710804027L; + + /** Default format. */ + private static final RealVectorFormat DEFAULT_FORMAT = + RealVectorFormat.getInstance(); + + /** Entries of the vector. */ + protected double data[]; + + /** + * Build a 0-length vector. + *

Zero-length vectors may be used to initialized construction of vectors + * by data gathering. We start with zero-length and use either the {@link + * #RealVectorImpl(RealVectorImpl, RealVectorImpl)} constructor + * or one of the append method ({@link #append(double)}, {@link + * #append(double[])}, {@link #append(RealVectorImpl)}) to gather data + * into this vector.

+ */ + public RealVectorImpl() { + data = new double[0]; + } + + /** + * Construct a (size)-length vector of zeros. + * @param size size of the vector + */ + public RealVectorImpl(int size) { + data = new double[size]; + } + + /** + * Construct an (size)-length vector with preset values. + * @param size size of the vector + * @param preset fill the vector with this scalar value + */ + public RealVectorImpl(int size, double preset) { + data = new double[size]; + Arrays.fill(data, preset); + } + + /** + * Construct a vector from an array, copying the input array. + * @param d array of doubles. + */ + public RealVectorImpl(double[] d) { + data = d.clone(); + } + + /** + * Create a new RealVectorImpl using the input array as the underlying + * data array. + *

If an array is built specially in order to be embedded in a + * RealVectorImpl and not used directly, the copyArray may be + * set to false + * @param d data for new vector + * @param copyArray if true, the input array will be copied, otherwise + * it will be referenced + * @throws IllegalArgumentException if d is empty + * @throws NullPointerException if d is null + * @see #RealVectorImpl(double[]) + */ + public RealVectorImpl(double[] d, boolean copyArray) + throws NullPointerException, IllegalArgumentException { + if (d == null) { + throw new NullPointerException(); + } + if (d.length == 0) { + throw new IllegalArgumentException("Vector must have at least one element."); + } + data = copyArray ? d.clone() : d; + } + + /** + * Construct a vector from part of a array. + * @param d array of doubles. + * @param pos position of first entry + * @param size number of entries to copy + */ + public RealVectorImpl(double[] d, int pos, int size) { + if (d.length < pos + size) { + throw new IllegalArgumentException("Position " + pos + " and size " + size + + " don't fit to the size of the input array " + + d.length); + } + data = new double[size]; + System.arraycopy(d, pos, data, 0, size); + } + + /** + * Construct a vector from an array. + * @param d array of Doubles. + */ + public RealVectorImpl(Double[] d) { + data = new double[d.length]; + for (int i = 0; i < d.length; i++) { + data[i] = d[i].doubleValue(); + } + } + + /** + * Construct a vector from part of a Double array + * @param d array of Doubles. + * @param pos position of first entry + * @param size number of entries to copy + */ + public RealVectorImpl(Double[] d, int pos, int size) { + if (d.length < pos + size) { + throw new IllegalArgumentException("Position " + pos + " and size " + size + + " don't fit to the size of the input array " + + d.length); + } + data = new double[size]; + for (int i = pos; i < pos + size; i++) { + data[i-pos] = d[i].doubleValue(); + } + } + + /** + * Construct a vector from another vector, using a deep copy. + * @param v vector to copy + */ + public RealVectorImpl(RealVector v) { + data = new double[v.getDimension()]; + for (int i = 0; i < data.length; ++i) { + data[i] = v.getEntry(i); + } + } + + /** + * Construct a vector from another vector, using a deep copy. + * @param v vector to copy + */ + public RealVectorImpl(RealVectorImpl v) { + data = v.data.clone(); + } + + /** + * Construct a vector from another vector. + * @param v vector to copy + * @param deep if true perform a deep copy otherwise perform a shallow copy + */ + public RealVectorImpl(RealVectorImpl v, boolean deep) { + data = deep ? v.data.clone() : v.data; + } + + /** + * Construct a vector by appending one vector to another vector. + * @param v1 first vector (will be put in front of the new vector) + * @param v2 second vector (will be put at back of the new vector) + */ + public RealVectorImpl(RealVectorImpl v1, RealVectorImpl v2) { + data = new double[v1.data.length + v2.data.length]; + System.arraycopy(v1.data, 0, data, 0, v1.data.length); + System.arraycopy(v2.data, 0, data, v1.data.length, v2.data.length); + } + + /** + * Construct a vector by appending one vector to another vector. + * @param v1 first vector (will be put in front of the new vector) + * @param v2 second vector (will be put at back of the new vector) + */ + public RealVectorImpl(RealVectorImpl v1, double[] v2) { + data = new double[v1.data.length + v2.length]; + System.arraycopy(v1.data, 0, data, 0, v1.data.length); + System.arraycopy(v2, 0, data, v1.data.length, v2.length); + } + + /** + * Construct a vector by appending one vector to another vector. + * @param v1 first vector (will be put in front of the new vector) + * @param v2 second vector (will be put at back of the new vector) + */ + public RealVectorImpl(double[] v1, RealVectorImpl v2) { + data = new double[v1.length + v2.data.length]; + System.arraycopy(v1, 0, data, 0, v1.length); + System.arraycopy(v2.data, 0, data, v1.length, v2.data.length); + } + + /** + * Construct a vector by appending one vector to another vector. + * @param v1 first vector (will be put in front of the new vector) + * @param v2 second vector (will be put at back of the new vector) + */ + public RealVectorImpl(double[] v1, double[] v2) { + data = new double[v1.length + v2.length]; + System.arraycopy(v1, 0, data, 0, v1.length); + System.arraycopy(v2, 0, data, v1.length, v2.length); + } + + /** {@inheritDoc} */ + public RealVector copy() { + return new RealVectorImpl(this, true); + } + + /** {@inheritDoc} */ + public RealVector add(RealVector v) + throws IllegalArgumentException { + try { + return add((RealVectorImpl) v); + } catch (ClassCastException cce) { + checkVectorDimensions(v); + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = data[i] + v.getEntry(i); + } + return new RealVectorImpl(out); + } + } + + /** {@inheritDoc} */ + public RealVector add(double[] v) + throws IllegalArgumentException { + checkVectorDimensions(v.length); + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = data[i] + v[i]; + } + return new RealVectorImpl(out); + } + + /** + * Compute the sum of this and v. + * @param v vector to be added + * @return this + v + * @throws IllegalArgumentException if v is not the same size as this + */ + public RealVectorImpl add(RealVectorImpl v) + throws IllegalArgumentException { + return (RealVectorImpl) add(v.data); + } + + /** {@inheritDoc} */ + public RealVector subtract(RealVector v) + throws IllegalArgumentException { + try { + return subtract((RealVectorImpl) v); + } catch (ClassCastException cce) { + checkVectorDimensions(v); + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = data[i] - v.getEntry(i); + } + return new RealVectorImpl(out); + } + } + + /** {@inheritDoc} */ + public RealVector subtract(double[] v) + throws IllegalArgumentException { + checkVectorDimensions(v.length); + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = data[i] - v[i]; + } + return new RealVectorImpl(out); + } + + /** + * Compute this minus v. + * @param v vector to be subtracted + * @return this + v + * @throws IllegalArgumentException if v is not the same size as this + */ + public RealVectorImpl subtract(RealVectorImpl v) + throws IllegalArgumentException { + return (RealVectorImpl) subtract(v.data); + } + + /** {@inheritDoc} */ + public RealVector mapAdd(double d) { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = data[i] + d; + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapAddToSelf(double d) { + for (int i = 0; i < data.length; i++) { + data[i] = data[i] + d; + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapSubtract(double d) { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = data[i] - d; + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapSubtractToSelf(double d) { + for (int i = 0; i < data.length; i++) { + data[i] = data[i] - d; + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapMultiply(double d) { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = data[i] * d; + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapMultiplyToSelf(double d) { + for (int i = 0; i < data.length; i++) { + data[i] = data[i] * d; + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapDivide(double d) { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = data[i] / d; + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapDivideToSelf(double d) { + for (int i = 0; i < data.length; i++) { + data[i] = data[i] / d; + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapPow(double d) { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.pow(data[i], d); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapPowToSelf(double d) { + for (int i = 0; i < data.length; i++) { + data[i] = Math.pow(data[i], d); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapExp() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.exp(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapExpToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.exp(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapExpm1() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.expm1(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapExpm1ToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.expm1(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapLog() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.log(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapLogToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.log(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapLog10() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.log10(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapLog10ToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.log10(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapLog1p() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.log1p(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapLog1pToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.log1p(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapCosh() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.cosh(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapCoshToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.cosh(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapSinh() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.sinh(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapSinhToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.sinh(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapTanh() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.tanh(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapTanhToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.tanh(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapCos() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.cos(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapCosToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.cos(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapSin() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.sin(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapSinToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.sin(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapTan() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.tan(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapTanToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.tan(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapAcos() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.acos(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapAcosToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.acos(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapAsin() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.asin(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapAsinToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.asin(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapAtan() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.atan(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapAtanToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.atan(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapInv() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = 1.0 / data[i]; + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapInvToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = 1.0 / data[i]; + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapAbs() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.abs(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapAbsToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.abs(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapSqrt() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.sqrt(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapSqrtToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.sqrt(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapCbrt() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.cbrt(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapCbrtToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.cbrt(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapCeil() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.ceil(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapCeilToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.ceil(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapFloor() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.floor(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapFloorToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.floor(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapRint() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.rint(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapRintToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.rint(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapSignum() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.signum(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapSignumToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.signum(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector mapUlp() { + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = Math.ulp(data[i]); + } + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector mapUlpToSelf() { + for (int i = 0; i < data.length; i++) { + data[i] = Math.ulp(data[i]); + } + return this; + } + + /** {@inheritDoc} */ + public RealVector ebeMultiply(RealVector v) + throws IllegalArgumentException { + try { + return ebeMultiply((RealVectorImpl) v); + } catch (ClassCastException cce) { + checkVectorDimensions(v); + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = data[i] * v.getEntry(i); + } + return new RealVectorImpl(out); + } + } + + /** {@inheritDoc} */ + public RealVector ebeMultiply(double[] v) + throws IllegalArgumentException { + checkVectorDimensions(v.length); + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = data[i] * v[i]; + } + return new RealVectorImpl(out); + } + + /** + * Element-by-element multiplication. + * @param v vector by which instance elements must be multiplied + * @return a vector containing this[i] * v[i] for all i + * @exception IllegalArgumentException if v is not the same size as this + */ + public RealVectorImpl ebeMultiply(RealVectorImpl v) + throws IllegalArgumentException { + return (RealVectorImpl) ebeMultiply(v.data); + } + + /** {@inheritDoc} */ + public RealVector ebeDivide(RealVector v) + throws IllegalArgumentException { + try { + return ebeDivide((RealVectorImpl) v); + } catch (ClassCastException cce) { + checkVectorDimensions(v); + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = data[i] / v.getEntry(i); + } + return new RealVectorImpl(out); + } + } + + /** {@inheritDoc} */ + public RealVector ebeDivide(double[] v) + throws IllegalArgumentException { + checkVectorDimensions(v.length); + double[] out = new double[data.length]; + for (int i = 0; i < data.length; i++) { + out[i] = data[i] / v[i]; + } + return new RealVectorImpl(out); + } + + /** + * Element-by-element division. + * @param v vector by which instance elements must be divided + * @return a vector containing this[i] / v[i] for all i + * @throws IllegalArgumentException if v is not the same size as this + */ + public RealVectorImpl ebeDivide(RealVectorImpl v) + throws IllegalArgumentException { + return (RealVectorImpl) ebeDivide(v.data); + } + + /** {@inheritDoc} */ + public double[] getData() { + return data.clone(); + } + + /** + * Returns a reference to the underlying data array. + *

Does not make a fresh copy of the underlying data.

+ * @return array of entries + */ + public double[] getDataRef() { + return data; + } + + /** {@inheritDoc} */ + public double dotProduct(RealVector v) + throws IllegalArgumentException { + try { + return dotProduct((RealVectorImpl) v); + } catch (ClassCastException cce) { + checkVectorDimensions(v); + double dot = 0; + for (int i = 0; i < data.length; i++) { + dot += data[i] * v.getEntry(i); + } + return dot; + } + } + + /** {@inheritDoc} */ + public double dotProduct(double[] v) + throws IllegalArgumentException { + checkVectorDimensions(v.length); + double dot = 0; + for (int i = 0; i < data.length; i++) { + dot += data[i] * v[i]; + } + return dot; + } + + /** + * Compute the dot product. + * @param v vector with which dot product should be computed + * @return the scalar dot product between instance and v + * @exception IllegalArgumentException if v is not the same size as this + */ + public double dotProduct(RealVectorImpl v) + throws IllegalArgumentException { + return dotProduct(v.data); + } + + /** {@inheritDoc} */ + public double getNorm() { + double sum = 0; + for (double a : data) { + sum += a * a; + } + return Math.sqrt(sum); + } + + /** {@inheritDoc} */ + public double getL1Norm() { + double sum = 0; + for (double a : data) { + sum += Math.abs(a); + } + return sum; + } + + /** {@inheritDoc} */ + public double getLInfNorm() { + double max = 0; + for (double a : data) { + max += Math.max(max, Math.abs(a)); + } + return max; + } + + /** {@inheritDoc} */ + public double getDistance(RealVector v) + throws IllegalArgumentException { + try { + return getDistance((RealVectorImpl) v); + } catch (ClassCastException cce) { + checkVectorDimensions(v); + double sum = 0; + for (int i = 0; i < data.length; ++i) { + final double delta = data[i] - v.getEntry(i); + sum += delta * delta; + } + return Math.sqrt(sum); + } + } + + /** {@inheritDoc} */ + public double getDistance(double[] v) + throws IllegalArgumentException { + checkVectorDimensions(v.length); + double sum = 0; + for (int i = 0; i < data.length; ++i) { + final double delta = data[i] - v[i]; + sum += delta * delta; + } + return Math.sqrt(sum); + } + + /** + * Distance between two vectors. + *

This method computes the distance consistent with the + * L2 norm, i.e. the square root of the sum of + * elements differences, or euclidian distance.

+ * @param v vector to which distance is requested + * @return distance between two vectors. + * @exception IllegalArgumentException if v is not the same size as this + * @see #getDistance(RealVector) + * @see #getL1Distance(RealVectorImpl) + * @see #getLInfDistance(RealVectorImpl) + * @see #getNorm() + */ + public double getDistance(RealVectorImpl v) + throws IllegalArgumentException { + return getDistance(v.data); + } + + /** {@inheritDoc} */ + public double getL1Distance(RealVector v) + throws IllegalArgumentException { + try { + return getL1Distance((RealVectorImpl) v); + } catch (ClassCastException cce) { + checkVectorDimensions(v); + double sum = 0; + for (int i = 0; i < data.length; ++i) { + final double delta = data[i] - v.getEntry(i); + sum += Math.abs(delta); + } + return sum; + } + } + + /** {@inheritDoc} */ + public double getL1Distance(double[] v) + throws IllegalArgumentException { + checkVectorDimensions(v.length); + double sum = 0; + for (int i = 0; i < data.length; ++i) { + final double delta = data[i] - v[i]; + sum += Math.abs(delta); + } + return sum; + } + + /** + * Distance between two vectors. + *

This method computes the distance consistent with + * L1 norm, i.e. the sum of the absolute values of + * elements differences.

+ * @param v vector to which distance is requested + * @return distance between two vectors. + * @exception IllegalArgumentException if v is not the same size as this + * @see #getDistance(RealVector) + * @see #getL1Distance(RealVectorImpl) + * @see #getLInfDistance(RealVectorImpl) + * @see #getNorm() + */ + public double getL1Distance(RealVectorImpl v) + throws IllegalArgumentException { + return getL1Distance(v.data); + } + + /** {@inheritDoc} */ + public double getLInfDistance(RealVector v) + throws IllegalArgumentException { + try { + return getLInfDistance((RealVectorImpl) v); + } catch (ClassCastException cce) { + checkVectorDimensions(v); + double max = 0; + for (int i = 0; i < data.length; ++i) { + final double delta = data[i] - v.getEntry(i); + max = Math.max(max, Math.abs(delta)); + } + return max; + } + } + + /** {@inheritDoc} */ + public double getLInfDistance(double[] v) + throws IllegalArgumentException { + checkVectorDimensions(v.length); + double max = 0; + for (int i = 0; i < data.length; ++i) { + final double delta = data[i] - v[i]; + max = Math.max(max, Math.abs(delta)); + } + return max; + } + + /** + * Distance between two vectors. + *

This method computes the distance consistent with + * L&infty; norm, i.e. the max of the absolute values of + * elements differences.

+ * @param v vector to which distance is requested + * @return distance between two vectors. + * @exception IllegalArgumentException if v is not the same size as this + * @see #getDistance(RealVector) + * @see #getL1Distance(RealVectorImpl) + * @see #getLInfDistance(RealVectorImpl) + * @see #getNorm() + */ + public double getLInfDistance(RealVectorImpl v) + throws IllegalArgumentException { + return getLInfDistance(v.data); + } + + /** {@inheritDoc} */ + public RealVector unitVector() throws ArithmeticException { + final double norm = getNorm(); + if (norm == 0) { + throw MathRuntimeException.createArithmeticException("zero norm", null); + } + return mapDivide(getNorm()); + } + + /** {@inheritDoc} */ + public void unitize() throws ArithmeticException { + final double norm = getNorm(); + if (norm == 0) { + throw MathRuntimeException.createArithmeticException("cannot normalize a zero norm vector", + null); + } + for (int i = 0; i < data.length; i++) { + data[i] /= norm; + } + } + + /** {@inheritDoc} */ + public RealVector projection(RealVector v) { + return v.mapMultiply(dotProduct(v) / v.dotProduct(v)); + } + + /** {@inheritDoc} */ + public RealVector projection(double[] v) { + return projection(new RealVectorImpl(v, false)); + } + + /** Find the orthogonal projection of this vector onto another vector. + * @param v vector onto which instance must be projected + * @return projection of the instance onto v + * @throws IllegalArgumentException if v is not the same size as this + */ + public RealVectorImpl projection(RealVectorImpl v) { + return (RealVectorImpl) v.mapMultiply(dotProduct(v) / v.dotProduct(v)); + } + + /** {@inheritDoc} */ + public RealMatrix outerProduct(RealVector v) + throws IllegalArgumentException { + try { + return outerProduct((RealVectorImpl) v); + } catch (ClassCastException cce) { + checkVectorDimensions(v); + double[][] out = new double[data.length][data.length]; + for (int i = 0; i < data.length; i++) { + for (int j = 0; j < data.length; j++) { + out[i][j] = data[i] * v.getEntry(j); + } + } + return new RealMatrixImpl(out); + } + } + + /** {@inheritDoc} */ + public RealMatrix outerProduct(double[] v) + throws IllegalArgumentException { + checkVectorDimensions(v.length); + double[][] out = new double[data.length][data.length]; + for (int i = 0; i < data.length; i++) { + for (int j = 0; j < data.length; j++) { + out[i][j] = data[i] * v[j]; + } + } + return new RealMatrixImpl(out); + } + + /** + * Compute the outer product. + * @param v vector with which outer product should be computed + * @return the square matrix outer product between instance and v + * @exception IllegalArgumentException if v is not the same size as this + */ + public RealMatrixImpl outerProduct(RealVectorImpl v) + throws IllegalArgumentException { + return (RealMatrixImpl) outerProduct(v.data); + } + + /** {@inheritDoc} */ + public double getEntry(int index) throws MatrixIndexException { + return data[index]; + } + + /** {@inheritDoc} */ + public int getDimension() { + return data.length; + } + + /** {@inheritDoc} */ + public RealVector append(RealVector v) { + try { + return append((RealVectorImpl) v); + } catch (ClassCastException cce) { + return new RealVectorImpl(this,new RealVectorImpl(v)); + } + } + + /** + * Construct a vector by appending a vector to this vector. + * @param v vector to append to this one. + * @return a new vector + */ + public RealVectorImpl append(RealVectorImpl v) { + return new RealVectorImpl(this, v); + } + + /** {@inheritDoc} */ + public RealVector append(double in) { + final double[] out = new double[data.length + 1]; + System.arraycopy(data, 0, out, 0, data.length); + out[data.length] = in; + return new RealVectorImpl(out); + } + + /** {@inheritDoc} */ + public RealVector append(double[] in) { + return new RealVectorImpl(this, in); + } + + /** {@inheritDoc} */ + public RealVector get(int index, int n) { + RealVectorImpl out = new RealVectorImpl(n); + try { + System.arraycopy(data, index, out.data, 0, n); + } catch (IndexOutOfBoundsException e) { + checkIndex(index); + checkIndex(index + n - 1); + } + return out; + } + + /** {@inheritDoc} */ + public void set(int index, double value) { + try { + data[index] = value; + } catch (IndexOutOfBoundsException e) { + checkIndex(index); + } + } + + /** {@inheritDoc} */ + public void set(int index, RealVector v) { + try { + try { + set(index, (RealVectorImpl) v); + } catch (ClassCastException cce) { + for (int i = index; i < index + v.getDimension(); ++i) { + data[i] = v.getEntry(i-index); + } + } + } catch (IndexOutOfBoundsException e) { + checkIndex(index); + checkIndex(index + v.getDimension() - 1); + } + } + + /** {@inheritDoc} */ + public void set(int index, double[] v) { + try { + System.arraycopy(v, 0, data, index, v.length); + } catch (IndexOutOfBoundsException e) { + checkIndex(index); + checkIndex(index + v.length - 1); + } + } + + /** + * Set a set of consecutive elements. + * + * @param index index of first element to be set. + * @param v vector containing the values to set. + * @exception MatrixIndexException if the index is + * inconsistent with vector size + */ + public void set(int index, RealVectorImpl v) + throws MatrixIndexException { + set(index, v.data); + } + + /** {@inheritDoc} */ + public void set(double value) { + Arrays.fill(data, value); + } + + /** {@inheritDoc} */ + public double[] toArray(){ + return data.clone(); + } + + /** {@inheritDoc} */ + public String toString(){ + return DEFAULT_FORMAT.format(this); + } + + /** + * Check if instance and specified vectors have the same dimension. + * @param v vector to compare instance with + * @exception IllegalArgumentException if the vectors do not + * have the same dimension + */ + public void checkVectorDimensions(RealVector v) + throws IllegalArgumentException { + checkVectorDimensions(v.getDimension()); + } + + /** + * Check if instance dimension is equal to some expected value. + * + * @param n expected dimension. + * @exception IllegalArgumentException if the dimension is + * inconsistent with vector size + */ + public void checkVectorDimensions(int n) + throws IllegalArgumentException { + if (data.length != n) { + throw new IllegalArgumentException("vector dimension is " + data.length + + ", not " + n + " as expected"); + } + } + + /** + * Returns true if any coordinate of this vector is NaN; false otherwise + * @return true if any coordinate of this vector is NaN; false otherwise + */ + public boolean isNaN() { + for (double v : data) { + if (Double.isNaN(v)) { + return true; + } + } + return false; + } + + /** + * Returns true if any coordinate of this vector is infinite and none are NaN; + * false otherwise + * @return true if any coordinate of this vector is infinite and none are NaN; + * false otherwise + */ + public boolean isInfinite() { + + if (isNaN()) { + return false; + } + + for (double v : data) { + if (Double.isInfinite(v)) { + return true; + } + } + + return false; + + } + + /** + * Test for the equality of two real vectors. + *

+ * If all coordinates of two real vectors are exactly the same, and none are + * Double.NaN, the two real vectors are considered to be equal. + *

+ *

+ * NaN coordinates are considered to affect globally the vector + * and be equals to each other - i.e, if either (or all) coordinates of the + * real vector are equal to Double.NaN, the real vector is equal to + * a vector with all Double.NaN coordinates. + *

+ * + * @param other Object to test for equality to this + * @return true if two 3D vector objects are equal, false if + * object is null, not an instance of Vector3D, or + * not equal to this Vector3D instance + * + */ + public boolean equals(Object other) { + + if (this == other) { + return true; + } + + if (other == null) { + return false; + } + + try { + + RealVector rhs = (RealVector) other; + if (data.length != rhs.getDimension()) { + return false; + } + + if (rhs.isNaN()) { + return this.isNaN(); + } + + for (int i = 0; i < data.length; ++i) { + if (data[i] != rhs.getEntry(i)) { + return false; + } + } + return true; + + } catch (ClassCastException ex) { + // ignore exception + return false; + } + + } + + /** + * Get a hashCode for the real vector. + *

All NaN values have the same hash code.

+ * @return a hash code value for this object + */ + public int hashCode() { + if (isNaN()) { + return 9; + } + return MathUtils.hash(data); + } + + /** + * Check if an index is valid. + * @param index index to check + * @exception MatrixIndexException if index is not valid + */ + private void checkIndex(final int index) + throws MatrixIndexException { + if (index < 0 || index >= getDimension()) { + throw new MatrixIndexException("index {0} out of allowed range [{1}, {2}]", + new Object[] { index, 0, getDimension() - 1}); + } + } + +} diff --git a/src/test/org/apache/commons/math/stat/descriptive/SummaryStatisticsImplTest.java b/src/java/org/apache/commons/math/linear/SingularMatrixException.java similarity index 54% rename from src/test/org/apache/commons/math/stat/descriptive/SummaryStatisticsImplTest.java rename to src/java/org/apache/commons/math/linear/SingularMatrixException.java index 04503cf78..e1c160e99 100644 --- a/src/test/org/apache/commons/math/stat/descriptive/SummaryStatisticsImplTest.java +++ b/src/java/org/apache/commons/math/linear/SingularMatrixException.java @@ -5,39 +5,33 @@ * 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.stat.descriptive; +package org.apache.commons.math.linear; -import junit.framework.Test; -import junit.framework.TestSuite; /** - * Test cases for the {@link SummaryStatisticsImpl} class. - * @deprecated - to be removed in 2.0 with SummaryStatisticsImpl + * Thrown when a matrix is singular. * @version $Revision$ $Date$ + * @since 2.0 */ +public class SingularMatrixException extends InvalidMatrixException { -public final class SummaryStatisticsImplTest extends SummaryStatisticsAbstractTest { + /** Serializable version identifier. */ + private static final long serialVersionUID = -7379143356784298432L; - public SummaryStatisticsImplTest(String name) { - super(name); + /** + * Construct an exception with a default message. + */ + public SingularMatrixException() { + super("matrix is singular", null); } - public static Test suite() { - TestSuite suite = new TestSuite(SummaryStatisticsImplTest.class); - suite.setName("SummaryStatisticsImpl Tests"); - return suite; - } - - protected SummaryStatistics createSummaryStatistics() { - return new SummaryStatisticsImpl(); - } } diff --git a/src/java/org/apache/commons/math/linear/SingularValueDecomposition.java b/src/java/org/apache/commons/math/linear/SingularValueDecomposition.java new file mode 100644 index 000000000..cfb9e1476 --- /dev/null +++ b/src/java/org/apache/commons/math/linear/SingularValueDecomposition.java @@ -0,0 +1,140 @@ +/* + * 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.linear; + +import org.apache.commons.math.ConvergenceException; + +/** + * An interface to classes that implement an algorithm to calculate the + * Singular Value Decomposition of a real matrix. + *

The Singular Value Decomposition of matrix A is a set of three matrices: + * U, Σ and V such that A = U × Σ × VT. + * Let A be an m × n matrix, then U is an m × m orthogonal matrix, + * Σ is a m × n diagonal matrix with positive diagonal elements, + * and V is an n × n orthogonal matrix.

+ *

This interface is similar to the class with similar name from the now defunct + * JAMA library, with the + * following changes:

+ *
    + *
  • solve methods have been added (in the superinterface),
  • + *
  • a {@link DecompositionSolver#decompose(RealMatrix) decompose(RealMatrix)} + * method has been added (in the superinterface),
  • + *
  • a {@link #decompose(RealMatrix, int) decompose(RealMatrix), int)} method + * has been added,
  • + *
  • a {@link DecompositionSolver#isNonSingular() isNonSingular} method has + * been added (in the superinterface),
  • + *
  • a {@link DecompositionSolver#getInverse() getInverse} method has been + * added (in the superinterface),
  • + *
  • the norm2 method which has been renamed as {@link #getNorm() + * getNorm},
  • + *
  • the cond method which has been renamed as {@link + * #getConditionNumber() getConditionNumber},
  • + *
  • the rank method which has been renamed as {@link #getRank() + * getRank}
  • + *
+ * @see MathWorld + * @see Wikipedia + * @version $Revision$ $Date$ + * @since 2.0 + */ +public interface SingularValueDecomposition extends DecompositionSolver { + + /** + * Decompose a matrix to find its largest singular values. + * @param matrix matrix to decompose + * @param maxSingularValues maximal number of singular values to compute + * @exception InvalidMatrixException (wrapping a {@link ConvergenceException} + * if algorithm fails to converge + */ + void decompose(RealMatrix matrix, int maxSingularValues) + throws InvalidMatrixException; + + /** + * Returns the matrix U of the decomposition. + *

U is an orthogonal matrix, i.e. its transpose is also its inverse.

+ * @return the U matrix + * @exception IllegalStateException if neither {@link + * DecompositionSolver#decompose(RealMatrix) decompose} nor {@link + * #decompose(RealMatrix, int)} have not been called + */ + RealMatrix getU() throws IllegalStateException; + + /** + * Returns the diagonal matrix Σ of the decomposition. + *

Σ is a diagonal matrix.

+ * @return the Σ matrix + * @exception IllegalStateException if neither {@link + * DecompositionSolver#decompose(RealMatrix) decompose} nor {@link + * #decompose(RealMatrix, int)} have not been called + */ + RealMatrix getS() throws IllegalStateException; + + /** + * Returns the diagonal elements of the matrix Σ of the decomposition. + * @return the diagonal elements of the Σ matrix + * @exception IllegalStateException if neither {@link + * DecompositionSolver#decompose(RealMatrix) decompose} nor {@link + * #decompose(RealMatrix, int)} have not been called + */ + double[] getSingularValues() throws IllegalStateException; + + /** + * Returns the matrix V of the decomposition. + *

V is an orthogonal matrix, i.e. its transpose is also its inverse.

+ * @return the V matrix (or null if decomposed matrix is singular) + * @exception IllegalStateException if neither {@link + * DecompositionSolver#decompose(RealMatrix) decompose} nor {@link + * #decompose(RealMatrix, int)} have not been called + */ + RealMatrix getV() throws IllegalStateException; + + /** + * Returns the L2 norm of the matrix. + *

The L2 norm is max(|A × u|2 / + * |u|2), where |.|2 denotes the vectorial 2-norm + * (i.e. the traditional euclidian norm).

+ * @return norm + * @exception IllegalStateException if neither {@link + * DecompositionSolver#decompose(RealMatrix) decompose} nor {@link + * #decompose(RealMatrix, int)} have not been called + */ + double getNorm() throws IllegalStateException; + + /** + * Return the condition number of the matrix. + * @return condition number of the matrix + * @exception IllegalStateException if neither {@link + * DecompositionSolver#decompose(RealMatrix) decompose} nor {@link + * #decompose(RealMatrix, int)} have not been called + */ + double getConditionNumber() throws IllegalStateException; + + /** + * Return the effective numerical matrix rank. + *

The effective numerical rank is the number of non-negligible + * singular values. The threshold used to identify non-negligible + * terms is max(m,n) × ulp(s1) where ulp(s1) + * is the least significant bit of the largest singular value.

+ * @return effective numerical matrix rank + * @exception IllegalStateException if neither {@link + * DecompositionSolver#decompose(RealMatrix) decompose} nor {@link + * #decompose(RealMatrix, int)} have not been called + */ + int getRank() throws IllegalStateException; + +} diff --git a/src/java/org/apache/commons/math/linear/TriDiagonalTransformer.java b/src/java/org/apache/commons/math/linear/TriDiagonalTransformer.java new file mode 100644 index 000000000..a4e5262d9 --- /dev/null +++ b/src/java/org/apache/commons/math/linear/TriDiagonalTransformer.java @@ -0,0 +1,280 @@ +/* + * 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.linear; + +import java.io.Serializable; +import java.util.Arrays; + +/** + * Class transforming a symmetrical matrix to tridiagonal shape. + *

A symmetrical m × m matrix A can be written as the product of three matrices: + * A = Q × T × QT with Q an orthogonal matrix and T a symmetrical + * tridiagonal matrix. Both Q and T are m × m matrices.

+ *

This implementation only uses the upper part of the matrix, the part below the + * diagonal is not accessed at all.

+ *

Transformation to tridiagonal shape is often not a goal by itself, but it is + * an intermediate step in more general decomposition algorithms like {@link + * EigenDecomposition eigen decomposition}. This class is therefore intended for internal + * use by the library and is not public. As a consequence of this explicitly limited scope, + * many methods directly returns references to internal arrays, not copies.

+ * @version $Revision$ $Date$ + * @since 2.0 + */ +class TriDiagonalTransformer implements Serializable { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 8935390784125343332L; + + /** Householder vectors. */ + private final double householderVectors[][]; + + /** Main diagonal. */ + private final double[] main; + + /** Secondary diagonal. */ + private final double[] secondary; + + /** Cached value of Q. */ + private RealMatrix cachedQ; + + /** Cached value of Qt. */ + private RealMatrix cachedQt; + + /** Cached value of T. */ + private RealMatrix cachedT; + + /** + * Build the transformation to tridiagonal shape of a symmetrical matrix. + *

The specified matrix is assumed to be symmetrical without any check. + * Only the upper triangular part of the matrix is used.

+ * @param matrix the symmetrical matrix to transform. + * @exception InvalidMatrixException if matrix is not square + */ + public TriDiagonalTransformer(RealMatrix matrix) + throws InvalidMatrixException { + if (!matrix.isSquare()) { + throw new NonSquareMatrixException(matrix.getRowDimension(), matrix.getColumnDimension()); + } + + final int m = matrix.getRowDimension(); + householderVectors = matrix.getData(); + main = new double[m]; + secondary = new double[m - 1]; + cachedQ = null; + cachedQt = null; + cachedT = null; + + // transform matrix + transform(); + + } + + /** + * Returns the matrix Q of the transform. + *

Q is an orthogonal matrix, i.e. its transpose is also its inverse.

+ * @return the Q matrix + */ + public RealMatrix getQ() { + if (cachedQ == null) { + cachedQ = getQT().transpose(); + } + return cachedQ; + } + + /** + * Returns the transpose of the matrix Q of the transform. + *

Q is an orthogonal matrix, i.e. its transpose is also its inverse.

+ * @return the Q matrix + */ + public RealMatrix getQT() { + + if (cachedQt == null) { + + final int m = householderVectors.length; + final double[][] qtData = new double[m][m]; + + // build up first part of the matrix by applying Householder transforms + for (int k = m - 1; k >= 1; --k) { + final double[] hK = householderVectors[k - 1]; + final double inv = 1.0 / (secondary[k - 1] * hK[k]); + qtData[k][k] = 1; + if (hK[k] != 0.0) { + final double[] qtK = qtData[k]; + double beta = 1.0 / secondary[k - 1]; + qtK[k] = 1 + beta * hK[k]; + for (int i = k + 1; i < m; ++i) { + qtK[i] = beta * hK[i]; + } + for (int j = k + 1; j < m; ++j) { + final double[] qtJ = qtData[j]; + beta = 0; + for (int i = k + 1; i < m; ++i) { + beta += qtJ[i] * hK[i]; + } + beta *= inv; + qtJ[k] = beta * hK[k]; + for (int i = k + 1; i < m; ++i) { + qtJ[i] += beta * hK[i]; + } + } + } + } + qtData[0][0] = 1; + + // cache the matrix for subsequent calls + cachedQt = new RealMatrixImpl(qtData, false); + + } + + // return the cached matrix + return cachedQt; + + } + + /** + * Returns the tridiagonal matrix T of the transform. + * @return the T matrix + */ + public RealMatrix getT() { + + if (cachedT == null) { + + final int m = main.length; + double[][] tData = new double[m][m]; + for (int i = 0; i < m; ++i) { + double[] tDataI = tData[i]; + tDataI[i] = main[i]; + if (i > 0) { + tDataI[i - 1] = secondary[i - 1]; + } + if (i < main.length - 1) { + tDataI[i + 1] = secondary[i]; + } + } + + // cache the matrix for subsequent calls + cachedT = new RealMatrixImpl(tData, false); + + } + + // return the cached matrix + return cachedT; + + } + + /** + * Get the Householder vectors of the transform. + *

Note that since this class is only intended for internal use, + * it returns directly a reference to its internal arrays, not a copy.

+ * @return the main diagonal elements of the B matrix + */ + double[][] getHouseholderVectorsRef() { + return householderVectors; + } + + /** + * Get the main diagonal elements of the matrix T of the transform. + *

Note that since this class is only intended for internal use, + * it returns directly a reference to its internal arrays, not a copy.

+ * @return the main diagonal elements of the T matrix + */ + double[] getMainDiagonalRef() { + return main; + } + + /** + * Get the secondary diagonal elements of the matrix T of the transform. + *

Note that since this class is only intended for internal use, + * it returns directly a reference to its internal arrays, not a copy.

+ * @return the secondary diagonal elements of the T matrix + */ + double[] getSecondaryDiagonalRef() { + return secondary; + } + + /** + * Transform original matrix to tridiagonal form. + *

Transformation is done using Householder transforms.

+ */ + private void transform() { + + final int m = householderVectors.length; + final double[] z = new double[m]; + for (int k = 0; k < m - 1; k++) { + + //zero-out a row and a column simultaneously + final double[] hK = householderVectors[k]; + main[k] = hK[k]; + double xNormSqr = 0; + for (int j = k + 1; j < m; ++j) { + final double c = hK[j]; + xNormSqr += c * c; + } + final double a = (hK[k + 1] > 0) ? -Math.sqrt(xNormSqr) : Math.sqrt(xNormSqr); + secondary[k] = a; + if (a != 0.0) { + // apply Householder transform from left and right simultaneously + + hK[k + 1] -= a; + final double beta = -1 / (a * hK[k + 1]); + + // compute a = beta A v, where v is the Householder vector + // this loop is written in such a way + // 1) only the upper triangular part of the matrix is accessed + // 2) access is cache-friendly for a matrix stored in rows + Arrays.fill(z, k + 1, m, 0); + for (int i = k + 1; i < m; ++i) { + final double[] hI = householderVectors[i]; + final double hKI = hK[i]; + double zI = hI[i] * hKI; + for (int j = i + 1; j < m; ++j) { + final double hIJ = hI[j]; + zI += hIJ * hK[j]; + z[j] += hIJ * hKI; + } + z[i] = beta * (z[i] + zI); + } + + // compute gamma = beta vT z / 2 + double gamma = 0; + for (int i = k + 1; i < m; ++i) { + gamma += z[i] * hK[i]; + } + gamma *= beta / 2; + + // compute z = z - gamma v + for (int i = k + 1; i < m; ++i) { + z[i] -= gamma * hK[i]; + } + + // update matrix: A = A - v zT - z vT + // only the upper triangular part of the matrix is updated + for (int i = k + 1; i < m; ++i) { + final double[] hI = householderVectors[i]; + for (int j = i; j < m; ++j) { + hI[j] -= hK[i] * z[j] + z[i] * hK[j]; + } + } + + } + + } + main[m - 1] = householderVectors[m - 1][m - 1]; + } + +} diff --git a/src/java/org/apache/commons/math/ode/AbstractIntegrator.java b/src/java/org/apache/commons/math/ode/AbstractIntegrator.java new file mode 100644 index 000000000..e8e36c0fa --- /dev/null +++ b/src/java/org/apache/commons/math/ode/AbstractIntegrator.java @@ -0,0 +1,225 @@ +/* + * 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.ode; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import org.apache.commons.math.ode.events.CombinedEventsManager; +import org.apache.commons.math.ode.events.EventHandler; +import org.apache.commons.math.ode.events.EventState; +import org.apache.commons.math.ode.sampling.StepHandler; + +/** + * Base class managing common boilerplate for all integrators. + * @version $Revision$ $Date$ + * @since 2.0 + */ +public abstract class AbstractIntegrator implements FirstOrderIntegrator { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 5771479854166853494L; + + /** Name of the method. */ + private final String name; + + /** Step handler. */ + protected Collection stepHandlers; + + /** Current step start time. */ + protected double stepStart; + + /** Current stepsize. */ + protected double stepSize; + + /** Events handlers manager. */ + protected CombinedEventsManager eventsHandlersManager; + + /** Build an instance. + * @param name name of the method + */ + public AbstractIntegrator(final String name) { + this.name = name; + stepHandlers = new ArrayList(); + stepStart = Double.NaN; + stepSize = Double.NaN; + eventsHandlersManager = new CombinedEventsManager(); + } + + /** {@inheritDoc} */ + public String getName() { + return name; + } + + /** {@inheritDoc} */ + public void addStepHandler(final StepHandler handler) { + stepHandlers.add(handler); + } + + /** {@inheritDoc} */ + public Collection getStepHandlers() { + return Collections.unmodifiableCollection(stepHandlers); + } + + /** {@inheritDoc} */ + public void clearStepHandlers() { + stepHandlers.clear(); + } + + /** {@inheritDoc} */ + public void addEventHandler(final EventHandler function, + final double maxCheckInterval, + final double convergence, + final int maxIterationCount) { + eventsHandlersManager.addEventHandler(function, maxCheckInterval, + convergence, maxIterationCount); + } + + /** {@inheritDoc} */ + public Collection getEventHandlers() { + return eventsHandlersManager.getEventsHandlers(); + } + + /** {@inheritDoc} */ + public void clearEventHandlers() { + eventsHandlersManager.clearEventsHandlers(); + } + + /** Check if one of the step handlers requires dense output. + * @return true if one of the step handlers requires dense output + */ + protected boolean requiresDenseOutput() { + for (StepHandler handler : stepHandlers) { + if (handler.requiresDenseOutput()) { + return true; + } + } + return false; + } + + /** {@inheritDoc} */ + public double getCurrentStepStart() { + return stepStart; + } + + /** {@inheritDoc} */ + public double getCurrentSignedStepsize() { + return stepSize; + } + + /** Perform some sanity checks on the integration parameters. + * @param equations differential equations set + * @param t0 start time + * @param y0 state vector at t0 + * @param t target time for the integration + * @param y placeholder where to put the state vector + * @exception IntegratorException if some inconsistency is detected + */ + protected void sanityChecks(final FirstOrderDifferentialEquations equations, + final double t0, final double[] y0, + final double t, final double[] y) + throws IntegratorException { + + if (equations.getDimension() != y0.length) { + throw new IntegratorException("dimensions mismatch: ODE problem has dimension {0}," + + " initial state vector has dimension {1}", + new Object[] { + Integer.valueOf(equations.getDimension()), + Integer.valueOf(y0.length) + }); + } + + if (equations.getDimension() != y.length) { + throw new IntegratorException("dimensions mismatch: ODE problem has dimension {0}," + + " final state vector has dimension {1}", + new Object[] { + Integer.valueOf(equations.getDimension()), + Integer.valueOf(y.length) + }); + } + + if (Math.abs(t - t0) <= 1.0e-12 * Math.max(Math.abs(t0), Math.abs(t))) { + throw new IntegratorException("too small integration interval: length = {0}", + new Object[] { + Double.valueOf(Math.abs(t - t0)) + }); + } + + } + + /** Add an event handler for end time checking. + *

This method can be used to simplify handling of integration end time. + * It leverages the nominal stop condition with the exceptional stop + * conditions.

+ * @param startTime integration start time + * @param endTime desired end time + * @param manager manager containing the user-defined handlers + * @return a new manager containing all the user-defined handlers plus a + * dedicated manager triggering a stop event at entTime + */ + protected CombinedEventsManager addEndTimeChecker(final double startTime, + final double endTime, + final CombinedEventsManager manager) { + CombinedEventsManager newManager = new CombinedEventsManager(); + for (final EventState state : manager.getEventsStates()) { + newManager.addEventHandler(state.getEventHandler(), + state.getMaxCheckInterval(), + state.getConvergence(), + state.getMaxIterationCount()); + } + newManager.addEventHandler(new EndTimeChecker(endTime), + Double.POSITIVE_INFINITY, + Math.ulp(Math.max(Math.abs(startTime), Math.abs(endTime))), + 100); + return newManager; + } + + /** Specialized event handler to stop integration. */ + private static class EndTimeChecker implements EventHandler { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -5211782540446301964L; + + /** DEsiredt end time. */ + private final double endTime; + + /** Build an instance. + * @param endTime desired time + */ + public EndTimeChecker(final double endTime) { + this.endTime = endTime; + } + + /** {@inheritDoc} */ + public int eventOccurred(double t, double[] y) { + return STOP; + } + + /** {@inheritDoc} */ + public double g(double t, double[] y) { + return t - endTime; + } + + /** {@inheritDoc} */ + public void resetState(double t, double[] y) { + } + + } + +} \ No newline at end of file diff --git a/src/java/org/apache/commons/math/ode/ClassicalRungeKuttaStepInterpolator.java b/src/java/org/apache/commons/math/ode/ClassicalRungeKuttaStepInterpolator.java deleted file mode 100644 index b1dec9ab8..000000000 --- a/src/java/org/apache/commons/math/ode/ClassicalRungeKuttaStepInterpolator.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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.ode; - -/** - * This class implements a step interpolator for the classical fourth - * order Runge-Kutta integrator. - * - *

This interpolator allows to compute dense output inside the last - * step computed. The interpolation equation is consistent with the - * integration scheme : - - *

- *   y(t_n + theta h) = y (t_n + h)
- *                    + (1 - theta) (h/6) [ (-4 theta^2 + 5 theta - 1) y'_1
- *                                          +(4 theta^2 - 2 theta - 2) (y'_2 + y'_3)
- *                                          -(4 theta^2 +   theta + 1) y'_4
- *                                        ]
- * 
- * - * where theta belongs to [0 ; 1] and where y'_1 to y'_4 are the four - * evaluations of the derivatives already computed during the - * step.

- * - * @see ClassicalRungeKuttaIntegrator - * @version $Revision$ $Date$ - * @since 1.2 - */ - -class ClassicalRungeKuttaStepInterpolator - extends RungeKuttaStepInterpolator { - - /** Simple constructor. - * This constructor builds an instance that is not usable yet, the - * {@link RungeKuttaStepInterpolator#reinitialize} method should be - * called before using the instance in order to initialize the - * internal arrays. This constructor is used only in order to delay - * the initialization in some cases. The {@link RungeKuttaIntegrator} - * class uses the prototyping design pattern to create the step - * interpolators by cloning an uninitialized model and latter initializing - * the copy. - */ - public ClassicalRungeKuttaStepInterpolator() { - } - - /** Copy constructor. - * @param interpolator interpolator to copy from. The copy is a deep - * copy: its arrays are separated from the original arrays of the - * instance - */ - public ClassicalRungeKuttaStepInterpolator(ClassicalRungeKuttaStepInterpolator interpolator) { - super(interpolator); - } - - /** Really copy the finalized instance. - * @return a copy of the finalized instance - */ - protected StepInterpolator doCopy() { - return new ClassicalRungeKuttaStepInterpolator(this); - } - - /** Compute the state at the interpolated time. - * This is the main processing method that should be implemented by - * the derived classes to perform the interpolation. - * @param theta normalized interpolation abscissa within the step - * (theta is zero at the previous time step and one at the current time step) - * @param oneMinusThetaH time gap between the interpolated time and - * the current time - * @throws DerivativeException this exception is propagated to the caller if the - * underlying user function triggers one - */ - protected void computeInterpolatedState(double theta, - double oneMinusThetaH) - throws DerivativeException { - - double fourTheta = 4 * theta; - double s = oneMinusThetaH / 6.0; - double coeff1 = s * ((-fourTheta + 5) * theta - 1); - double coeff23 = s * (( fourTheta - 2) * theta - 2); - double coeff4 = s * ((-fourTheta - 1) * theta - 1); - - for (int i = 0; i < interpolatedState.length; ++i) { - interpolatedState[i] = currentState[i] + - coeff1 * yDotK[0][i] + - coeff23 * (yDotK[1][i] + yDotK[2][i]) + - coeff4 * yDotK[3][i]; - } - - } - - /** Serializable version identifier */ - private static final long serialVersionUID = -6576285612589783992L; - -} diff --git a/src/java/org/apache/commons/math/ode/ContinuousOutputModel.java b/src/java/org/apache/commons/math/ode/ContinuousOutputModel.java index 7fa22fec4..5c41f1c25 100644 --- a/src/java/org/apache/commons/math/ode/ContinuousOutputModel.java +++ b/src/java/org/apache/commons/math/ode/ContinuousOutputModel.java @@ -18,10 +18,14 @@ package org.apache.commons.math.ode; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.io.Serializable; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.ode.nonstiff.AdaptiveStepsizeIntegrator; +import org.apache.commons.math.ode.sampling.StepHandler; +import org.apache.commons.math.ode.sampling.StepInterpolator; + /** * This class stores all information provided by an ODE integrator * during the integration process and build a continuous model of the @@ -45,14 +49,14 @@ import java.io.Serializable; * get the model value at any time or to navigate through the * data).

* - *

If problem modelization is done with several separate + *

If problem modeling is done with several separate * integration phases for contiguous intervals, the same * ContinuousOutputModel can be used as step handler for all * integration phases as long as they are performed in order and in * the same direction. As an example, one can extrapolate the * trajectory of a satellite with one model (i.e. one set of * differential equations) up to the beginning of a maneuver, use - * another more complex model including thrusters modelization and + * another more complex model including thrusters modeling and * accurate attitude control during the maneuver, and revert to the * first model after the end of the maneuver. If the same continuous * output model handles the steps of all integration phases, the user @@ -86,7 +90,7 @@ public class ContinuousOutputModel * Build an empty continuous output model. */ public ContinuousOutputModel() { - steps = new ArrayList(); + steps = new ArrayList(); reset(); } @@ -98,7 +102,7 @@ public class ContinuousOutputModel * compatible with the instance (dimension of the state vector, * propagation direction, hole between the dates) */ - public void append(ContinuousOutputModel model) + public void append(final ContinuousOutputModel model) throws DerivativeException { if (model.steps.size() == 0) { @@ -118,19 +122,19 @@ public class ContinuousOutputModel throw new IllegalArgumentException("propagation direction mismatch"); } - StepInterpolator lastInterpolator = (StepInterpolator) steps.get(index); - double current = lastInterpolator.getCurrentTime(); - double previous = lastInterpolator.getPreviousTime(); - double step = current - previous; - double gap = model.getInitialTime() - current; + final StepInterpolator lastInterpolator = (StepInterpolator) steps.get(index); + final double current = lastInterpolator.getCurrentTime(); + final double previous = lastInterpolator.getPreviousTime(); + final double step = current - previous; + final double gap = model.getInitialTime() - current; if (Math.abs(gap) > 1.0e-3 * Math.abs(step)) { throw new IllegalArgumentException("hole between time ranges"); } } - for (Iterator iter = model.steps.iterator(); iter.hasNext(); ) { - steps.add(((AbstractStepInterpolator) iter.next()).copy()); + for (StepInterpolator interpolator : model.steps) { + steps.add(interpolator.copy()); } index = steps.size() - 1; @@ -168,20 +172,18 @@ public class ContinuousOutputModel * @throws DerivativeException this exception is propagated to the * caller if the underlying user function triggers one */ - public void handleStep(StepInterpolator interpolator, boolean isLast) + public void handleStep(final StepInterpolator interpolator, final boolean isLast) throws DerivativeException { - AbstractStepInterpolator ai = (AbstractStepInterpolator) interpolator; - if (steps.size() == 0) { initialTime = interpolator.getPreviousTime(); forward = interpolator.isForward(); } - steps.add(ai.copy()); + steps.add(interpolator.copy()); if (isLast) { - finalTime = ai.getCurrentTime(); + finalTime = interpolator.getCurrentTime(); index = steps.size() - 1; } @@ -210,7 +212,7 @@ public class ContinuousOutputModel * @return interpolation point time */ public double getInterpolatedTime() { - return ((StepInterpolator) steps.get(index)).getInterpolatedTime(); + return steps.get(index).getInterpolatedTime(); } /** Set the time of the interpolated point. @@ -225,16 +227,16 @@ public class ContinuousOutputModel * near the interval endpoints.

* @param time time of the interpolated point */ - public void setInterpolatedTime(double time) { + public void setInterpolatedTime(final double time) { try { // initialize the search with the complete steps table int iMin = 0; - StepInterpolator sMin = (StepInterpolator) steps.get(iMin); + final StepInterpolator sMin = steps.get(iMin); double tMin = 0.5 * (sMin.getPreviousTime() + sMin.getCurrentTime()); int iMax = steps.size() - 1; - StepInterpolator sMax = (StepInterpolator) steps.get(iMax); + final StepInterpolator sMax = steps.get(iMax); double tMax = 0.5 * (sMax.getPreviousTime() + sMax.getCurrentTime()); // handle points outside of the integration interval @@ -254,8 +256,8 @@ public class ContinuousOutputModel while (iMax - iMin > 5) { // use the last estimated index as the splitting index - StepInterpolator si = (StepInterpolator) steps.get(index); - int location = locatePoint(time, si); + final StepInterpolator si = steps.get(index); + final int location = locatePoint(time, si); if (location < 0) { iMax = index; tMax = 0.5 * (si.getPreviousTime() + si.getCurrentTime()); @@ -269,9 +271,9 @@ public class ContinuousOutputModel } // compute a new estimate of the index in the reduced table slice - int iMed = (iMin + iMax) / 2; - StepInterpolator sMed = (StepInterpolator) steps.get(iMed); - double tMed = 0.5 * (sMed.getPreviousTime() + sMed.getCurrentTime()); + final int iMed = (iMin + iMax) / 2; + final StepInterpolator sMed = steps.get(iMed); + final double tMed = 0.5 * (sMed.getPreviousTime() + sMed.getCurrentTime()); if ((Math.abs(tMed - tMin) < 1e-6) || (Math.abs(tMax - tMed) < 1e-6)) { // too close to the bounds, we estimate using a simple dichotomy @@ -280,22 +282,22 @@ public class ContinuousOutputModel // estimate the index using a reverse quadratic polynom // (reverse means we have i = P(t), thus allowing to simply // compute index = P(time) rather than solving a quadratic equation) - double d12 = tMax - tMed; - double d23 = tMed - tMin; - double d13 = tMax - tMin; - double dt1 = time - tMax; - double dt2 = time - tMed; - double dt3 = time - tMin; - double iLagrange = ((dt2 * dt3 * d23) * iMax - - (dt1 * dt3 * d13) * iMed + - (dt1 * dt2 * d12) * iMin) / - (d12 * d23 * d13); + final double d12 = tMax - tMed; + final double d23 = tMed - tMin; + final double d13 = tMax - tMin; + final double dt1 = time - tMax; + final double dt2 = time - tMed; + final double dt3 = time - tMin; + final double iLagrange = ((dt2 * dt3 * d23) * iMax - + (dt1 * dt3 * d13) * iMed + + (dt1 * dt2 * d12) * iMin) / + (d12 * d23 * d13); index = (int) Math.rint(iLagrange); } // force the next size reduction to be at least one tenth - int low = Math.max(iMin + 1, (9 * iMin + iMax) / 10); - int high = Math.min(iMax - 1, (iMin + 9 * iMax) / 10); + final int low = Math.max(iMin + 1, (9 * iMin + iMax) / 10); + final int high = Math.min(iMax - 1, (iMin + 9 * iMax) / 10); if (index < low) { index = low; } else if (index > high) { @@ -306,18 +308,14 @@ public class ContinuousOutputModel // now the table slice is very small, we perform an iterative search index = iMin; - while ((index <= iMax) && - (locatePoint(time, (StepInterpolator) steps.get(index)) > 0)) { + while ((index <= iMax) && (locatePoint(time, steps.get(index)) > 0)) { ++index; } - StepInterpolator si = (StepInterpolator) steps.get(index); - - si.setInterpolatedTime(time); + steps.get(index).setInterpolatedTime(time); } catch (DerivativeException de) { - throw new RuntimeException("unexpected DerivativeException caught: " + - de.getMessage()); + throw new MathRuntimeException("unexpected exception caught", null, de); } } @@ -327,7 +325,7 @@ public class ContinuousOutputModel * @return state vector at time {@link #getInterpolatedTime} */ public double[] getInterpolatedState() { - return ((StepInterpolator) steps.get(index)).getInterpolatedState(); + return steps.get(index).getInterpolatedState(); } /** Compare a step interval and a double. @@ -337,7 +335,7 @@ public class ContinuousOutputModel * the interval, and +1 if it is after the interval, according to * the interval direction */ - private int locatePoint(double time, StepInterpolator interval) { + private int locatePoint(final double time, final StepInterpolator interval) { if (forward) { if (time < interval.getPreviousTime()) { return -1; @@ -369,9 +367,9 @@ public class ContinuousOutputModel private int index; /** Steps table. */ - private List steps; + private List steps; /** Serializable version identifier */ - private static final long serialVersionUID = 2259286184268533249L; + private static final long serialVersionUID = -1417964919405031606L; } diff --git a/src/java/org/apache/commons/math/ode/DerivativeException.java b/src/java/org/apache/commons/math/ode/DerivativeException.java index 4233b3c34..841286f59 100644 --- a/src/java/org/apache/commons/math/ode/DerivativeException.java +++ b/src/java/org/apache/commons/math/ode/DerivativeException.java @@ -21,7 +21,7 @@ import org.apache.commons.math.MathException; /** * This exception is made available to users to report - * the error conditions that are trigegred while computing + * the error conditions that are triggered while computing * the differential equations. * @version $Revision$ $Date$ * @since 1.2 @@ -34,14 +34,14 @@ public class DerivativeException * @param specifier format specifier (to be translated) * @param parts to insert in the format (no translation) */ - public DerivativeException(String specifier, String[] parts) { + public DerivativeException(final String specifier, final Object[] parts) { super(specifier, parts); } /** Build an instance from an underlying cause. * @param cause cause for the exception */ - public DerivativeException(Throwable cause) { + public DerivativeException(final Throwable cause) { super(cause); } diff --git a/src/java/org/apache/commons/math/ode/FirstOrderConverter.java b/src/java/org/apache/commons/math/ode/FirstOrderConverter.java index ef6ae31c4..3e9295afa 100644 --- a/src/java/org/apache/commons/math/ode/FirstOrderConverter.java +++ b/src/java/org/apache/commons/math/ode/FirstOrderConverter.java @@ -56,11 +56,14 @@ package org.apache.commons.math.ode; public class FirstOrderConverter implements FirstOrderDifferentialEquations { + /** Serializable version identifier. */ + private static final long serialVersionUID = -8233657110042144146L; + /** Simple constructor. * Build a converter around a second order equations set. * @param equations second order equations set to convert */ - public FirstOrderConverter (SecondOrderDifferentialEquations equations) { + public FirstOrderConverter (final SecondOrderDifferentialEquations equations) { this.equations = equations; dimension = equations.getDimension(); z = new double[dimension]; @@ -84,8 +87,8 @@ public class FirstOrderConverter * @throws DerivativeException this exception is propagated to the caller if the * underlying user function triggers one */ - public void computeDerivatives(double t, double[] y, double[] yDot) - throws DerivativeException { + public void computeDerivatives(final double t, final double[] y, final double[] yDot) + throws DerivativeException { // split the state vector in two System.arraycopy(y, 0, z, 0, dimension); diff --git a/src/java/org/apache/commons/math/ode/FirstOrderDifferentialEquations.java b/src/java/org/apache/commons/math/ode/FirstOrderDifferentialEquations.java index 91e6a3dee..b26824763 100644 --- a/src/java/org/apache/commons/math/ode/FirstOrderDifferentialEquations.java +++ b/src/java/org/apache/commons/math/ode/FirstOrderDifferentialEquations.java @@ -17,6 +17,8 @@ package org.apache.commons.math.ode; +import java.io.Serializable; + /** This interface represents a first order differential equations set. * *

This interface should be implemented by all real first order @@ -44,7 +46,7 @@ package org.apache.commons.math.ode; * @since 1.2 */ -public interface FirstOrderDifferentialEquations { +public interface FirstOrderDifferentialEquations extends Serializable { /** Get the dimension of the problem. * @return dimension of the problem diff --git a/src/java/org/apache/commons/math/ode/FirstOrderIntegrator.java b/src/java/org/apache/commons/math/ode/FirstOrderIntegrator.java index b931d2288..dc72978f0 100644 --- a/src/java/org/apache/commons/math/ode/FirstOrderIntegrator.java +++ b/src/java/org/apache/commons/math/ode/FirstOrderIntegrator.java @@ -17,6 +17,9 @@ package org.apache.commons.math.ode; +import org.apache.commons.math.ode.events.EventHandler; +import org.apache.commons.math.ode.sampling.StepHandler; + /** This interface represents a first order integrator for * differential equations. @@ -27,43 +30,12 @@ package org.apache.commons.math.ode; * * @see FirstOrderDifferentialEquations * @see StepHandler - * @see SwitchingFunction + * @see EventHandler * @version $Revision$ $Date$ * @since 1.2 */ -public interface FirstOrderIntegrator { - - /** Get the name of the method. - * @return name of the method - */ - public String getName(); - - /** Set the step handler for this integrator. - * The handler will be called by the integrator for each accepted - * step. - * @param handler handler for the accepted steps - */ - public void setStepHandler (StepHandler handler); - - /** Get the step handler for this integrator. - * @return the step handler for this integrator - */ - public StepHandler getStepHandler(); - - /** Add a switching function to the integrator. - * @param function switching function - * @param maxCheckInterval maximal time interval between switching - * function checks (this interval prevents missing sign changes in - * case the integration steps becomes very large) - * @param convergence convergence threshold in the event time search - * @param maxIterationCount upper limit of the iteration count in - * the event time search - */ - public void addSwitchingFunction(SwitchingFunction function, - double maxCheckInterval, - double convergence, - int maxIterationCount); +public interface FirstOrderIntegrator extends ODEIntegrator { /** Integrate the differential equations up to the given time. *

This method solves an Initial Value Problem (IVP).

@@ -77,35 +49,15 @@ public interface FirstOrderIntegrator { * (can be set to a value smaller than t0 for backward integration) * @param y placeholder where to put the state vector at each successful * step (and hence at the end of integration), can be the same object as y0 + * @return stop time, will be the same as target time if integration reached its + * target, but may be different if some {@link EventHandler} stops it at some point. * @throws IntegratorException if the integrator cannot perform integration * @throws DerivativeException this exception is propagated to the caller if * the underlying user function triggers one */ - public void integrate (FirstOrderDifferentialEquations equations, - double t0, double[] y0, - double t, double[] y) + public double integrate (FirstOrderDifferentialEquations equations, + double t0, double[] y0, + double t, double[] y) throws DerivativeException, IntegratorException; - /** Get the current value of the step start time ti. - *

This method can be called during integration (typically by - * the object implementing the {@link FirstOrderDifferentialEquations - * differential equations} problem) if the value of the current step that - * is attempted is needed.

- *

The result is undefined if the method is called outside of - * calls to {@link #integrate}

- * @return current value of the step start time ti - */ - public double getCurrentStepStart(); - - /** Get the current signed value of the integration stepsize. - *

This method can be called during integration (typically by - * the object implementing the {@link FirstOrderDifferentialEquations - * differential equations} problem) if the signed value of the current stepsize - * that is tried is needed.

- *

The result is undefined if the method is called outside of - * calls to {@link #integrate}

- * @return current signed value of the stepsize - */ - public double getCurrentSignedStepsize(); - } diff --git a/src/java/org/apache/commons/math/ode/IntegratorException.java b/src/java/org/apache/commons/math/ode/IntegratorException.java index 683eef1b0..d917d1938 100644 --- a/src/java/org/apache/commons/math/ode/IntegratorException.java +++ b/src/java/org/apache/commons/math/ode/IntegratorException.java @@ -33,7 +33,7 @@ public class IntegratorException * @param specifier format specifier (to be translated) * @param parts to insert in the format (no translation) */ - public IntegratorException(String specifier, Object[] parts) { + public IntegratorException(final String specifier, final Object[] parts) { super(specifier, parts); } @@ -41,7 +41,7 @@ public class IntegratorException * Create an exception with a given root cause. * @param cause the exception or error that caused this exception to be thrown */ - public IntegratorException(Throwable cause) { + public IntegratorException(final Throwable cause) { super(cause); } diff --git a/src/java/org/apache/commons/math/ode/ODEIntegrator.java b/src/java/org/apache/commons/math/ode/ODEIntegrator.java new file mode 100644 index 000000000..a1fe60d5a --- /dev/null +++ b/src/java/org/apache/commons/math/ode/ODEIntegrator.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.ode; + +import java.io.Serializable; +import java.util.Collection; + +import org.apache.commons.math.ode.events.EventHandler; +import org.apache.commons.math.ode.sampling.StepHandler; + +/** + * This interface defines the common parts shared by integrators + * for first and second order differential equations. + * @see FirstOrderIntegrator + * @see SecondOrderIntegrator + * @version $Revision$ $Date$ + * @since 2.0 + */ +public interface ODEIntegrator extends Serializable { + + /** Get the name of the method. + * @return name of the method + */ + String getName(); + + /** Add a step handler to this integrator. + *

The handler will be called by the integrator for each accepted + * step.

+ * @param handler handler for the accepted steps + * @see #getStepHandlers() + * @see #clearStepHandlers() + * @since 2.0 + */ + void addStepHandler(StepHandler handler); + + /** Get all the step handlers that have been added to the integrator. + * @return an unmodifiable collection of the added events handlers + * @see #addStepHandler(StepHandler) + * @see #clearStepHandlers() + * @since 2.0 + */ + Collection getStepHandlers(); + + /** Remove all the step handlers that have been added to the integrator. + * @see #addStepHandler(StepHandler) + * @see #getStepHandlers() + * @since 2.0 + */ + void clearStepHandlers(); + + /** Add an event handler to the integrator. + * @param handler event handler + * @param maxCheckInterval maximal time interval between switching + * function checks (this interval prevents missing sign changes in + * case the integration steps becomes very large) + * @param convergence convergence threshold in the event time search + * @param maxIterationCount upper limit of the iteration count in + * the event time search + * @see #getEventHandlers() + * @see #clearEventHandlers() + */ + void addEventHandler(EventHandler handler, + double maxCheckInterval, + double convergence, + int maxIterationCount); + + /** Get all the event handlers that have been added to the integrator. + * @return an unmodifiable collection of the added events handlers + * @see #addEventHandler(EventHandler, double, double, int) + * @see #clearEventHandlers() + */ + Collection getEventHandlers(); + + /** Remove all the event handlers that have been added to the integrator. + * @see #addEventHandler(EventHandler, double, double, int) + * @see #getEventHandlers() + */ + void clearEventHandlers(); + + /** Get the current value of the step start time ti. + *

This method can be called during integration (typically by + * the object implementing the {@link FirstOrderDifferentialEquations + * differential equations} problem) if the value of the current step that + * is attempted is needed.

+ *

The result is undefined if the method is called outside of + * calls to {@link #integrate}

+ * @return current value of the step start time ti + */ + double getCurrentStepStart(); + + /** Get the current signed value of the integration stepsize. + *

This method can be called during integration (typically by + * the object implementing the {@link FirstOrderDifferentialEquations + * differential equations} problem) if the signed value of the current stepsize + * that is tried is needed.

+ *

The result is undefined if the method is called outside of + * calls to {@link #integrate}

+ * @return current signed value of the stepsize + */ + double getCurrentSignedStepsize(); + +} diff --git a/src/java/org/apache/commons/math/ode/RungeKuttaIntegrator.java b/src/java/org/apache/commons/math/ode/RungeKuttaIntegrator.java deleted file mode 100644 index ebd21404b..000000000 --- a/src/java/org/apache/commons/math/ode/RungeKuttaIntegrator.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * 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.ode; - -/** - * This class implements the common part of all fixed step Runge-Kutta - * integrators for Ordinary Differential Equations. - * - *

These methods are explicit Runge-Kutta methods, their Butcher - * arrays are as follows : - *

- *    0  |
- *   c2  | a21
- *   c3  | a31  a32
- *   ... |        ...
- *   cs  | as1  as2  ...  ass-1
- *       |--------------------------
- *       |  b1   b2  ...   bs-1  bs
- * 
- *

- * - * @see EulerIntegrator - * @see ClassicalRungeKuttaIntegrator - * @see GillIntegrator - * @see MidpointIntegrator - * @version $Revision$ $Date$ - * @since 1.2 - */ - -public abstract class RungeKuttaIntegrator - implements FirstOrderIntegrator { - - /** Simple constructor. - * Build a Runge-Kutta integrator with the given - * step. The default step handler does nothing. - * @param c time steps from Butcher array (without the first zero) - * @param a internal weights from Butcher array (without the first empty row) - * @param b propagation weights for the high order method from Butcher array - * @param prototype prototype of the step interpolator to use - * @param step integration step - */ - protected RungeKuttaIntegrator(double[] c, double[][] a, double[] b, - RungeKuttaStepInterpolator prototype, - double step) { - this.c = c; - this.a = a; - this.b = b; - this.prototype = prototype; - this.step = step; - handler = DummyStepHandler.getInstance(); - switchesHandler = new SwitchingFunctionsHandler(); - resetInternalState(); - } - - /** Get the name of the method. - * @return name of the method - */ - public abstract String getName(); - - /** Set the step handler for this integrator. - * The handler will be called by the integrator for each accepted - * step. - * @param handler handler for the accepted steps - */ - public void setStepHandler (StepHandler handler) { - this.handler = handler; - } - - /** Get the step handler for this integrator. - * @return the step handler for this integrator - */ - public StepHandler getStepHandler() { - return handler; - } - - /** Add a switching function to the integrator. - * @param function switching function - * @param maxCheckInterval maximal time interval between switching - * function checks (this interval prevents missing sign changes in - * case the integration steps becomes very large) - * @param convergence convergence threshold in the event time search - * @param maxIterationCount upper limit of the iteration count in - * the event time search - */ - public void addSwitchingFunction(SwitchingFunction function, - double maxCheckInterval, - double convergence, - int maxIterationCount) { - switchesHandler.add(function, maxCheckInterval, convergence, maxIterationCount); - } - - /** Perform some sanity checks on the integration parameters. - * @param equations differential equations set - * @param t0 start time - * @param y0 state vector at t0 - * @param t target time for the integration - * @param y placeholder where to put the state vector - * @exception IntegratorException if some inconsistency is detected - */ - private void sanityChecks(FirstOrderDifferentialEquations equations, - double t0, double[] y0, double t, double[] y) - throws IntegratorException { - if (equations.getDimension() != y0.length) { - throw new IntegratorException("dimensions mismatch: ODE problem has dimension {0}," + - " initial state vector has dimension {1}", - new Object[] { - new Integer(equations.getDimension()), - new Integer(y0.length) - }); - } - if (equations.getDimension() != y.length) { - throw new IntegratorException("dimensions mismatch: ODE problem has dimension {0}," + - " final state vector has dimension {1}", - new Object[] { - new Integer(equations.getDimension()), - new Integer(y.length) - }); - } - if (Math.abs(t - t0) <= 1.0e-12 * Math.max(Math.abs(t0), Math.abs(t))) { - throw new IntegratorException("too small integration interval: length = {0}", - new Object[] { new Double(Math.abs(t - t0)) }); - } - } - - /** Integrate the differential equations up to the given time. - *

This method solves an Initial Value Problem (IVP).

- *

Since this method stores some internal state variables made - * available in its public interface during integration ({@link - * #getCurrentSignedStepsize()}), it is not thread-safe.

- * @param equations differential equations to integrate - * @param t0 initial time - * @param y0 initial value of the state vector at t0 - * @param t target time for the integration - * (can be set to a value smaller than t0 for backward integration) - * @param y placeholder where to put the state vector at each successful - * step (and hence at the end of integration), can be the same object as y0 - * @throws IntegratorException if the integrator cannot perform integration - * @throws DerivativeException this exception is propagated to the caller if - * the underlying user function triggers one - */ - public void integrate(FirstOrderDifferentialEquations equations, - double t0, double[] y0, - double t, double[] y) - throws DerivativeException, IntegratorException { - - sanityChecks(equations, t0, y0, t, y); - boolean forward = (t > t0); - - // create some internal working arrays - int stages = c.length + 1; - if (y != y0) { - System.arraycopy(y0, 0, y, 0, y0.length); - } - double[][] yDotK = new double[stages][]; - for (int i = 0; i < stages; ++i) { - yDotK [i] = new double[y0.length]; - } - double[] yTmp = new double[y0.length]; - - // set up an interpolator sharing the integrator arrays - AbstractStepInterpolator interpolator; - if (handler.requiresDenseOutput() || (! switchesHandler.isEmpty())) { - RungeKuttaStepInterpolator rki = (RungeKuttaStepInterpolator) prototype.copy(); - rki.reinitialize(equations, yTmp, yDotK, forward); - interpolator = rki; - } else { - interpolator = new DummyStepInterpolator(yTmp, forward); - } - interpolator.storeTime(t0); - - // recompute the step - long nbStep = Math.max(1l, Math.abs(Math.round((t - t0) / step))); - boolean lastStep = false; - stepStart = t0; - stepSize = (t - t0) / nbStep; - handler.reset(); - for (long i = 0; ! lastStep; ++i) { - - interpolator.shift(); - - boolean needUpdate = false; - for (boolean loop = true; loop;) { - - // first stage - equations.computeDerivatives(stepStart, y, yDotK[0]); - - // next stages - for (int k = 1; k < stages; ++k) { - - for (int j = 0; j < y0.length; ++j) { - double sum = a[k-1][0] * yDotK[0][j]; - for (int l = 1; l < k; ++l) { - sum += a[k-1][l] * yDotK[l][j]; - } - yTmp[j] = y[j] + stepSize * sum; - } - - equations.computeDerivatives(stepStart + c[k-1] * stepSize, yTmp, yDotK[k]); - - } - - // estimate the state at the end of the step - for (int j = 0; j < y0.length; ++j) { - double sum = b[0] * yDotK[0][j]; - for (int l = 1; l < stages; ++l) { - sum += b[l] * yDotK[l][j]; - } - yTmp[j] = y[j] + stepSize * sum; - } - - // Switching functions handling - interpolator.storeTime(stepStart + stepSize); - if (switchesHandler.evaluateStep(interpolator)) { - needUpdate = true; - stepSize = switchesHandler.getEventTime() - stepStart; - } else { - loop = false; - } - - } - - // the step has been accepted - double nextStep = stepStart + stepSize; - System.arraycopy(yTmp, 0, y, 0, y0.length); - switchesHandler.stepAccepted(nextStep, y); - if (switchesHandler.stop()) { - lastStep = true; - } else { - lastStep = (i == (nbStep - 1)); - } - - // provide the step data to the step handler - interpolator.storeTime(nextStep); - handler.handleStep(interpolator, lastStep); - stepStart = nextStep; - - if (switchesHandler.reset(stepStart, y) && ! lastStep) { - // some switching function has triggered changes that - // invalidate the derivatives, we need to recompute them - equations.computeDerivatives(stepStart, y, yDotK[0]); - } - - if (needUpdate) { - // a switching function has changed the step - // we need to recompute stepsize - nbStep = Math.max(1l, Math.abs(Math.round((t - stepStart) / step))); - stepSize = (t - stepStart) / nbStep; - i = -1; - } - - } - - resetInternalState(); - - } - - /** Get the current value of the step start time ti. - *

This method can be called during integration (typically by - * the object implementing the {@link FirstOrderDifferentialEquations - * differential equations} problem) if the value of the current step that - * is attempted is needed.

- *

The result is undefined if the method is called outside of - * calls to {@link #integrate}

- * @return current value of the step start time ti - */ - public double getCurrentStepStart() { - return stepStart; - } - - /** Get the current signed value of the integration stepsize. - *

This method can be called during integration (typically by - * the object implementing the {@link FirstOrderDifferentialEquations - * differential equations} problem) if the signed value of the current stepsize - * that is tried is needed.

- *

The result is undefined if the method is called outside of - * calls to {@link #integrate}

- * @return current signed value of the stepsize - */ - public double getCurrentSignedStepsize() { - return stepSize; - } - - /** Reset internal state to dummy values. */ - private void resetInternalState() { - stepStart = Double.NaN; - stepSize = Double.NaN; - } - - /** Time steps from Butcher array (without the first zero). */ - private double[] c; - - /** Internal weights from Butcher array (without the first empty row). */ - private double[][] a; - - /** External weights for the high order method from Butcher array. */ - private double[] b; - - /** Prototype of the step interpolator. */ - private RungeKuttaStepInterpolator prototype; - - /** Integration step. */ - private double step; - - /** Step handler. */ - private StepHandler handler; - - /** Switching functions handler. */ - protected SwitchingFunctionsHandler switchesHandler; - - /** Current step start time. */ - private double stepStart; - - /** Current stepsize. */ - private double stepSize; - -} diff --git a/src/java/org/apache/commons/math/ode/SecondOrderIntegrator.java b/src/java/org/apache/commons/math/ode/SecondOrderIntegrator.java index 9a6fb2b7e..e23ca8acc 100644 --- a/src/java/org/apache/commons/math/ode/SecondOrderIntegrator.java +++ b/src/java/org/apache/commons/math/ode/SecondOrderIntegrator.java @@ -17,6 +17,7 @@ package org.apache.commons.math.ode; + /** This interface represents a second order integrator for * differential equations. * @@ -30,24 +31,7 @@ package org.apache.commons.math.ode; * @since 1.2 */ -public interface SecondOrderIntegrator { - - /** Get the name of the method. - * @return name of the method - */ - public String getName(); - - /** Set the step handler for this integrator. - * The handler will be called by the integrator for each accepted - * step. - * @param handler handler for the accepted steps - */ - public void setStepHandler (StepHandler handler); - - /** Get the step handler for this integrator. - * @return the step handler for this integrator - */ - public StepHandler getStepHandler(); +public interface SecondOrderIntegrator extends ODEIntegrator { /** Integrate the differential equations up to the given time * @param equations differential equations to integrate diff --git a/src/java/org/apache/commons/math/ode/StepNormalizer.java b/src/java/org/apache/commons/math/ode/StepNormalizer.java deleted file mode 100644 index 7c21b6e0b..000000000 --- a/src/java/org/apache/commons/math/ode/StepNormalizer.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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.ode; - -/** - * This class wraps an object implementing {@link FixedStepHandler} - * into a {@link StepHandler}. - - *

This wrapper allows to use fixed step handlers with general - * integrators which cannot guaranty their integration steps will - * remain constant and therefore only accept general step - * handlers.

- * - *

The stepsize used is selected at construction time. The {@link - * FixedStepHandler#handleStep handleStep} method of the underlying - * {@link FixedStepHandler} object is called at the beginning time of - * the integration t0 and also at times t0+h, t0+2h, ... If the - * integration range is an integer multiple of the stepsize, then the - * last point handled will be the endpoint of the integration tend, if - * not, the last point will belong to the interval [tend - h ; - * tend].

- * - *

There is no constraint on the integrator, it can use any - * timestep it needs (time steps longer or shorter than the fixed time - * step and non-integer ratios are all allowed).

- * - * @see StepHandler - * @see FixedStepHandler - * @version $Revision$ $Date$ - * @since 1.2 - */ - -public class StepNormalizer - implements StepHandler { - - /** Simple constructor. - * @param h fixed time step (sign is not used) - * @param handler fixed time step handler to wrap - */ - public StepNormalizer(double h, FixedStepHandler handler) { - this.h = Math.abs(h); - this.handler = handler; - reset(); - } - - /** Determines whether this handler needs dense output. - * This handler needs dense output in order to provide data at - * regularly spaced steps regardless of the steps the integrator - * uses, so this method always returns true. - * @return always true - */ - public boolean requiresDenseOutput() { - return true; - } - - /** Reset the step handler. - * Initialize the internal data as required before the first step is - * handled. - */ - public void reset() { - lastTime = Double.NaN; - lastState = null; - forward = true; - } - - /** - * Handle the last accepted step - * @param interpolator interpolator for the last accepted step. For - * efficiency purposes, the various integrators reuse the same - * object on each call, so if the instance wants to keep it across - * all calls (for example to provide at the end of the integration a - * continuous model valid throughout the integration range), it - * should build a local copy using the clone method and store this - * copy. - * @param isLast true if the step is the last one - * @throws DerivativeException this exception is propagated to the - * caller if the underlying user function triggers one - */ - public void handleStep(StepInterpolator interpolator, boolean isLast) - throws DerivativeException { - - double nextTime; - - if (lastState == null) { - - lastTime = interpolator.getPreviousTime(); - interpolator.setInterpolatedTime(lastTime); - - double[] state = interpolator.getInterpolatedState(); - lastState = (double[]) state.clone(); - - // take the integration direction into account - forward = (interpolator.getCurrentTime() >= lastTime); - if (! forward) { - h = -h; - } - - } - - nextTime = lastTime + h; - boolean nextInStep = forward ^ (nextTime > interpolator.getCurrentTime()); - while (nextInStep) { - - // output the stored previous step - handler.handleStep(lastTime, lastState, false); - - // store the next step - lastTime = nextTime; - interpolator.setInterpolatedTime(lastTime); - System.arraycopy(interpolator.getInterpolatedState(), 0, - lastState, 0, lastState.length); - - nextTime += h; - nextInStep = forward ^ (nextTime > interpolator.getCurrentTime()); - - } - - if (isLast) { - // there will be no more steps, - // the stored one should be flagged as being the last - handler.handleStep(lastTime, lastState, true); - } - - } - - /** Fixed time step. */ - private double h; - - /** Underlying step handler. */ - private FixedStepHandler handler; - - /** Last step time. */ - private double lastTime; - - /** Last State vector. */ - private double[] lastState; - - /** Integration direction indicator. */ - private boolean forward; - -} diff --git a/src/java/org/apache/commons/math/ode/SwitchState.java b/src/java/org/apache/commons/math/ode/SwitchState.java deleted file mode 100644 index d729d365d..000000000 --- a/src/java/org/apache/commons/math/ode/SwitchState.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * 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.ode; - -import java.io.Serializable; - -import org.apache.commons.math.ConvergenceException; -import org.apache.commons.math.FunctionEvaluationException; -import org.apache.commons.math.analysis.BrentSolver; -import org.apache.commons.math.analysis.UnivariateRealFunction; -import org.apache.commons.math.analysis.UnivariateRealSolver; - -/** This class handles the state for one {@link SwitchingFunction - * switching function} during integration steps. - * - *

Each time the integrator proposes a step, the switching function - * should be checked. This class handles the state of one function - * during one integration step, with references to the state at the - * end of the preceding step. This information is used to determine if - * the function should trigger an event or not during the proposed - * step (and hence the step should be reduced to ensure the event - * occurs at a bound rather than inside the step).

- * - * @version $Revision$ $Date$ - * @since 1.2 - */ -class SwitchState implements Serializable { - - /** Serializable version identifier. */ - private static final long serialVersionUID = -7307007422156119622L; - - /** Switching function. */ - private SwitchingFunction function; - - /** Maximal time interval between switching function checks. */ - private double maxCheckInterval; - - /** Convergence threshold for event localisation. */ - private double convergence; - - /** Upper limit in the iteration count for event localisation. */ - private int maxIterationCount; - - /** Time at the beginning of the step. */ - private double t0; - - /** Value of the switching function at the beginning of the step. */ - private double g0; - - /** Simulated sign of g0 (we cheat when crossing events). */ - private boolean g0Positive; - - /** Indicator of event expected during the step. */ - private boolean pendingEvent; - - /** Occurrence time of the pending event. */ - private double pendingEventTime; - - /** Occurrence time of the previous event. */ - private double previousEventTime; - - /** Variation direction around pending event. - * (this is considered with respect to the integration direction) - */ - private boolean increasing; - - /** Next action indicator. */ - private int nextAction; - - /** Simple constructor. - * @param function switching function - * @param maxCheckInterval maximal time interval between switching - * function checks (this interval prevents missing sign changes in - * case the integration steps becomes very large) - * @param convergence convergence threshold in the event time search - * @param maxIterationCount upper limit of the iteration count in - * the event time search - */ - public SwitchState(SwitchingFunction function, double maxCheckInterval, - double convergence, int maxIterationCount) { - this.function = function; - this.maxCheckInterval = maxCheckInterval; - this.convergence = Math.abs(convergence); - this.maxIterationCount = maxIterationCount; - - // some dummy values ... - t0 = Double.NaN; - g0 = Double.NaN; - g0Positive = true; - pendingEvent = false; - pendingEventTime = Double.NaN; - previousEventTime = Double.NaN; - increasing = true; - nextAction = SwitchingFunction.CONTINUE; - - } - - /** Reinitialize the beginning of the step. - * @param t0 value of the independent time variable at the - * beginning of the step - * @param y0 array containing the current value of the state vector - * at the beginning of the step - * @exception FunctionEvaluationException if the switching function - * value cannot be evaluated at the beginning of the step - */ - public void reinitializeBegin(double t0, double[] y0) - throws FunctionEvaluationException { - this.t0 = t0; - g0 = function.g(t0, y0); - g0Positive = (g0 >= 0); - } - - /** Evaluate the impact of the proposed step on the switching function. - * @param interpolator step interpolator for the proposed step - * @return true if the switching function triggers an event before - * the end of the proposed step (this implies the step should be - * rejected) - * @exception DerivativeException if the interpolator fails to - * compute the function somewhere within the step - * @exception FunctionEvaluationException if the switching function - * cannot be evaluated - * @exception ConvergenceException if an event cannot be located - */ - public boolean evaluateStep(final StepInterpolator interpolator) - throws DerivativeException, FunctionEvaluationException, ConvergenceException { - - try { - - double t1 = interpolator.getCurrentTime(); - int n = Math.max(1, (int) Math.ceil(Math.abs(t1 - t0) / maxCheckInterval)); - double h = (t1 - t0) / n; - - double ta = t0; - double ga = g0; - double tb = t0 + ((t1 > t0) ? convergence : -convergence); - for (int i = 0; i < n; ++i) { - - // evaluate function value at the end of the substep - tb += h; - interpolator.setInterpolatedTime(tb); - double gb = function.g(tb, interpolator.getInterpolatedState()); - - // check events occurrence - if (g0Positive ^ (gb >= 0)) { - // there is a sign change: an event is expected during this step - - // variation direction, with respect to the integration direction - increasing = (gb >= ga); - - UnivariateRealSolver solver = new BrentSolver(new UnivariateRealFunction() { - public double value(double t) throws FunctionEvaluationException { - try { - interpolator.setInterpolatedTime(t); - return function.g(t, interpolator.getInterpolatedState()); - } catch (DerivativeException e) { - throw new FunctionEvaluationException(t, e); - } - } - }); - solver.setAbsoluteAccuracy(convergence); - solver.setMaximalIterationCount(maxIterationCount); - double root = solver.solve(ta, tb); - if (Double.isNaN(previousEventTime) || (Math.abs(previousEventTime - root) > convergence)) { - pendingEventTime = root; - if (pendingEvent && (Math.abs(t1 - pendingEventTime) <= convergence)) { - // we were already waiting for this event which was - // found during a previous call for a step that was - // rejected, this step must now be accepted since it - // properly ends exactly at the event occurrence - return false; - } - // either we were not waiting for the event or it has - // moved in such a way the step cannot be accepted - pendingEvent = true; - return true; - } - - } else { - // no sign change: there is no event for now - ta = tb; - ga = gb; - } - - } - - // no event during the whole step - pendingEvent = false; - pendingEventTime = Double.NaN; - return false; - - } catch (FunctionEvaluationException e) { - Throwable cause = e.getCause(); - if ((cause != null) && (cause instanceof DerivativeException)) { - throw (DerivativeException) cause; - } - throw e; - } - - } - - /** Get the occurrence time of the event triggered in the current - * step. - * @return occurrence time of the event triggered in the current - * step. - */ - public double getEventTime() { - return pendingEventTime; - } - - /** Acknowledge the fact the step has been accepted by the integrator. - * @param t value of the independent time variable at the - * end of the step - * @param y array containing the current value of the state vector - * at the end of the step - * @exception FunctionEvaluationException if the value of the switching - * function cannot be evaluated - */ - public void stepAccepted(double t, double[] y) - throws FunctionEvaluationException { - - t0 = t; - g0 = function.g(t, y); - - if (pendingEvent) { - // force the sign to its value "just after the event" - previousEventTime = t; - g0Positive = increasing; - nextAction = function.eventOccurred(t, y); - } else { - g0Positive = (g0 >= 0); - nextAction = SwitchingFunction.CONTINUE; - } - } - - /** Check if the integration should be stopped at the end of the - * current step. - * @return true if the integration should be stopped - */ - public boolean stop() { - return nextAction == SwitchingFunction.STOP; - } - - /** Let the switching function reset the state if it wants. - * @param t value of the independent time variable at the - * beginning of the next step - * @param y array were to put the desired state vector at the beginning - * of the next step - * @return true if the integrator should reset the derivatives too - */ - public boolean reset(double t, double[] y) { - - if (! pendingEvent) { - return false; - } - - if (nextAction == SwitchingFunction.RESET_STATE) { - function.resetState(t, y); - } - pendingEvent = false; - pendingEventTime = Double.NaN; - - return (nextAction == SwitchingFunction.RESET_STATE) || - (nextAction == SwitchingFunction.RESET_DERIVATIVES); - - } - -} diff --git a/src/java/org/apache/commons/math/ode/SwitchingFunctionsHandler.java b/src/java/org/apache/commons/math/ode/SwitchingFunctionsHandler.java deleted file mode 100644 index 3f2509a3f..000000000 --- a/src/java/org/apache/commons/math/ode/SwitchingFunctionsHandler.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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.ode; - -import org.apache.commons.math.ConvergenceException; -import org.apache.commons.math.FunctionEvaluationException; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** This class handles several {@link SwitchingFunction switching - * functions} during integration. - * - * @see SwitchingFunction - * @version $Revision$ $Date$ - * @since 1.2 - */ - -public class SwitchingFunctionsHandler { - - /** Simple constructor. - * Create an empty handler - */ - public SwitchingFunctionsHandler() { - functions = new ArrayList(); - first = null; - initialized = false; - } - - /** Add a switching function. - * @param function switching function - * @param maxCheckInterval maximal time interval between switching - * function checks (this interval prevents missing sign changes in - * case the integration steps becomes very large) - * @param convergence convergence threshold in the event time search - * @param maxIterationCount upper limit of the iteration count in - * the event time search - */ - public void add(SwitchingFunction function, double maxCheckInterval, - double convergence, int maxIterationCount) { - functions.add(new SwitchState(function, maxCheckInterval, - convergence, maxIterationCount)); - } - - /** Check if the handler does not have any condition. - * @return true if handler is empty - */ - public boolean isEmpty() { - return functions.isEmpty(); - } - - /** Evaluate the impact of the proposed step on all handled - * switching functions. - * @param interpolator step interpolator for the proposed step - * @return true if at least one switching function triggers an event - * before the end of the proposed step (this implies the step should - * be rejected) - * @exception DerivativeException if the interpolator fails to - * compute the function somewhere within the step - * @exception IntegratorException if an event cannot be located - */ - public boolean evaluateStep(StepInterpolator interpolator) - throws DerivativeException, IntegratorException { - - try { - - first = null; - if (functions.isEmpty()) { - // there is nothing to do, return now to avoid setting the - // interpolator time (and hence avoid unneeded calls to the - // user function due to interpolator finalization) - return false; - } - - if (! initialized) { - - // initialize the switching functions - double t0 = interpolator.getPreviousTime(); - interpolator.setInterpolatedTime(t0); - double [] y = interpolator.getInterpolatedState(); - for (Iterator iter = functions.iterator(); iter.hasNext();) { - ((SwitchState) iter.next()).reinitializeBegin(t0, y); - } - - initialized = true; - - } - - // check events occurrence - for (Iterator iter = functions.iterator(); iter.hasNext();) { - - SwitchState state = (SwitchState) iter.next(); - if (state.evaluateStep(interpolator)) { - if (first == null) { - first = state; - } else { - if (interpolator.isForward()) { - if (state.getEventTime() < first.getEventTime()) { - first = state; - } - } else { - if (state.getEventTime() > first.getEventTime()) { - first = state; - } - } - } - } - - } - - return first != null; - - } catch (FunctionEvaluationException fee) { - throw new IntegratorException(fee); - } catch (ConvergenceException ce) { - throw new IntegratorException(ce); - } - - } - - /** Get the occurrence time of the first event triggered in the - * last evaluated step. - * @return occurrence time of the first event triggered in the last - * evaluated step, or
Double.NaN
if no event is - * triggered - */ - public double getEventTime() { - return (first == null) ? Double.NaN : first.getEventTime(); - } - - /** Inform the switching functions that the step has been accepted - * by the integrator. - * @param t value of the independent time variable at the - * end of the step - * @param y array containing the current value of the state vector - * at the end of the step - * @exception IntegratorException if the value of one of the - * switching functions cannot be evaluated - */ - public void stepAccepted(double t, double[] y) - throws IntegratorException { - try { - for (Iterator iter = functions.iterator(); iter.hasNext();) { - ((SwitchState) iter.next()).stepAccepted(t, y); - } - } catch (FunctionEvaluationException fee) { - throw new IntegratorException(fee); - } - } - - /** Check if the integration should be stopped at the end of the - * current step. - * @return true if the integration should be stopped - */ - public boolean stop() { - for (Iterator iter = functions.iterator(); iter.hasNext();) { - if (((SwitchState) iter.next()).stop()) { - return true; - } - } - return false; - } - - /** Let the switching functions reset the state if they want. - * @param t value of the independent time variable at the - * beginning of the next step - * @param y array were to put the desired state vector at the beginning - * of the next step - * @return true if the integrator should reset the derivatives too - */ - public boolean reset(double t, double[] y) { - boolean resetDerivatives = false; - for (Iterator iter = functions.iterator(); iter.hasNext();) { - if (((SwitchState) iter.next()).reset(t, y)) { - resetDerivatives = true; - } - } - return resetDerivatives; - } - - /** Switching functions. */ - private List functions; - - /** First active switching function. */ - private SwitchState first; - - /** Initialization indicator. */ - private boolean initialized; - -} diff --git a/src/java/org/apache/commons/math/ode/events/CombinedEventsManager.java b/src/java/org/apache/commons/math/ode/events/CombinedEventsManager.java new file mode 100644 index 000000000..e105d19cd --- /dev/null +++ b/src/java/org/apache/commons/math/ode/events/CombinedEventsManager.java @@ -0,0 +1,251 @@ +/* + * 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.ode.events; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.IntegratorException; +import org.apache.commons.math.ode.sampling.StepInterpolator; + +/** This class manages several {@link EventHandler event handlers} during integration. + * + * @see EventHandler + * @see EventState + * @version $Revision$ $Date$ + * @since 1.2 + */ + +public class CombinedEventsManager implements Serializable { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -4151965151236441261L; + + /** Events states. */ + private final List states; + + /** First active event. */ + private EventState first; + + /** Initialization indicator. */ + private boolean initialized; + + /** Simple constructor. + * Create an empty manager + */ + public CombinedEventsManager() { + states = new ArrayList(); + first = null; + initialized = false; + } + + /** Add an events handler. + * @param handler event handler + * @param maxCheckInterval maximal time interval between events + * checks (this interval prevents missing sign changes in + * case the integration steps becomes very large) + * @param convergence convergence threshold in the event time search + * @param maxIterationCount upper limit of the iteration count in + * the event time search + * @see #getEventsHandlers() + * @see #clearEventsHandlers() + */ + public void addEventHandler(final EventHandler handler, final double maxCheckInterval, + final double convergence, final int maxIterationCount) { + states.add(new EventState(handler, maxCheckInterval, + convergence, maxIterationCount)); + } + + /** Get all the events handlers that have been added to the manager. + * @return an unmodifiable collection of the added event handlers + * @see #addEventHandler(EventHandler, double, double, int) + * @see #clearEventsHandlers() + * @see #getEventsStates() + */ + public Collection getEventsHandlers() { + final List list = new ArrayList(); + for (EventState state : states) { + list.add(state.getEventHandler()); + } + return Collections.unmodifiableCollection(list); + } + + /** Remove all the events handlers that have been added to the manager. + * @see #addEventHandler(EventHandler, double, double, int) + * @see #getEventsHandlers() + */ + public void clearEventsHandlers() { + states.clear(); + } + + /** Get all the events state wrapping the handlers that have been added to the manager. + * @return a collection of the events states + * @see #getEventsHandlers() + */ + public Collection getEventsStates() { + return states; + } + + /** Check if the manager does not manage any event handlers. + * @return true if manager is empty + */ + public boolean isEmpty() { + return states.isEmpty(); + } + + /** Evaluate the impact of the proposed step on all managed + * event handlers. + * @param interpolator step interpolator for the proposed step + * @return true if at least one event handler triggers an event + * before the end of the proposed step (this implies the step should + * be rejected) + * @exception DerivativeException if the interpolator fails to + * compute the function somewhere within the step + * @exception IntegratorException if an event cannot be located + */ + public boolean evaluateStep(final StepInterpolator interpolator) + throws DerivativeException, IntegratorException { + + try { + + first = null; + if (states.isEmpty()) { + // there is nothing to do, return now to avoid setting the + // interpolator time (and hence avoid unneeded calls to the + // user function due to interpolator finalization) + return false; + } + + if (! initialized) { + + // initialize the events states + final double t0 = interpolator.getPreviousTime(); + interpolator.setInterpolatedTime(t0); + final double [] y = interpolator.getInterpolatedState(); + for (EventState state : states) { + state.reinitializeBegin(t0, y); + } + + initialized = true; + + } + + // check events occurrence + for (EventState state : states) { + + if (state.evaluateStep(interpolator)) { + if (first == null) { + first = state; + } else { + if (interpolator.isForward()) { + if (state.getEventTime() < first.getEventTime()) { + first = state; + } + } else { + if (state.getEventTime() > first.getEventTime()) { + first = state; + } + } + } + } + + } + + return first != null; + + } catch (EventException se) { + throw new IntegratorException(se); + } catch (ConvergenceException ce) { + throw new IntegratorException(ce); + } + + } + + /** Get the occurrence time of the first event triggered in the + * last evaluated step. + * @return occurrence time of the first event triggered in the last + * evaluated step, or Double.NaN if no event is + * triggered + */ + public double getEventTime() { + return (first == null) ? Double.NaN : first.getEventTime(); + } + + /** Inform the event handlers that the step has been accepted + * by the integrator. + * @param t value of the independent time variable at the + * end of the step + * @param y array containing the current value of the state vector + * at the end of the step + * @exception IntegratorException if the value of one of the + * events states cannot be evaluated + */ + public void stepAccepted(final double t, final double[] y) + throws IntegratorException { + try { + for (EventState state : states) { + state.stepAccepted(t, y); + } + } catch (EventException se) { + throw new IntegratorException(se); + } + } + + /** Check if the integration should be stopped at the end of the + * current step. + * @return true if the integration should be stopped + */ + public boolean stop() { + for (EventState state : states) { + if (state.stop()) { + return true; + } + } + return false; + } + + /** Let the event handlers reset the state if they want. + * @param t value of the independent time variable at the + * beginning of the next step + * @param y array were to put the desired state vector at the beginning + * of the next step + * @return true if the integrator should reset the derivatives too + * @exception IntegratorException if one of the events states + * that should reset the state fails to do it + */ + public boolean reset(final double t, final double[] y) + throws IntegratorException { + try { + boolean resetDerivatives = false; + for (EventState state : states) { + if (state.reset(t, y)) { + resetDerivatives = true; + } + } + return resetDerivatives; + } catch (EventException se) { + throw new IntegratorException(se); + } + } + +} diff --git a/src/java/org/apache/commons/math/ode/events/EventException.java b/src/java/org/apache/commons/math/ode/events/EventException.java new file mode 100644 index 000000000..b5f636024 --- /dev/null +++ b/src/java/org/apache/commons/math/ode/events/EventException.java @@ -0,0 +1,50 @@ +/* + * 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.ode.events; + +import org.apache.commons.math.MathException; + +/** + * This exception is made available to users to report + * the error conditions that are triggered by {@link EventHandler} + * @version $Revision$ $Date$ + * @since 2.0 + */ +public class EventException extends MathException { + + /** Serialization UID. */ + private static final long serialVersionUID = -898215297400035290L; + + /** Simple constructor. + * Build an exception by translating and formating a message + * @param specifier format specifier (to be translated) + * @param parts to insert in the format (no translation) + */ + public EventException(final String specifier, final Object[] parts) { + super(specifier, parts); + } + + /** + * Create an exception with a given root cause. + * @param cause the exception or error that caused this exception to be thrown + */ + public EventException(final Throwable cause) { + super(cause); + } + +} diff --git a/src/java/org/apache/commons/math/ode/SwitchingFunction.java b/src/java/org/apache/commons/math/ode/events/EventHandler.java similarity index 76% rename from src/java/org/apache/commons/math/ode/SwitchingFunction.java rename to src/java/org/apache/commons/math/ode/events/EventHandler.java index 06ef10b9b..01cf094d2 100644 --- a/src/java/org/apache/commons/math/ode/SwitchingFunction.java +++ b/src/java/org/apache/commons/math/ode/events/EventHandler.java @@ -15,22 +15,26 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.events; import java.io.Serializable; -import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.ode.FirstOrderDifferentialEquations; +import org.apache.commons.math.ode.sampling.StepHandler; -/** This interface represents a switching function. +/** This interface represents a handler for discrete events triggered + * during ODE integration. * - *

A switching function allows to handle discrete events in - * integration problems. These events occur for example when the - * integration process should be stopped as some value is reached - * (G-stop facility), or when the derivatives have + *

Some events can be triggered at discrete times as an ODE problem + * is solved. These occurs for example when the integration process + * should be stopped as some state is reached (G-stop facility) when the + * precise date is unknown a priori, or when the derivatives have * discontinuities, or simply when the user wants to monitor some - * states boundaries crossings. These events are traditionally defined - * as occurring when a g function sign changes, hence - * the name switching functions.

+ * states boundaries crossings. + *

+ * + *

These events are defined as occurring when a g + * switching function sign changes.

* *

Since events are only problem-dependent and are triggered by the * independent time variable and the state vector, they can @@ -41,14 +45,14 @@ import org.apache.commons.math.FunctionEvaluationException; * step interpolation (which always has a one step scope) is relevant * even in presence of discontinuities. This is independent from the * stepsize control provided by integrators that monitor the local - * error (this feature is available on all integrators, including - * fixed step ones).

+ * error (this event handling feature is available for all integrators, + * including fixed step ones).

* * @version $Revision$ $Date$ * @since 1.2 */ -public interface SwitchingFunction extends Serializable { +public interface EventHandler extends Serializable { /** Stop indicator. *

This value should be used as the return value of the {@link @@ -84,19 +88,19 @@ public interface SwitchingFunction extends Serializable { /** Compute the value of the switching function. - *

Discrete events are generated when the sign of this function - * changes, the integrator will take care to change the stepsize in - * such a way these events occur exactly at step boundaries. This - * function must be continuous (at least in its roots neighborhood), - * as the integrator will need to find its roots to locate the events.

+ *

The discrete events are generated when the sign of this + * switching function changes. The integrator will take care to change + * the stepsize in such a way these events occur exactly at step boundaries. + * The switching function must be continuous in its roots neighborhood + * (but not necessarily smooth), as the integrator will need to find its + * roots to locate precisely the events.

* @param t current value of the independent time variable * @param y array containing the current value of the state vector - * @return value of the g function - * @exception FunctionEvaluationException if the value of the function - * cannot be evaluated + * @return value of the g switching function + * @exception EventException if the switching function cannot be evaluated */ - public double g(double t, double[] y) throws FunctionEvaluationException; + public double g(double t, double[] y) throws EventException; /** Handle an event and choose what to do next. @@ -131,8 +135,9 @@ public interface SwitchingFunction extends Serializable { * @return indication of what the integrator should do next, this * value must be one of {@link #STOP}, {@link #RESET_STATE}, * {@link #RESET_DERIVATIVES} or {@link #CONTINUE} + * @exception EventException if the event occurrence triggers an error */ - public int eventOccurred(double t, double[] y); + public int eventOccurred(double t, double[] y) throws EventException; /** Reset the state prior to continue the integration. @@ -148,7 +153,8 @@ public interface SwitchingFunction extends Serializable { * @param t current value of the independent time variable * @param y array containing the current value of the state vector * the new state should be put in the same array + * @exception EventException if the state cannot be reseted */ - public void resetState(double t, double[] y); + public void resetState(double t, double[] y) throws EventException; } diff --git a/src/java/org/apache/commons/math/ode/events/EventState.java b/src/java/org/apache/commons/math/ode/events/EventState.java new file mode 100644 index 000000000..b5fa22856 --- /dev/null +++ b/src/java/org/apache/commons/math/ode/events/EventState.java @@ -0,0 +1,324 @@ +/* + * 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.ode.events; + +import java.io.Serializable; + +import org.apache.commons.math.ConvergenceException; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.analysis.BrentSolver; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.apache.commons.math.analysis.UnivariateRealSolver; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.sampling.StepInterpolator; + +/** This class handles the state for one {@link EventHandler + * event handler} during integration steps. + * + *

Each time the integrator proposes a step, the event handler + * switching function should be checked. This class handles the state + * of one handler during one integration step, with references to the + * state at the end of the preceding step. This information is used to + * decide if the handler should trigger an event or not during the + * proposed step (and hence the step should be reduced to ensure the + * event occurs at a bound rather than inside the step).

+ * + * @version $Revision$ $Date$ + * @since 1.2 + */ +public class EventState implements Serializable { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -216176055159247559L; + + /** Event handler. */ + private final EventHandler handler; + + /** Maximal time interval between events handler checks. */ + private final double maxCheckInterval; + + /** Convergence threshold for event localization. */ + private final double convergence; + + /** Upper limit in the iteration count for event localization. */ + private final int maxIterationCount; + + /** Time at the beginning of the step. */ + private double t0; + + /** Value of the events handler at the beginning of the step. */ + private double g0; + + /** Simulated sign of g0 (we cheat when crossing events). */ + private boolean g0Positive; + + /** Indicator of event expected during the step. */ + private boolean pendingEvent; + + /** Occurrence time of the pending event. */ + private double pendingEventTime; + + /** Occurrence time of the previous event. */ + private double previousEventTime; + + /** Variation direction around pending event. + * (this is considered with respect to the integration direction) + */ + private boolean increasing; + + /** Next action indicator. */ + private int nextAction; + + /** Simple constructor. + * @param handler event handler + * @param maxCheckInterval maximal time interval between switching + * function checks (this interval prevents missing sign changes in + * case the integration steps becomes very large) + * @param convergence convergence threshold in the event time search + * @param maxIterationCount upper limit of the iteration count in + * the event time search + */ + public EventState(final EventHandler handler, final double maxCheckInterval, + final double convergence, final int maxIterationCount) { + this.handler = handler; + this.maxCheckInterval = maxCheckInterval; + this.convergence = Math.abs(convergence); + this.maxIterationCount = maxIterationCount; + + // some dummy values ... + t0 = Double.NaN; + g0 = Double.NaN; + g0Positive = true; + pendingEvent = false; + pendingEventTime = Double.NaN; + previousEventTime = Double.NaN; + increasing = true; + nextAction = EventHandler.CONTINUE; + + } + + /** Get the underlying event handler. + * @return underlying event handler + */ + public EventHandler getEventHandler() { + return handler; + } + + /** Get the maximal time interval between events handler checks. + * @return maximal time interval between events handler checks + */ + public double getMaxCheckInterval() { + return maxCheckInterval; + } + + /** Get the convergence threshold for event localization. + * @return convergence threshold for event localization + */ + public double getConvergence() { + return convergence; + } + + /** Get the upper limit in the iteration count for event localization. + * @return upper limit in the iteration count for event localization + */ + public int getMaxIterationCount() { + return maxIterationCount; + } + + /** Reinitialize the beginning of the step. + * @param t0 value of the independent time variable at the + * beginning of the step + * @param y0 array containing the current value of the state vector + * at the beginning of the step + * @exception EventException if the event handler + * value cannot be evaluated at the beginning of the step + */ + public void reinitializeBegin(final double t0, final double[] y0) + throws EventException { + this.t0 = t0; + g0 = handler.g(t0, y0); + g0Positive = (g0 >= 0); + } + + /** Evaluate the impact of the proposed step on the event handler. + * @param interpolator step interpolator for the proposed step + * @return true if the event handler triggers an event before + * the end of the proposed step (this implies the step should be + * rejected) + * @exception DerivativeException if the interpolator fails to + * compute the switching function somewhere within the step + * @exception EventException if the switching function + * cannot be evaluated + * @exception ConvergenceException if an event cannot be located + */ + public boolean evaluateStep(final StepInterpolator interpolator) + throws DerivativeException, EventException, ConvergenceException { + + try { + + final double t1 = interpolator.getCurrentTime(); + final int n = Math.max(1, (int) Math.ceil(Math.abs(t1 - t0) / maxCheckInterval)); + final double h = (t1 - t0) / n; + + double ta = t0; + double ga = g0; + double tb = t0 + (interpolator.isForward() ? convergence : -convergence); + for (int i = 0; i < n; ++i) { + + // evaluate handler value at the end of the substep + tb += h; + interpolator.setInterpolatedTime(tb); + final double gb = handler.g(tb, interpolator.getInterpolatedState()); + + // check events occurrence + if (g0Positive ^ (gb >= 0)) { + // there is a sign change: an event is expected during this step + + // variation direction, with respect to the integration direction + increasing = (gb >= ga); + + final UnivariateRealSolver solver = new BrentSolver(new UnivariateRealFunction() { + public double value(final double t) throws FunctionEvaluationException { + try { + interpolator.setInterpolatedTime(t); + return handler.g(t, interpolator.getInterpolatedState()); + } catch (DerivativeException e) { + throw new FunctionEvaluationException(t, e); + } catch (EventException e) { + throw new FunctionEvaluationException(t, e); + } + } + }); + solver.setAbsoluteAccuracy(convergence); + solver.setMaximalIterationCount(maxIterationCount); + final double root = (ta <= tb) ? solver.solve(ta, tb) : solver.solve(tb, ta); + if (Math.abs(root - ta) <= convergence) { + // we have found (again ?) a past event, we simply ignore it + ta = tb; + ga = gb; + } else if (Double.isNaN(previousEventTime) || + (Math.abs(previousEventTime - root) > convergence)) { + pendingEventTime = root; + if (pendingEvent && (Math.abs(t1 - pendingEventTime) <= convergence)) { + // we were already waiting for this event which was + // found during a previous call for a step that was + // rejected, this step must now be accepted since it + // properly ends exactly at the event occurrence + return false; + } + // either we were not waiting for the event or it has + // moved in such a way the step cannot be accepted + pendingEvent = true; + return true; + } + + } else { + // no sign change: there is no event for now + ta = tb; + ga = gb; + } + + } + + // no event during the whole step + pendingEvent = false; + pendingEventTime = Double.NaN; + return false; + + } catch (FunctionEvaluationException e) { + final Throwable cause = e.getCause(); + if ((cause != null) && (cause instanceof DerivativeException)) { + throw (DerivativeException) cause; + } else if ((cause != null) && (cause instanceof EventException)) { + throw (EventException) cause; + } + throw new EventException(e); + } + + } + + /** Get the occurrence time of the event triggered in the current + * step. + * @return occurrence time of the event triggered in the current + * step. + */ + public double getEventTime() { + return pendingEventTime; + } + + /** Acknowledge the fact the step has been accepted by the integrator. + * @param t value of the independent time variable at the + * end of the step + * @param y array containing the current value of the state vector + * at the end of the step + * @exception EventException if the value of the event + * handler cannot be evaluated + */ + public void stepAccepted(final double t, final double[] y) + throws EventException { + + t0 = t; + g0 = handler.g(t, y); + + if (pendingEvent) { + // force the sign to its value "just after the event" + previousEventTime = t; + g0Positive = increasing; + nextAction = handler.eventOccurred(t, y); + } else { + g0Positive = (g0 >= 0); + nextAction = EventHandler.CONTINUE; + } + } + + /** Check if the integration should be stopped at the end of the + * current step. + * @return true if the integration should be stopped + */ + public boolean stop() { + return nextAction == EventHandler.STOP; + } + + /** Let the event handler reset the state if it wants. + * @param t value of the independent time variable at the + * beginning of the next step + * @param y array were to put the desired state vector at the beginning + * of the next step + * @return true if the integrator should reset the derivatives too + * @exception EventException if the state cannot be reseted by the event + * handler + */ + public boolean reset(final double t, final double[] y) + throws EventException { + + if (! pendingEvent) { + return false; + } + + if (nextAction == EventHandler.RESET_STATE) { + handler.resetState(t, y); + } + pendingEvent = false; + pendingEventTime = Double.NaN; + + return (nextAction == EventHandler.RESET_STATE) || + (nextAction == EventHandler.RESET_DERIVATIVES); + + } + +} diff --git a/src/java/org/apache/commons/math/ode/events/package.html b/src/java/org/apache/commons/math/ode/events/package.html new file mode 100644 index 000000000..68dcd9797 --- /dev/null +++ b/src/java/org/apache/commons/math/ode/events/package.html @@ -0,0 +1,96 @@ + + + + +

+This package provides classes to handle discrete events occurring during +Ordinary Differential Equations integration. +

+ +

+Discrete events detection is based on switching functions. The user provides +a simple {@link org.apache.commons.math.ode.events.EventHandler#g g(t, y)} +function depending on the current time and state. The integrator will monitor +the value of the function throughout integration range and will trigger the +event when its sign changes. The magnitude of the value is almost irrelevant, +it should however be continuous (but not necessarily smooth) for the sake of +root finding. The steps are shortened as needed to ensure the events occur +at step boundaries (even if the integrator is a fixed-step integrator). +

+ +

+When an event is triggered, several different options are available: +

+
    +
  • integration can be stopped (this is called a G-stop facility),
  • +
  • the state vector or the derivatives can be changed,
  • +
  • or integration can simply go on.
  • +
+ +

+The first case, G-stop, is the most common one. A typical use case is when an +ODE must be solved up to some target state is reached, with a known value of +the state but an unknown occurrence time. As an example, if we want to monitor +a chemical reaction up to some predefined concentration for the first substance, +we can use the following switching function setting: +

+  public double g(double t, double[] y) {
+    return y[0] - targetConcentration;
+  }
+
+  public int eventOccurred(double t, double[] y) {
+    return STOP;
+  }
+
+

+ +

+The second case, change state vector or derivatives is encountered when dealing +with discontinuous dynamical models. A typical case would be the motion of a +spacecraft when thrusters are fired for orbital maneuvers. The acceleration is +smooth as long as no maneuver are performed, depending only on gravity, drag, +third body attraction, radiation pressure. Firing a thruster introduces a +discontinuity that must be handled appropriately by the integrator. In such a case, +we would use a switching function setting similar to this: +

+  public double g(double t, double[] y) {
+    return (t - tManeuverStart) * (t - tManeuverStop);
+  }
+
+  public int eventOccurred(double t, double[] y) {
+    return RESET_DERIVATIVES;
+  }
+
+

+ +

+The third case is useful mainly for monitoring purposes, a simple example is: +

+  public double g(double t, double[] y) {
+    return y[0] - y[1];
+  }
+
+  public int eventOccurred(double t, double[] y) {
+    logger.log("y0(t) and y1(t) curves cross at t = " + t);
+    return CONTINUE;
+  }
+
+

+ + + diff --git a/src/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.java b/src/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.java new file mode 100644 index 000000000..f27f61822 --- /dev/null +++ b/src/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.java @@ -0,0 +1,287 @@ +/* + * 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.ode.nonstiff; + +import org.apache.commons.math.fraction.Fraction; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderDifferentialEquations; +import org.apache.commons.math.ode.IntegratorException; +import org.apache.commons.math.ode.events.CombinedEventsManager; +import org.apache.commons.math.ode.sampling.StepHandler; + + +/** + * This class implements explicit Adams-Bashforth integrators for Ordinary + * Differential Equations. + * + *

Adams-Bashforth (in fact due to Adams alone) methods are explicit + * multistep ODE solvers witch fixed stepsize. The value of state vector + * at step n+1 is a simple combination of the value at step n and of the + * derivatives at steps n, n-1, n-2 ... Depending on the number k of previous + * steps one wants to use for computing the next value, different formulas + * are available:

+ *
    + *
  • k = 1: yn+1 = yn + h fn
  • + *
  • k = 2: yn+1 = yn + h (3fn-fn-1)/2
  • + *
  • k = 3: yn+1 = yn + h (23fn-16fn-1+5fn-2)/12
  • + *
  • k = 4: yn+1 = yn + h (55fn-59fn-1+37fn-2-9fn-3)/24
  • + *
  • ...
  • + *
+ * + *

A k-steps Adams-Bashforth method is of order k. There is no limit to the + * value of k.

+ * + *

These methods are explicit: fn+1 is not used to compute + * yn+1. More accurate implicit Adams methods exist: the + * Adams-Moulton methods (which are also due to Adams alone). They are + * provided by the {@link AdamsMoultonIntegrator AdamsMoultonIntegrator} class.

+ * + * @see AdamsMoultonIntegrator + * @see BDFIntegrator + * @version $Revision$ $Date$ + * @since 2.0 + */ +public class AdamsBashforthIntegrator extends MultistepIntegrator { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 1676381657635800870L; + + /** Integrator method name. */ + private static final String METHOD_NAME = "Adams-Bashforth"; + + /** Coefficients for the current method. */ + private final double[] coeffs; + + /** Integration step. */ + private final double step; + + /** + * Build an Adams-Bashforth integrator with the given order and step size. + * @param order order of the method (must be strictly positive) + * @param step integration step size + */ + public AdamsBashforthIntegrator(final int order, final double step) { + + super(METHOD_NAME, order, new AdamsBashforthStepInterpolator()); + + // compute the integration coefficients + int[][] bdArray = computeBackwardDifferencesArray(order); + Fraction[] gamma = computeGammaArray(order); + coeffs = new double[order]; + for (int i = 0; i < order; ++i) { + Fraction f = Fraction.ZERO; + for (int j = i; j < order; ++j) { + f = f.add(gamma[j].multiply(new Fraction(bdArray[j][i], 1))); + } + coeffs[i] = f.doubleValue(); + } + + this.step = Math.abs(step); + + } + + /** {@inheritDoc} */ + public double integrate(FirstOrderDifferentialEquations equations, + double t0, double[] y0, double t, double[] y) + throws DerivativeException, IntegratorException { + + sanityChecks(equations, t0, y0, t, y); + final boolean forward = (t > t0); + + // initialize working arrays + if (y != y0) { + System.arraycopy(y0, 0, y, 0, y0.length); + } + final double[] yTmp = new double[y0.length]; + + // set up an interpolator sharing the integrator arrays + final AdamsBashforthStepInterpolator interpolator = + (AdamsBashforthStepInterpolator) prototype.copy(); + interpolator.reinitialize(yTmp, previousT, previousF, forward); + + // set up integration control objects + stepStart = t0; + stepSize = forward ? step : -step; + for (StepHandler handler : stepHandlers) { + handler.reset(); + } + CombinedEventsManager manager = addEndTimeChecker(t0, t, eventsHandlersManager); + + // compute the first few steps using the configured starter integrator + double stopTime = + start(previousF.length, stepSize, manager, equations, stepStart, y); + if (Double.isNaN(previousT[0])) { + return stopTime; + } + stepStart = previousT[0]; + interpolator.storeTime(stepStart); + + boolean lastStep = false; + while (!lastStep) { + + // shift all data + interpolator.shift(); + + // estimate the state at the end of the step + for (int j = 0; j < y0.length; ++j) { + double sum = 0; + for (int l = 0; l < coeffs.length; ++l) { + sum += coeffs[l] * previousF[l][j]; + } + yTmp[j] = y[j] + stepSize * sum; + } + + // discrete events handling + interpolator.storeTime(stepStart + stepSize); + final boolean truncated; + if (manager.evaluateStep(interpolator)) { + truncated = true; + interpolator.truncateStep(manager.getEventTime()); + } else { + truncated = false; + } + + // the step has been accepted (may have been truncated) + final double nextStep = interpolator.getCurrentTime(); + interpolator.setInterpolatedTime(nextStep); + System.arraycopy(interpolator.getInterpolatedState(), 0, y, 0, y0.length); + manager.stepAccepted(nextStep, y); + lastStep = manager.stop(); + + // provide the step data to the step handler + for (StepHandler handler : stepHandlers) { + handler.handleStep(interpolator, lastStep); + } + stepStart = nextStep; + + if (!lastStep) { + // prepare next step + + if (manager.reset(stepStart, y)) { + + // some events handler has triggered changes that + // invalidate the derivatives, we need to restart from scratch + stopTime = + start(previousF.length, stepSize, manager, equations, stepStart, y); + if (Double.isNaN(previousT[0])) { + return stopTime; + } + stepStart = previousT[0]; + + } else { + + if (truncated) { + // the step has been truncated, we need to adjust the previous steps + for (int i = 1; i < previousF.length; ++i) { + previousT[i] = stepStart - i * stepSize; + interpolator.setInterpolatedTime(previousT[i]); + System.arraycopy(interpolator.getInterpolatedState(), 0, + previousF[i], 0, y0.length); + } + } else { + rotatePreviousSteps(); + } + + // evaluate differential equations for next step + previousT[0] = stepStart; + equations.computeDerivatives(stepStart, y, previousF[0]); + + } + } + + } + + stopTime = stepStart; + stepStart = Double.NaN; + stepSize = Double.NaN; + return stopTime; + + } + + /** Get the coefficients of the method. + *

The coefficients are the ci terms in the following formula:

+ *
+     *   yn+1 = yn + h × ∑i=0i=k-1 cifn-i
+     * 
+ * @return a copy of the coefficients of the method + */ + public double[] getCoeffs() { + return coeffs.clone(); + } + + /** Compute the backward differences coefficients array. + *

This is quite similar to the Pascal triangle containing the + * binomial coefficiens, except for an additional (-1)i sign. + * We use a straightforward approach here, since we don't expect this to + * be run too many times with too high k. It is based on the recurrence + * relations:

+ *
+     *   ∇0 fn = fn
+     *   ∇i+1 fn = ∇ifn - ∇ifn-1
+     * 
+ * @param order order of the integration method + * @return the coefficients array for backward differences + */ + static int[][] computeBackwardDifferencesArray(final int order) { + + // create the array + int[][] bdArray = new int[order][]; + + // recurrence initialization + bdArray[0] = new int[] { 1 }; + + // fill up array using recurrence relation + for (int i = 1; i < order; ++i) { + bdArray[i] = new int[i + 1]; + bdArray[i][0] = 1; + for (int j = 0; j < i - 1; ++j) { + bdArray[i][j + 1] = bdArray[i - 1][j + 1] - bdArray[i - 1][j]; + } + bdArray[i][i] = -bdArray[i - 1][i - 1]; + } + + return bdArray; + + } + + /** Compute the gamma coefficients. + * @param order order of the integration method + * @return gamma coefficients array + */ + static Fraction[] computeGammaArray(final int order) { + + // create the array + Fraction[] gammaArray = new Fraction[order]; + + // recurrence initialization + gammaArray[0] = Fraction.ONE; + + // fill up array using recurrence relation + for (int i = 1; i < order; ++i) { + Fraction gamma = Fraction.ONE; + for (int j = 1; j <= i; ++j) { + gamma = gamma.subtract(gammaArray[i - j].multiply(new Fraction(1, j + 1))); + } + gammaArray[i] = gamma; + } + + return gammaArray; + + } + +} diff --git a/src/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthStepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthStepInterpolator.java new file mode 100644 index 000000000..01f35ff4b --- /dev/null +++ b/src/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthStepInterpolator.java @@ -0,0 +1,289 @@ +/* + * 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.ode.nonstiff; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import org.apache.commons.math.fraction.Fraction; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; +import org.apache.commons.math.ode.sampling.StepInterpolator; + +/** + * This class implements an interpolator for Adams-Bashforth multiple steps. + * + *

This interpolator computes dense output inside the last few + * steps computed. The interpolation equation is consistent with the + * integration scheme, it is based on a kind of rollback of the + * integration from step end to interpolation date: + *

+ *   y(tn + theta h) = y (tn + h) - ∫tn + theta htn + hp(t)dt
+ * 
+ * where theta belongs to [0 ; 1] and p(t) is the interpolation polynomial based on + * the derivatives at previous steps fn-k+1, fn-k+2 ... + * fn and fn.

+ * + * @see AdamsBashforthIntegrator + * @version $Revision$ $Date$ + * @since 2.0 + */ + +class AdamsBashforthStepInterpolator extends MultistepStepInterpolator { + + /** Serializable version identifier */ + private static final long serialVersionUID = -7179861704951334960L; + + /** Neville's interpolation array. */ + private double[] neville; + + /** Integration rollback array. */ + private double[] rollback; + + /** γ array. */ + private double[] gamma; + + /** Backward differences array. */ + private int[][] bdArray; + + /** Original non-truncated step end time. */ + private double nonTruncatedEnd; + + /** Original non-truncated step size. */ + private double nonTruncatedH; + + /** Simple constructor. + * This constructor builds an instance that is not usable yet, the + * {@link AbstractStepInterpolator#reinitialize} method should be called + * before using the instance in order to initialize the internal arrays. This + * constructor is used only in order to delay the initialization in + * some cases. + */ + public AdamsBashforthStepInterpolator() { + } + + /** Copy constructor. + * @param interpolator interpolator to copy from. The copy is a deep + * copy: its arrays are separated from the original arrays of the + * instance + */ + public AdamsBashforthStepInterpolator(final AdamsBashforthStepInterpolator interpolator) { + super(interpolator); + nonTruncatedEnd = interpolator.nonTruncatedEnd; + nonTruncatedH = interpolator.nonTruncatedH; + } + + /** {@inheritDoc} */ + protected StepInterpolator doCopy() { + return new AdamsBashforthStepInterpolator(this); + } + + /** {@inheritDoc} */ + protected void initializeCoefficients() { + + neville = new double[previousF.length]; + rollback = new double[previousF.length]; + + bdArray = AdamsBashforthIntegrator.computeBackwardDifferencesArray(previousF.length); + + Fraction[] fGamma = AdamsBashforthIntegrator.computeGammaArray(previousF.length); + gamma = new double[fGamma.length]; + for (int i = 0; i < fGamma.length; ++i) { + gamma[i] = fGamma[i].doubleValue(); + } + + } + + /** {@inheritDoc} */ + public void storeTime(final double t) { + nonTruncatedEnd = t; + nonTruncatedH = nonTruncatedEnd - previousTime; + super.storeTime(t); + } + + /** Truncate a step. + *

Truncating a step is necessary when an event is triggered + * before the nominal end of the step.

+ * @param truncatedEndTime end time of truncated step + */ + void truncateStep(final double truncatedEndTime) { + currentTime = truncatedEndTime; + h = currentTime - previousTime; + } + + /** {@inheritDoc} */ + public void setInterpolatedTime(final double time) + throws DerivativeException { + interpolatedTime = time; + final double oneMinusThetaH = nonTruncatedEnd - interpolatedTime; + final double theta = (nonTruncatedH == 0) ? + 0 : (nonTruncatedH - oneMinusThetaH) / nonTruncatedH; + computeInterpolatedState(theta, oneMinusThetaH); + } + + /** {@inheritDoc} */ + protected void computeInterpolatedState(final double theta, final double oneMinusThetaH) { + interpolateDerivatives(); + interpolateState(theta); + } + + /** Interpolate the derivatives. + *

The Adams method is based on a polynomial interpolation of the + * derivatives based on the preceding steps. So the interpolation of + * the derivatives here is strictly equivalent: it is a simple polynomial + * interpolation.

+ */ + private void interpolateDerivatives() { + + for (int i = 0; i < interpolatedDerivatives.length; ++i) { + + // initialize the Neville's interpolation algorithm + for (int k = 0; k < previousF.length; ++k) { + neville[k] = previousF[k][i]; + } + + // combine the contributions of each points + for (int l = 1; l < neville.length; ++l) { + for (int m = neville.length - 1; m >= l; --m) { + final double xm = previousT[m]; + final double xmMl = previousT[m - l]; + neville[m] = ((interpolatedTime - xm) * neville[m-1] + + (xmMl - interpolatedTime) * neville[m]) / (xmMl - xm); + } + } + + // the interpolation polynomial value is in the array last element + interpolatedDerivatives[i] = neville[neville.length - 1]; + + } + + } + + /** Interpolate the state. + *

The Adams method is based on a polynomial interpolation of the + * derivatives based on the preceding steps. The polynomial model is + * integrated analytically throughout the last step. Using the notations + * found in the second edition of the first volume (Nonstiff Problems) + * of the reference book by Hairer, Norsett and Wanner: Solving Ordinary + * Differential Equations (Springer-Verlag, ISBN 3-540-56670-8), this + * process leads to the following expression:

+ *
+     * yn+1 = yn +
+     * h × ∑j=0j=k-1 γjjfn
+     * 
+ *

In the previous expression, the γj terms are the + * ones that result from the analytical integration, and can be computed form + * the binomial coefficients Cj-s:

+ *

+ * γj = (-1)j01Cj-sds + *

+ *

In order to interpolate the state in a manner that is consistent with the + * integration scheme, we simply subtract from the current state (at the end of the step) + * the integral computed from interpolation time to step end time.

+ *

+ * ηj(θ)= + * (-1)jθ1Cj-sds + *

+ * The method described in the Hairer, Norsett and Wanner book to compute γj + * is easily extended to compute γj(θ)= + * (-1)j0θCj-sds. From this, + * we can compute ηj(θ) = γjj(θ). + * The first few values are:

+ * + * + * θ + * θ2/2 + * (3θ2+2θ3)/12 + *
jγjγj(θ)ηj(θ)
011-θ
11/2(1-θ2)/2
25/12(5-3θ2-2θ3)/12
+ *

+ * The ηj(θ) functions appear to be polynomial ones. As expected, + * we see that ηj(1)= 0. The recurrence relation derived for + * γj(θ) is: + *

+ *

+ * &sumj=0j=mγj(θ)/(m+1-j) = + * 1/(m+1)! ∏k=0k=m(θ+k) + *

+ * @param theta location of the interpolation point within the last step + */ + private void interpolateState(final double theta) { + + // compute the integrals to remove from the final state + computeRollback(previousT.length - 1, theta); + + // remove these integrals from the final state + for (int j = 0; j < interpolatedState.length; ++j) { + double sum = 0; + for (int l = 0; l < previousT.length; ++l) { + sum += rollback[l] * previousF[l][j]; + } + interpolatedState[j] = currentState[j] - h * sum; + } + + } + + /** Compute the rollback coefficients. + * @param order order of the integration method + * @param theta current value for theta + */ + private void computeRollback(final int order, final double theta) { + + // compute the gamma(theta) values from the recurrence relation + double product = theta; + rollback[0] = theta; + for (int i = 1; i < order; ++i) { + product *= (i + theta) / (i + 1); + double g = product; + for (int j = 1; j <= i; ++j) { + g -= rollback[i - j] / (j + 1); + } + rollback[i] = g; + } + + // subtract it from gamma to get eta(theta) + for (int i = 0; i < order; ++i) { + rollback[i] -= gamma[i]; + } + + // combine the eta integrals with the backward differences array + // to get the rollback coefficients + for (int i = 0; i < order; ++i) { + double f = 0; + for (int j = i; j <= order; ++j) { + f -= rollback[j] * bdArray[j][i]; + } + rollback[i] = f; + } + + } + + /** {@inheritDoc} */ + public void writeExternal(final ObjectOutput out) + throws IOException { + super.writeExternal(out); + out.writeDouble(nonTruncatedEnd); + } + + /** {@inheritDoc} */ + public void readExternal(final ObjectInput in) + throws IOException { + nonTruncatedEnd = in.readDouble(); + } + +} diff --git a/src/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegrator.java b/src/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegrator.java new file mode 100644 index 000000000..b01582827 --- /dev/null +++ b/src/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegrator.java @@ -0,0 +1,296 @@ +/* + * 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.ode.nonstiff; + +import org.apache.commons.math.fraction.Fraction; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderDifferentialEquations; +import org.apache.commons.math.ode.IntegratorException; +import org.apache.commons.math.ode.events.CombinedEventsManager; +import org.apache.commons.math.ode.sampling.StepHandler; + + +/** + * This class implements implicit Adams-Moulton integrators for Ordinary + * Differential Equations. + * + *

Adams-Moulton (in fact due to Adams alone) methods are implicit + * multistep ODE solvers witch fixed stepsize. The value of state vector + * at step n+1 is a simple combination of the value at step n and of the + * derivatives at steps n+1, n, n-1 ... Depending on the number k of previous + * steps one wants to use for computing the next value, different formulas + * are available:

+ *
    + *
  • k = 0: yn+1 = yn + h fn+1
  • + *
  • k = 1: yn+1 = yn + h (fn+1+fn)/2
  • + *
  • k = 2: yn+1 = yn + h (5fn+1+8fn-fn-1)/12
  • + *
  • k = 3: yn+1 = yn + h (9fn+1+19fn-5fn-1+fn-2)/24
  • + *
  • ...
  • + *
+ * + *

A k-steps Adams-Moulton method is of order k+1. There is no limit to the + * value of k.

+ * + *

These methods are implicit: fn+1 is used to compute + * yn+1. Simpler explicit Adams methods exist: the + * Adams-Bashforth methods (which are also due to Adams alone). They are + * provided by the {@link AdamsBashforthIntegrator AdamsBashforthIntegrator} class.

+ * + * @see AdamsBashforthIntegrator + * @see BDFIntegrator + * @version $Revision$ $Date$ + * @since 2.0 + */ +public class AdamsMoultonIntegrator extends MultistepIntegrator { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 4990335331377040417L; + + /** Integrator method name. */ + private static final String METHOD_NAME = "Adams-Moulton"; + + /** Coefficients for the predictor phase of the method. */ + private final double[] predictorCoeffs; + + /** Coefficients for the corrector phase of the method. */ + private final double[] correctorCoeffs; + + /** Integration step. */ + private final double step; + + /** + * Build an Adams-Moulton integrator with the given order and step size. + * @param order order of the method (must be strictly positive) + * @param step integration step size + */ + public AdamsMoultonIntegrator(final int order, final double step) { + + super(METHOD_NAME, order + 1, new AdamsMoultonStepInterpolator()); + + // compute the integration coefficients + int[][] bdArray = AdamsBashforthIntegrator.computeBackwardDifferencesArray(order + 1); + + Fraction[] gamma = AdamsBashforthIntegrator.computeGammaArray(order); + predictorCoeffs = new double[order]; + for (int i = 0; i < order; ++i) { + Fraction fPredictor = Fraction.ZERO; + for (int j = i; j < order; ++j) { + Fraction f = new Fraction(bdArray[j][i], 1); + fPredictor = fPredictor.add(gamma[j].multiply(f)); + } + predictorCoeffs[i] = fPredictor.doubleValue(); + } + + Fraction[] gammaStar = computeGammaStarArray(order); + correctorCoeffs = new double[order + 1]; + for (int i = 0; i <= order; ++i) { + Fraction fCorrector = Fraction.ZERO; + for (int j = i; j <= order; ++j) { + Fraction f = new Fraction(bdArray[j][i], 1); + fCorrector = fCorrector.add(gammaStar[j].multiply(f)); + } + correctorCoeffs[i] = fCorrector.doubleValue(); + } + + this.step = Math.abs(step); + + } + + /** {@inheritDoc} */ + public double integrate(FirstOrderDifferentialEquations equations, + double t0, double[] y0, double t, double[] y) + throws DerivativeException, IntegratorException { + + sanityChecks(equations, t0, y0, t, y); + final boolean forward = (t > t0); + + // initialize working arrays + if (y != y0) { + System.arraycopy(y0, 0, y, 0, y0.length); + } + final double[] yTmp = new double[y0.length]; + + // set up an interpolator sharing the integrator arrays + final AdamsMoultonStepInterpolator interpolator = + (AdamsMoultonStepInterpolator) prototype.copy(); + interpolator.reinitialize(yTmp, previousT, previousF, forward); + + // set up integration control objects + stepStart = t0; + stepSize = forward ? step : -step; + for (StepHandler handler : stepHandlers) { + handler.reset(); + } + CombinedEventsManager manager = addEndTimeChecker(t0, t, eventsHandlersManager); + + // compute the first few steps using the configured starter integrator + double stopTime = + start(previousF.length - 1, stepSize, manager, equations, stepStart, y); + if (Double.isNaN(previousT[0])) { + return stopTime; + } + stepStart = previousT[0]; + rotatePreviousSteps(); + previousF[0] = new double[y0.length]; + interpolator.storeTime(stepStart); + + boolean lastStep = false; + while (!lastStep) { + + // shift all data + interpolator.shift(); + + // predict state at end of step + for (int j = 0; j < y0.length; ++j) { + double sum = 0; + for (int l = 0; l < predictorCoeffs.length; ++l) { + sum += predictorCoeffs[l] * previousF[l+1][j]; + } + yTmp[j] = y[j] + stepSize * sum; + } + + // evaluate the derivatives + final double stepEnd = stepStart + stepSize; + equations.computeDerivatives(stepEnd, yTmp, previousF[0]); + + // apply corrector + for (int j = 0; j < y0.length; ++j) { + double sum = 0; + for (int l = 0; l < correctorCoeffs.length; ++l) { + sum += correctorCoeffs[l] * previousF[l][j]; + } + yTmp[j] = y[j] + stepSize * sum; + } + + // discrete events handling + interpolator.storeTime(stepEnd); + final boolean truncated; + if (manager.evaluateStep(interpolator)) { + truncated = true; + interpolator.truncateStep(manager.getEventTime()); + } else { + truncated = false; + } + + // the step has been accepted (may have been truncated) + final double nextStep = interpolator.getCurrentTime(); + interpolator.setInterpolatedTime(nextStep); + System.arraycopy(interpolator.getInterpolatedState(), 0, y, 0, y0.length); + manager.stepAccepted(nextStep, y); + lastStep = manager.stop(); + + // provide the step data to the step handler + for (StepHandler handler : stepHandlers) { + handler.handleStep(interpolator, lastStep); + } + stepStart = nextStep; + + if (!lastStep) { + // prepare next step + + if (manager.reset(stepStart, y)) { + + // some events handler has triggered changes that + // invalidate the derivatives, we need to restart from scratch + stopTime = + start(previousF.length - 1, stepSize, manager, equations, stepStart, y); + if (Double.isNaN(previousT[0])) { + return stopTime; + } + stepStart = previousT[0]; + rotatePreviousSteps(); + previousF[0] = new double[y0.length]; + + } else { + + if (truncated) { + // the step has been truncated, we need to adjust the previous steps + for (int i = 1; i < previousF.length; ++i) { + previousT[i] = stepStart - i * stepSize; + interpolator.setInterpolatedTime(previousT[i]); + System.arraycopy(interpolator.getInterpolatedState(), 0, + previousF[i], 0, y0.length); + } + } else { + rotatePreviousSteps(); + } + + // evaluate differential equations for next step + previousT[0] = stepStart; + equations.computeDerivatives(stepStart, y, previousF[0]); + + } + } + + } + + stopTime = stepStart; + stepStart = Double.NaN; + stepSize = Double.NaN; + return stopTime; + + } + + /** Get the coefficients of the predictor phase of the method. + *

The coefficients are the ci terms in the following formula:

+ *
+     *   yn+1 = yn + h × ∑i=0i=k-1 cifn-i
+     * 
+ * @return a copy of the coefficients of the method + */ + public double[] getPredictorCoeffs() { + return predictorCoeffs.clone(); + } + + /** Get the coefficients of the corrector phase of the method. + *

The coefficients are the ci terms in the following formula:

+ *
+     *   yn+1 = yn + h × ∑i=0i=k cifn-i
+     * 
+ * @return a copy of the coefficients of the method + */ + public double[] getCorrectorCoeffs() { + return correctorCoeffs.clone(); + } + + /** Compute the gamma star coefficients. + * @param order order of the integration method + * @return gamma star coefficients array + */ + static Fraction[] computeGammaStarArray(final int order) { + + // create the array + Fraction[] gammaStarArray = new Fraction[order + 1]; + + // recurrence initialization + gammaStarArray[0] = Fraction.ONE; + + // fill up array using recurrence relation + for (int i = 1; i <= order; ++i) { + Fraction gammaStar = Fraction.ZERO; + for (int j = 1; j <= i; ++j) { + gammaStar = gammaStar.subtract(gammaStarArray[i - j].multiply(new Fraction(1, j + 1))); + } + gammaStarArray[i] = gammaStar; + } + + return gammaStarArray; + + } + +} diff --git a/src/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonStepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonStepInterpolator.java new file mode 100644 index 000000000..4e41252f0 --- /dev/null +++ b/src/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonStepInterpolator.java @@ -0,0 +1,290 @@ +/* + * 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.ode.nonstiff; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import org.apache.commons.math.fraction.Fraction; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; +import org.apache.commons.math.ode.sampling.StepInterpolator; + +/** + * This class implements an interpolator for Adams-Moulton multiple steps. + * + *

This interpolator computes dense output inside the last few + * steps computed. The interpolation equation is consistent with the + * integration scheme, it is based on a kind of rollback of the + * integration from step end to interpolation date: + *

+ *   y(tn + theta h) = y (tn + h) - ∫tn + theta htn + hp(t)dt
+ * 
+ * where theta belongs to [0 ; 1] and p(t) is the interpolation polynomial based on + * the derivatives at previous steps fn-k+1, fn-k+2 ... + * fn, fn and fn+1.

+ * + * @see AdamsMoultonIntegrator + * @version $Revision$ $Date$ + * @since 2.0 + */ + +class AdamsMoultonStepInterpolator extends MultistepStepInterpolator { + + /** Serializable version identifier */ + private static final long serialVersionUID = 735568489801241899L; + + /** Neville's interpolation array. */ + private double[] neville; + + /** Integration rollback array. */ + private double[] rollback; + + /** γ star array. */ + private double[] gammaStar; + + /** Backward differences array. */ + private int[][] bdArray; + + /** Original non-truncated step end time. */ + private double nonTruncatedEnd; + + /** Original non-truncated step size. */ + private double nonTruncatedH; + + /** Simple constructor. + * This constructor builds an instance that is not usable yet, the + * {@link AbstractStepInterpolator#reinitialize} method should be called + * before using the instance in order to initialize the internal arrays. This + * constructor is used only in order to delay the initialization in + * some cases. + */ + public AdamsMoultonStepInterpolator() { + } + + /** Copy constructor. + * @param interpolator interpolator to copy from. The copy is a deep + * copy: its arrays are separated from the original arrays of the + * instance + */ + public AdamsMoultonStepInterpolator(final AdamsMoultonStepInterpolator interpolator) { + super(interpolator); + nonTruncatedEnd = interpolator.nonTruncatedEnd; + nonTruncatedH = interpolator.nonTruncatedH; + } + + /** {@inheritDoc} */ + protected StepInterpolator doCopy() { + return new AdamsMoultonStepInterpolator(this); + } + + /** {@inheritDoc} */ + protected void initializeCoefficients() { + + neville = new double[previousF.length]; + rollback = new double[previousF.length]; + + bdArray = AdamsBashforthIntegrator.computeBackwardDifferencesArray(previousF.length); + + Fraction[] fGammaStar = AdamsMoultonIntegrator.computeGammaStarArray(previousF.length); + gammaStar = new double[fGammaStar.length]; + for (int i = 0; i < fGammaStar.length; ++i) { + gammaStar[i] = fGammaStar[i].doubleValue(); + } + + } + + /** {@inheritDoc} */ + public void storeTime(final double t) { + nonTruncatedEnd = t; + nonTruncatedH = nonTruncatedEnd - previousTime; + super.storeTime(t); + } + + /** Truncate a step. + *

Truncating a step is necessary when an event is triggered + * before the nominal end of the step.

+ * @param truncatedEndTime end time of truncated step + */ + void truncateStep(final double truncatedEndTime) { + currentTime = truncatedEndTime; + h = currentTime - previousTime; + } + + /** {@inheritDoc} */ + public void setInterpolatedTime(final double time) + throws DerivativeException { + interpolatedTime = time; + final double oneMinusThetaH = nonTruncatedEnd - interpolatedTime; + final double theta = (nonTruncatedH == 0) ? + 0 : (nonTruncatedH - oneMinusThetaH) / nonTruncatedH; + computeInterpolatedState(theta, oneMinusThetaH); + } + + /** {@inheritDoc} */ + protected void computeInterpolatedState(final double theta, final double oneMinusThetaH) { + interpolateDerivatives(); + interpolateState(theta); + } + + /** Interpolate the derivatives. + *

The Adams method is based on a polynomial interpolation of the + * derivatives based on the preceding steps. So the interpolation of + * the derivatives here is strictly equivalent: it is a simple polynomial + * interpolation.

+ */ + private void interpolateDerivatives() { + + for (int i = 0; i < interpolatedDerivatives.length; ++i) { + + // initialize the Neville's interpolation algorithm + for (int k = 0; k < previousF.length; ++k) { + neville[k] = previousF[k][i]; + } + + // combine the contributions of each points + for (int l = 1; l < neville.length; ++l) { + for (int m = neville.length - 1; m >= l; --m) { + final double xm = previousT[m]; + final double xmMl = previousT[m - l]; + neville[m] = ((interpolatedTime - xm) * neville[m-1] + + (xmMl - interpolatedTime) * neville[m]) / (xmMl - xm); + } + } + + // the interpolation polynomial value is in the array last element + interpolatedDerivatives[i] = neville[neville.length - 1]; + + } + + } + + /** Interpolate the state. + *

The Adams method is based on a polynomial interpolation of the + * derivatives based on the preceding steps. The polynomial model is + * integrated analytically throughout the last step. Using the notations + * found in the second edition of the first volume (Nonstiff Problems) + * of the reference book by Hairer, Norsett and Wanner: Solving Ordinary + * Differential Equations (Springer-Verlag, ISBN 3-540-56670-8), this + * process leads to the following expression:

+ *
+     * yn+1 = yn +
+     * h × ∑j=0j=k γj*jfn+1
+     * 
+ *

In the previous expression, the γj* terms are the + * ones that result from the analytical integration, and can be computed form + * the binomial coefficients Cj-s:

+ *

+ * γj* = (-1)j01Cj1-sds + *

+ *

In order to interpolate the state in a manner that is consistent with the + * integration scheme, we simply subtract from the current state (at the end of the step) + * the integral computed from interpolation time to step end time.

+ *

+ * ηj*(θ)= + * (-1)jθ1Cj1-sds + *

+ * The method described in the Hairer, Norsett and Wanner book to compute γj* + * is easily extended to compute γj*(θ)= + * (-1)j0θCj1-sds. From this, + * we can compute ηj*(θ) = + * γj*j*(θ). + * The first few values are:

+ * + * + * + * + * + *
jγj*γj*(θ)ηj*(θ)
01θ1-θ
1-1/22-2θ)/2(-1+2θ-θ2)/2
2-1/12(2θ3-3θ2)/12(-1+3θ2-2θ3)/12
+ *

+ * The ηj(θ) functions appear to be polynomial ones. As expected, + * we see that ηj(1)= 0. The recurrence relation derived for + * γj(θ) is: + *

+ *

+ * &sumj=0j=mγj*(θ)/(m+1-j) = + * 1/(m+1)! ∏k=0k=m(θ+k-1) + *

+ * @param theta location of the interpolation point within the last step + */ + private void interpolateState(final double theta) { + + // compute the integrals to remove from the final state + computeRollback(previousT.length - 1, theta); + + // remove these integrals from the final state + for (int j = 0; j < interpolatedState.length; ++j) { + double sum = 0; + for (int l = 0; l < previousT.length; ++l) { + sum += rollback[l] * previousF[l][j]; + } + interpolatedState[j] = currentState[j] - h * sum; + } + + } + + /** Compute the rollback coefficients. + * @param order order of the integration method + * @param theta current value for theta + */ + private void computeRollback(final int order, final double theta) { + + // compute the gamma star(theta) values from the recurrence relation + double product = theta - 1; + rollback[0] = theta; + for (int i = 1; i <= order; ++i) { + product *= (i - 1 + theta) / (i + 1); + double gStar = product; + for (int j = 1; j <= i; ++j) { + gStar -= rollback[i - j] / (j + 1); + } + rollback[i] = gStar; + } + + // subtract it from gamma star to get eta star(theta) + for (int i = 0; i <= order; ++i) { + rollback[i] -= gammaStar[i]; + } + + // combine the eta star integrals with the backward differences array + // to get the rollback coefficients + for (int i = 0; i <= order; ++i) { + double f = 0; + for (int j = i; j <= order; ++j) { + f -= rollback[j] * bdArray[j][i]; + } + rollback[i] = f; + } + + } + + /** {@inheritDoc} */ + public void writeExternal(final ObjectOutput out) + throws IOException { + super.writeExternal(out); + out.writeDouble(nonTruncatedEnd); + } + + /** {@inheritDoc} */ + public void readExternal(final ObjectInput in) + throws IOException { + nonTruncatedEnd = in.readDouble(); + } + +} diff --git a/src/java/org/apache/commons/math/ode/AdaptiveStepsizeIntegrator.java b/src/java/org/apache/commons/math/ode/nonstiff/AdaptiveStepsizeIntegrator.java similarity index 54% rename from src/java/org/apache/commons/math/ode/AdaptiveStepsizeIntegrator.java rename to src/java/org/apache/commons/math/ode/nonstiff/AdaptiveStepsizeIntegrator.java index ad5415598..6908eb9a0 100644 --- a/src/java/org/apache/commons/math/ode/AdaptiveStepsizeIntegrator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/AdaptiveStepsizeIntegrator.java @@ -15,7 +15,12 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; + +import org.apache.commons.math.ode.AbstractIntegrator; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderDifferentialEquations; +import org.apache.commons.math.ode.IntegratorException; /** * This abstract class holds the common part of all adaptive @@ -47,10 +52,14 @@ package org.apache.commons.math.ode; */ public abstract class AdaptiveStepsizeIntegrator - implements FirstOrderIntegrator { + extends AbstractIntegrator { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -6883579828273958419L; /** Build an integrator with the given stepsize bounds. * The default step handler does nothing. + * @param name name of the method * @param minStep minimal step (must be positive even for backward * integration), the last step can be smaller than this * @param maxStep maximal step (must be positive even for backward @@ -58,12 +67,15 @@ public abstract class AdaptiveStepsizeIntegrator * @param scalAbsoluteTolerance allowed absolute error * @param scalRelativeTolerance allowed relative error */ - public AdaptiveStepsizeIntegrator(double minStep, double maxStep, - double scalAbsoluteTolerance, - double scalRelativeTolerance) { + public AdaptiveStepsizeIntegrator(final String name, + final double minStep, final double maxStep, + final double scalAbsoluteTolerance, + final double scalRelativeTolerance) { - this.minStep = minStep; - this.maxStep = maxStep; + super(name); + + this.minStep = Math.abs(minStep); + this.maxStep = Math.abs(maxStep); this.initialStep = -1.0; this.scalAbsoluteTolerance = scalAbsoluteTolerance; @@ -71,17 +83,13 @@ public abstract class AdaptiveStepsizeIntegrator this.vecAbsoluteTolerance = null; this.vecRelativeTolerance = null; - // set the default step handler - handler = DummyStepHandler.getInstance(); - - switchesHandler = new SwitchingFunctionsHandler(); - resetInternalState(); } /** Build an integrator with the given stepsize bounds. * The default step handler does nothing. + * @param name name of the method * @param minStep minimal step (must be positive even for backward * integration), the last step can be smaller than this * @param maxStep maximal step (must be positive even for backward @@ -89,9 +97,12 @@ public abstract class AdaptiveStepsizeIntegrator * @param vecAbsoluteTolerance allowed absolute error * @param vecRelativeTolerance allowed relative error */ - public AdaptiveStepsizeIntegrator(double minStep, double maxStep, - double[] vecAbsoluteTolerance, - double[] vecRelativeTolerance) { + public AdaptiveStepsizeIntegrator(final String name, + final double minStep, final double maxStep, + final double[] vecAbsoluteTolerance, + final double[] vecRelativeTolerance) { + + super(name); this.minStep = minStep; this.maxStep = maxStep; @@ -102,11 +113,6 @@ public abstract class AdaptiveStepsizeIntegrator this.vecAbsoluteTolerance = vecAbsoluteTolerance; this.vecRelativeTolerance = vecRelativeTolerance; - // set the default step handler - handler = DummyStepHandler.getInstance(); - - switchesHandler = new SwitchingFunctionsHandler(); - resetInternalState(); } @@ -122,7 +128,7 @@ public abstract class AdaptiveStepsizeIntegrator * outside of the min/max step interval will lead the integrator to * ignore the value and compute the initial step size by itself) */ - public void setInitialStepSize(double initialStepSize) { + public void setInitialStepSize(final double initialStepSize) { if ((initialStepSize < minStep) || (initialStepSize > maxStep)) { initialStep = -1.0; } else { @@ -130,38 +136,6 @@ public abstract class AdaptiveStepsizeIntegrator } } - /** Set the step handler for this integrator. - * The handler will be called by the integrator for each accepted - * step. - * @param handler handler for the accepted steps - */ - public void setStepHandler (StepHandler handler) { - this.handler = handler; - } - - /** Get the step handler for this integrator. - * @return the step handler for this integrator - */ - public StepHandler getStepHandler() { - return handler; - } - - /** Add a switching function to the integrator. - * @param function switching function - * @param maxCheckInterval maximal time interval between switching - * function checks (this interval prevents missing sign changes in - * case the integration steps becomes very large) - * @param convergence convergence threshold in the event time search - * @param maxIterationCount upper limit of the iteration count in - * the event time search - */ - public void addSwitchingFunction(SwitchingFunction function, - double maxCheckInterval, - double convergence, - int maxIterationCount) { - switchesHandler.add(function, maxCheckInterval, convergence, maxIterationCount); - } - /** Perform some sanity checks on the integration parameters. * @param equations differential equations set * @param t0 start time @@ -170,46 +144,31 @@ public abstract class AdaptiveStepsizeIntegrator * @param y placeholder where to put the state vector * @exception IntegratorException if some inconsistency is detected */ - protected void sanityChecks(FirstOrderDifferentialEquations equations, - double t0, double[] y0, double t, double[] y) - throws IntegratorException { - if (equations.getDimension() != y0.length) { - throw new IntegratorException("dimensions mismatch: ODE problem has dimension {0}," + - " initial state vector has dimension {1}", - new Object[] { - new Integer(equations.getDimension()), - new Integer(y0.length) - }); - } - if (equations.getDimension() != y.length) { - throw new IntegratorException("dimensions mismatch: ODE problem has dimension {0}," + - " final state vector has dimension {1}", - new Object[] { - new Integer(equations.getDimension()), - new Integer(y.length) - }); - } + protected void sanityChecks(final FirstOrderDifferentialEquations equations, + final double t0, final double[] y0, + final double t, final double[] y) + throws IntegratorException { + + super.sanityChecks(equations, t0, y0, t, y); + if ((vecAbsoluteTolerance != null) && (vecAbsoluteTolerance.length != y0.length)) { throw new IntegratorException("dimensions mismatch: state vector has dimension {0}," + " absolute tolerance vector has dimension {1}", new Object[] { - new Integer(y0.length), - new Integer(vecAbsoluteTolerance.length) + Integer.valueOf(y0.length), + Integer.valueOf(vecAbsoluteTolerance.length) }); } + if ((vecRelativeTolerance != null) && (vecRelativeTolerance.length != y0.length)) { throw new IntegratorException("dimensions mismatch: state vector has dimension {0}," + " relative tolerance vector has dimension {1}", new Object[] { - new Integer(y0.length), - new Integer(vecRelativeTolerance.length) + Integer.valueOf(y0.length), + Integer.valueOf(vecRelativeTolerance.length) }); } - if (Math.abs(t - t0) <= 1.0e-12 * Math.max(Math.abs(t0), Math.abs(t))) { - throw new IntegratorException("too small integration interval: length = {0}", - new Object[] { new Double(Math.abs(t - t0)) }); - } - + } /** Initialize the integration step. @@ -226,11 +185,11 @@ public abstract class AdaptiveStepsizeIntegrator * @exception DerivativeException this exception is propagated to * the caller if the underlying user function triggers one */ - public double initializeStep(FirstOrderDifferentialEquations equations, - boolean forward, int order, double[] scale, - double t0, double[] y0, double[] yDot0, - double[] y1, double[] yDot1) - throws DerivativeException { + public double initializeStep(final FirstOrderDifferentialEquations equations, + final boolean forward, final int order, final double[] scale, + final double t0, final double[] y0, final double[] yDot0, + final double[] y1, final double[] yDot1) + throws DerivativeException { if (initialStep > 0) { // use the user provided value @@ -271,10 +230,10 @@ public abstract class AdaptiveStepsizeIntegrator // step size is computed such that // h^order * max (||y'/tol||, ||y''/tol||) = 0.01 - double maxInv2 = Math.max(Math.sqrt(yDotOnScale2), yDDotOnScale); - double h1 = (maxInv2 < 1.0e-15) ? - Math.max(1.0e-6, 0.001 * Math.abs(h)) : - Math.pow(0.01 / maxInv2, 1.0 / order); + final double maxInv2 = Math.max(Math.sqrt(yDotOnScale2), yDDotOnScale); + final double h1 = (maxInv2 < 1.0e-15) ? + Math.max(1.0e-6, 0.001 * Math.abs(h)) : + Math.pow(0.01 / maxInv2, 1.0 / order); h = Math.min(100.0 * Math.abs(h), h1); h = Math.max(h, 1.0e-12 * Math.abs(t0)); // avoids cancellation when computing t1 - t0 if (h < getMinStep()) { @@ -293,85 +252,51 @@ public abstract class AdaptiveStepsizeIntegrator /** Filter the integration step. * @param h signed step + * @param forward forward integration indicator * @param acceptSmall if true, steps smaller than the minimal value * are silently increased up to this value, if false such small * steps generate an exception * @return a bounded integration step (h if no bound is reach, or a bounded value) * @exception IntegratorException if the step is too small and acceptSmall is false */ - protected double filterStep(double h, boolean acceptSmall) + protected double filterStep(final double h, final boolean forward, final boolean acceptSmall) throws IntegratorException { - if (Math.abs(h) < minStep) { - if (acceptSmall) { - h = (h < 0) ? -minStep : minStep; - } else { - throw new IntegratorException("minimal step size ({0}) reached," + - " integration needs {1}", - new Object[] { - new Double(minStep), - new Double(Math.abs(h)) - }); + double filteredH = h; + if (Math.abs(h) < minStep) { + if (acceptSmall) { + filteredH = forward ? minStep : -minStep; + } else { + throw new IntegratorException("minimal step size ({0}) reached," + + " integration needs {1}", + new Object[] { + Double.valueOf(minStep), + Double.valueOf(Math.abs(h)) + }); + } } - } - if (h > maxStep) { - h = maxStep; - } else if (h < -maxStep) { - h = -maxStep; - } + if (filteredH > maxStep) { + filteredH = maxStep; + } else if (filteredH < -maxStep) { + filteredH = -maxStep; + } - return h; + return filteredH; } - /** Integrate the differential equations up to the given time. - *

This method solves an Initial Value Problem (IVP).

- *

Since this method stores some internal state variables made - * available in its public interface during integration ({@link - * #getCurrentSignedStepsize()}), it is not thread-safe.

- * @param equations differential equations to integrate - * @param t0 initial time - * @param y0 initial value of the state vector at t0 - * @param t target time for the integration - * (can be set to a value smaller than t0 for backward integration) - * @param y placeholder where to put the state vector at each successful - * step (and hence at the end of integration), can be the same object as y0 - * @throws IntegratorException if the integrator cannot perform integration - * @throws DerivativeException this exception is propagated to the caller if - * the underlying user function triggers one - */ - public abstract void integrate (FirstOrderDifferentialEquations equations, - double t0, double[] y0, - double t, double[] y) + /** {@inheritDoc} */ + public abstract double integrate (FirstOrderDifferentialEquations equations, + double t0, double[] y0, + double t, double[] y) throws DerivativeException, IntegratorException; - /** Get the current value of the step start time ti. - *

This method can be called during integration (typically by - * the object implementing the {@link FirstOrderDifferentialEquations - * differential equations} problem) if the value of the current step that - * is attempted is needed.

- *

The result is undefined if the method is called outside of - * calls to {@link #integrate}

- * @return current value of the step start time ti - */ + /** {@inheritDoc} */ public double getCurrentStepStart() { return stepStart; } - /** Get the current signed value of the integration stepsize. - *

This method can be called during integration (typically by - * the object implementing the {@link FirstOrderDifferentialEquations - * differential equations} problem) if the signed value of the current stepsize - * that is tried is needed.

- *

The result is undefined if the method is called outside of - * calls to {@link #integrate}

- * @return current signed value of the stepsize - */ - public double getCurrentSignedStepsize() { - return stepSize; - } - /** Reset internal state to dummy values. */ protected void resetInternalState() { stepStart = Double.NaN; @@ -413,16 +338,4 @@ public abstract class AdaptiveStepsizeIntegrator /** Allowed relative vectorial error. */ protected double[] vecRelativeTolerance; - /** Step handler. */ - protected StepHandler handler; - - /** Switching functions handler. */ - protected SwitchingFunctionsHandler switchesHandler; - - /** Current step start time. */ - protected double stepStart; - - /** Current stepsize. */ - protected double stepSize; - } diff --git a/src/java/org/apache/commons/math/ode/ClassicalRungeKuttaIntegrator.java b/src/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegrator.java similarity index 84% rename from src/java/org/apache/commons/math/ode/ClassicalRungeKuttaIntegrator.java rename to src/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegrator.java index 5c281c14a..e4d86650e 100644 --- a/src/java/org/apache/commons/math/ode/ClassicalRungeKuttaIntegrator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegrator.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; /** * This class implements the classical fourth order Runge-Kutta @@ -45,8 +45,8 @@ package org.apache.commons.math.ode; public class ClassicalRungeKuttaIntegrator extends RungeKuttaIntegrator { - /** Integrator method name. */ - private static final String methodName = "classical Runge-Kutta"; + /** Serializable version identifier. */ + private static final long serialVersionUID = 3710070023793519840L; /** Time steps Butcher array. */ private static final double[] c = { @@ -70,15 +70,9 @@ public class ClassicalRungeKuttaIntegrator * step. * @param step integration step */ - public ClassicalRungeKuttaIntegrator(double step) { - super(c, a, b, new ClassicalRungeKuttaStepInterpolator(), step); - } - - /** Get the name of the method. - * @return name of the method - */ - public String getName() { - return methodName; + public ClassicalRungeKuttaIntegrator(final double step) { + super("classical Runge-Kutta", c, a, b, + new ClassicalRungeKuttaStepInterpolator(), step); } } diff --git a/src/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java new file mode 100644 index 000000000..b5a5d6e9c --- /dev/null +++ b/src/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java @@ -0,0 +1,108 @@ +/* + * 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.ode.nonstiff; + +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.sampling.StepInterpolator; + +/** + * This class implements a step interpolator for the classical fourth + * order Runge-Kutta integrator. + * + *

This interpolator allows to compute dense output inside the last + * step computed. The interpolation equation is consistent with the + * integration scheme : + + *

+ *   y(t_n + theta h) = y (t_n + h)
+ *                    + (1 - theta) (h/6) [ (-4 theta^2 + 5 theta - 1) y'_1
+ *                                          +(4 theta^2 - 2 theta - 2) (y'_2 + y'_3)
+ *                                          -(4 theta^2 +   theta + 1) y'_4
+ *                                        ]
+ * 
+ * + * where theta belongs to [0 ; 1] and where y'_1 to y'_4 are the four + * evaluations of the derivatives already computed during the + * step.

+ * + * @see ClassicalRungeKuttaIntegrator + * @version $Revision$ $Date$ + * @since 1.2 + */ + +class ClassicalRungeKuttaStepInterpolator + extends RungeKuttaStepInterpolator { + + /** Serializable version identifier */ + private static final long serialVersionUID = -6576285612589783992L; + + /** Simple constructor. + * This constructor builds an instance that is not usable yet, the + * {@link RungeKuttaStepInterpolator#reinitialize} method should be + * called before using the instance in order to initialize the + * internal arrays. This constructor is used only in order to delay + * the initialization in some cases. The {@link RungeKuttaIntegrator} + * class uses the prototyping design pattern to create the step + * interpolators by cloning an uninitialized model and latter initializing + * the copy. + */ + public ClassicalRungeKuttaStepInterpolator() { + } + + /** Copy constructor. + * @param interpolator interpolator to copy from. The copy is a deep + * copy: its arrays are separated from the original arrays of the + * instance + */ + public ClassicalRungeKuttaStepInterpolator(final ClassicalRungeKuttaStepInterpolator interpolator) { + super(interpolator); + } + + /** {@inheritDoc} */ + protected StepInterpolator doCopy() { + return new ClassicalRungeKuttaStepInterpolator(this); + } + + /** {@inheritDoc} */ + protected void computeInterpolatedState(final double theta, + final double oneMinusThetaH) + throws DerivativeException { + + final double fourTheta = 4 * theta; + final double oneMinusTheta = 1 - theta; + final double oneMinus2Theta = 1 - 2 * theta; + final double s = oneMinusThetaH / 6.0; + final double coeff1 = s * ((-fourTheta + 5) * theta - 1); + final double coeff23 = s * (( fourTheta - 2) * theta - 2); + final double coeff4 = s * ((-fourTheta - 1) * theta - 1); + final double coeffDot1 = oneMinusTheta * oneMinus2Theta; + final double coeffDot23 = 2 * theta * oneMinusTheta; + final double coeffDot4 = -theta * oneMinus2Theta; + for (int i = 0; i < interpolatedState.length; ++i) { + final double yDot1 = yDotK[0][i]; + final double yDot23 = yDotK[1][i] + yDotK[2][i]; + final double yDot4 = yDotK[3][i]; + interpolatedState[i] = + currentState[i] + coeff1 * yDot1 + coeff23 * yDot23 + coeff4 * yDot4; + interpolatedDerivatives[i] = + coeffDot1 * yDot1 + coeffDot23 * yDot23 + coeffDot4 * yDot4; + } + + } + +} diff --git a/src/java/org/apache/commons/math/ode/DormandPrince54Integrator.java b/src/java/org/apache/commons/math/ode/nonstiff/DormandPrince54Integrator.java similarity index 70% rename from src/java/org/apache/commons/math/ode/DormandPrince54Integrator.java rename to src/java/org/apache/commons/math/ode/nonstiff/DormandPrince54Integrator.java index b33a376df..ddd8d8dae 100644 --- a/src/java/org/apache/commons/math/ode/DormandPrince54Integrator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/DormandPrince54Integrator.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; /** * This class implements the 5(4) Dormand-Prince integrator for Ordinary @@ -46,8 +46,11 @@ package org.apache.commons.math.ode; public class DormandPrince54Integrator extends EmbeddedRungeKuttaIntegrator { + /** Serializable version identifier. */ + private static final long serialVersionUID = -7932553613600031791L; + /** Integrator method name. */ - private static final String methodName = "Dormand-Prince 5(4)"; + private static final String METHOD_NAME = "Dormand-Prince 5(4)"; /** Time steps Butcher array. */ private static final double[] staticC = { @@ -98,10 +101,10 @@ public class DormandPrince54Integrator * @param scalAbsoluteTolerance allowed absolute error * @param scalRelativeTolerance allowed relative error */ - public DormandPrince54Integrator(double minStep, double maxStep, - double scalAbsoluteTolerance, - double scalRelativeTolerance) { - super(true, staticC, staticA, staticB, new DormandPrince54StepInterpolator(), + public DormandPrince54Integrator(final double minStep, final double maxStep, + final double scalAbsoluteTolerance, + final double scalRelativeTolerance) { + super(METHOD_NAME, true, staticC, staticA, staticB, new DormandPrince54StepInterpolator(), minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance); } @@ -114,51 +117,36 @@ public class DormandPrince54Integrator * @param vecAbsoluteTolerance allowed absolute error * @param vecRelativeTolerance allowed relative error */ - public DormandPrince54Integrator(double minStep, double maxStep, - double[] vecAbsoluteTolerance, - double[] vecRelativeTolerance) { - super(true, staticC, staticA, staticB, new DormandPrince54StepInterpolator(), + public DormandPrince54Integrator(final double minStep, final double maxStep, + final double[] vecAbsoluteTolerance, + final double[] vecRelativeTolerance) { + super(METHOD_NAME, true, staticC, staticA, staticB, new DormandPrince54StepInterpolator(), minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance); } - /** Get the name of the method. - * @return name of the method - */ - public String getName() { - return methodName; - } - - /** Get the order of the method. - * @return order of the method - */ + /** {@inheritDoc} */ public int getOrder() { return 5; } - /** Compute the error ratio. - * @param yDotK derivatives computed during the first stages - * @param y0 estimate of the step at the start of the step - * @param y1 estimate of the step at the end of the step - * @param h current step - * @return error ratio, greater than 1 if step should be rejected - */ - protected double estimateError(double[][] yDotK, - double[] y0, double[] y1, - double h) { + /** {@inheritDoc} */ + protected double estimateError(final double[][] yDotK, + final double[] y0, final double[] y1, + final double h) { double error = 0; for (int j = 0; j < y0.length; ++j) { - double errSum = e1 * yDotK[0][j] + e3 * yDotK[2][j] + - e4 * yDotK[3][j] + e5 * yDotK[4][j] + - e6 * yDotK[5][j] + e7 * yDotK[6][j]; + final double errSum = e1 * yDotK[0][j] + e3 * yDotK[2][j] + + e4 * yDotK[3][j] + e5 * yDotK[4][j] + + e6 * yDotK[5][j] + e7 * yDotK[6][j]; - double yScale = Math.max(Math.abs(y0[j]), Math.abs(y1[j])); - double tol = (vecAbsoluteTolerance == null) ? - (scalAbsoluteTolerance + scalRelativeTolerance * yScale) : - (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale); - double ratio = h * errSum / tol; - error += ratio * ratio; + final double yScale = Math.max(Math.abs(y0[j]), Math.abs(y1[j])); + final double tol = (vecAbsoluteTolerance == null) ? + (scalAbsoluteTolerance + scalRelativeTolerance * yScale) : + (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale); + final double ratio = h * errSum / tol; + error += ratio * ratio; } diff --git a/src/java/org/apache/commons/math/ode/DormandPrince54StepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/DormandPrince54StepInterpolator.java similarity index 73% rename from src/java/org/apache/commons/math/ode/DormandPrince54StepInterpolator.java rename to src/java/org/apache/commons/math/ode/nonstiff/DormandPrince54StepInterpolator.java index f66a92d83..00a648dc4 100644 --- a/src/java/org/apache/commons/math/ode/DormandPrince54StepInterpolator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/DormandPrince54StepInterpolator.java @@ -15,7 +15,11 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; + +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderDifferentialEquations; +import org.apache.commons.math.ode.sampling.StepInterpolator; /** * This class represents an interpolator over the last step during an @@ -53,7 +57,7 @@ class DormandPrince54StepInterpolator * copy: its arrays are separated from the original arrays of the * instance */ - public DormandPrince54StepInterpolator(DormandPrince54StepInterpolator interpolator) { + public DormandPrince54StepInterpolator(final DormandPrince54StepInterpolator interpolator) { super(interpolator); @@ -77,24 +81,15 @@ class DormandPrince54StepInterpolator } - /** Really copy the finalized instance. - * @return a copy of the finalized instance - */ + /** {@inheritDoc} */ protected StepInterpolator doCopy() { return new DormandPrince54StepInterpolator(this); } - /** Reinitialize the instance - * @param equations set of differential equations being integrated - * @param y reference to the integrator array holding the state at - * the end of the step - * @param yDotK reference to the integrator array holding all the - * intermediate slopes - * @param forward integration direction indicator - */ - public void reinitialize(FirstOrderDifferentialEquations equations, - double[] y, double[][] yDotK, boolean forward) { + /** {@inheritDoc} */ + public void reinitialize(final FirstOrderDifferentialEquations equations, + final double[] y, final double[][] yDotK, final boolean forward) { super.reinitialize(equations, y, yDotK, forward); v1 = null; v2 = null; @@ -103,24 +98,15 @@ class DormandPrince54StepInterpolator vectorsInitialized = false; } - /** Store the current step time. - * @param t current time - */ - public void storeTime(double t) { + /** {@inheritDoc} */ + public void storeTime(final double t) { super.storeTime(t); vectorsInitialized = false; } - /** Compute the state at the interpolated time. - * @param theta normalized interpolation abscissa within the step - * (theta is zero at the previous time step and one at the current time step) - * @param oneMinusThetaH time gap between the interpolated time and - * the current time - * @throws DerivativeException this exception is propagated to the caller if the - * underlying user function triggers one - */ - protected void computeInterpolatedState(double theta, - double oneMinusThetaH) + /** {@inheritDoc} */ + protected void computeInterpolatedState(final double theta, + final double oneMinusThetaH) throws DerivativeException { if (! vectorsInitialized) { @@ -136,12 +122,16 @@ class DormandPrince54StepInterpolator // we need to compute the interpolation vectors for this time step for (int i = 0; i < interpolatedState.length; ++i) { - v1[i] = h * (a70 * yDotK[0][i] + a72 * yDotK[2][i] + a73 * yDotK[3][i] + - a74 * yDotK[4][i] + a75 * yDotK[5][i]); - v2[i] = h * yDotK[0][i] - v1[i]; - v3[i] = v1[i] - v2[i] - h * yDotK[6][i]; - v4[i] = h * (d0 * yDotK[0][i] + d2 * yDotK[2][i] + d3 * yDotK[3][i] + - d4 * yDotK[4][i] + d5 * yDotK[5][i] + d6 * yDotK[6][i]); + final double yDot0 = yDotK[0][i]; + final double yDot2 = yDotK[2][i]; + final double yDot3 = yDotK[3][i]; + final double yDot4 = yDotK[4][i]; + final double yDot5 = yDotK[5][i]; + final double yDot6 = yDotK[6][i]; + v1[i] = a70 * yDot0 + a72 * yDot2 + a73 * yDot3 + a74 * yDot4 + a75 * yDot5; + v2[i] = yDot0 - v1[i]; + v3[i] = v1[i] - v2[i] - yDot6; + v4[i] = d0 * yDot0 + d2 * yDot2 + d3 * yDot3 + d4 * yDot4 + d5 * yDot5 + d6 * yDot6; } vectorsInitialized = true; @@ -149,10 +139,15 @@ class DormandPrince54StepInterpolator } // interpolate - double eta = oneMinusThetaH / h; + final double eta = 1 - theta; + final double twoTheta = 2 * theta; + final double dot2 = 1 - twoTheta; + final double dot3 = theta * (2 - 3 * theta); + final double dot4 = twoTheta * (1 + theta * (twoTheta - 3)); for (int i = 0; i < interpolatedState.length; ++i) { - interpolatedState[i] = currentState[i] - - eta * (v1[i] - theta * (v2[i] + theta * (v3[i] + eta * v4[i]))); + interpolatedState[i] = + currentState[i] - oneMinusThetaH * (v1[i] - theta * (v2[i] + theta * (v3[i] + eta * v4[i]))); + interpolatedDerivatives[i] = v1[i] + dot2 * v2[i] + dot3 * v3[i] + dot4 * v4[i]; } } diff --git a/src/java/org/apache/commons/math/ode/DormandPrince853Integrator.java b/src/java/org/apache/commons/math/ode/nonstiff/DormandPrince853Integrator.java similarity index 81% rename from src/java/org/apache/commons/math/ode/DormandPrince853Integrator.java rename to src/java/org/apache/commons/math/ode/nonstiff/DormandPrince853Integrator.java index 71736bfa5..f1dcd088b 100644 --- a/src/java/org/apache/commons/math/ode/DormandPrince853Integrator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/DormandPrince853Integrator.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; /** * This class implements the 8(5,3) Dormand-Prince integrator for Ordinary @@ -54,8 +54,11 @@ package org.apache.commons.math.ode; public class DormandPrince853Integrator extends EmbeddedRungeKuttaIntegrator { + /** Serializable version identifier. */ + private static final long serialVersionUID = -8627142100635188441L; + /** Integrator method name. */ - private static final String methodName = "Dormand-Prince 8 (5, 3)"; + private static final String METHOD_NAME = "Dormand-Prince 8 (5, 3)"; /** Time steps Butcher array. */ private static final double[] staticC = { @@ -210,10 +213,10 @@ public class DormandPrince853Integrator * @param scalAbsoluteTolerance allowed absolute error * @param scalRelativeTolerance allowed relative error */ - public DormandPrince853Integrator(double minStep, double maxStep, - double scalAbsoluteTolerance, - double scalRelativeTolerance) { - super(true, staticC, staticA, staticB, + public DormandPrince853Integrator(final double minStep, final double maxStep, + final double scalAbsoluteTolerance, + final double scalRelativeTolerance) { + super(METHOD_NAME, true, staticC, staticA, staticB, new DormandPrince853StepInterpolator(), minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance); } @@ -227,58 +230,43 @@ public class DormandPrince853Integrator * @param vecAbsoluteTolerance allowed absolute error * @param vecRelativeTolerance allowed relative error */ - public DormandPrince853Integrator(double minStep, double maxStep, - double[] vecAbsoluteTolerance, - double[] vecRelativeTolerance) { - super(true, staticC, staticA, staticB, + public DormandPrince853Integrator(final double minStep, final double maxStep, + final double[] vecAbsoluteTolerance, + final double[] vecRelativeTolerance) { + super(METHOD_NAME, true, staticC, staticA, staticB, new DormandPrince853StepInterpolator(), minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance); } - /** Get the name of the method. - * @return name of the method - */ - public String getName() { - return methodName; - } - - /** Get the order of the method. - * @return order of the method - */ + /** {@inheritDoc} */ public int getOrder() { return 8; } - /** Compute the error ratio. - * @param yDotK derivatives computed during the first stages - * @param y0 estimate of the step at the start of the step - * @param y1 estimate of the step at the end of the step - * @param h current step - * @return error ratio, greater than 1 if step should be rejected - */ - protected double estimateError(double[][] yDotK, - double[] y0, double[] y1, - double h) { + /** {@inheritDoc} */ + protected double estimateError(final double[][] yDotK, + final double[] y0, final double[] y1, + final double h) { double error1 = 0; double error2 = 0; for (int j = 0; j < y0.length; ++j) { - double errSum1 = e1_01 * yDotK[0][j] + e1_06 * yDotK[5][j] + - e1_07 * yDotK[6][j] + e1_08 * yDotK[7][j] + - e1_09 * yDotK[8][j] + e1_10 * yDotK[9][j] + - e1_11 * yDotK[10][j] + e1_12 * yDotK[11][j]; - double errSum2 = e2_01 * yDotK[0][j] + e2_06 * yDotK[5][j] + - e2_07 * yDotK[6][j] + e2_08 * yDotK[7][j] + - e2_09 * yDotK[8][j] + e2_10 * yDotK[9][j] + - e2_11 * yDotK[10][j] + e2_12 * yDotK[11][j]; + final double errSum1 = e1_01 * yDotK[0][j] + e1_06 * yDotK[5][j] + + e1_07 * yDotK[6][j] + e1_08 * yDotK[7][j] + + e1_09 * yDotK[8][j] + e1_10 * yDotK[9][j] + + e1_11 * yDotK[10][j] + e1_12 * yDotK[11][j]; + final double errSum2 = e2_01 * yDotK[0][j] + e2_06 * yDotK[5][j] + + e2_07 * yDotK[6][j] + e2_08 * yDotK[7][j] + + e2_09 * yDotK[8][j] + e2_10 * yDotK[9][j] + + e2_11 * yDotK[10][j] + e2_12 * yDotK[11][j]; - double yScale = Math.max(Math.abs(y0[j]), Math.abs(y1[j])); - double tol = (vecAbsoluteTolerance == null) ? - (scalAbsoluteTolerance + scalRelativeTolerance * yScale) : - (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale); - double ratio1 = errSum1 / tol; + final double yScale = Math.max(Math.abs(y0[j]), Math.abs(y1[j])); + final double tol = (vecAbsoluteTolerance == null) ? + (scalAbsoluteTolerance + scalRelativeTolerance * yScale) : + (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale); + final double ratio1 = errSum1 / tol; error1 += ratio1 * ratio1; - double ratio2 = errSum2 / tol; + final double ratio2 = errSum2 / tol; error2 += ratio2 * ratio2; } diff --git a/src/java/org/apache/commons/math/ode/DormandPrince853StepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/DormandPrince853StepInterpolator.java similarity index 77% rename from src/java/org/apache/commons/math/ode/DormandPrince853StepInterpolator.java rename to src/java/org/apache/commons/math/ode/nonstiff/DormandPrince853StepInterpolator.java index a6d9ed711..60c7e190b 100644 --- a/src/java/org/apache/commons/math/ode/DormandPrince853StepInterpolator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/DormandPrince853StepInterpolator.java @@ -15,11 +15,16 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; -import java.io.ObjectOutput; -import java.io.ObjectInput; import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderDifferentialEquations; +import org.apache.commons.math.ode.sampling.StepInterpolator; /** * This class represents an interpolator over the last step during an @@ -55,7 +60,7 @@ class DormandPrince853StepInterpolator * copy: its arrays are separated from the original arrays of the * instance */ - public DormandPrince853StepInterpolator(DormandPrince853StepInterpolator interpolator) { + public DormandPrince853StepInterpolator(final DormandPrince853StepInterpolator interpolator) { super(interpolator); @@ -67,7 +72,7 @@ class DormandPrince853StepInterpolator } else { - int dimension = interpolator.currentState.length; + final int dimension = interpolator.currentState.length; yDotKLast = new double[3][]; for (int k = 0; k < yDotKLast.length; ++k) { @@ -88,40 +93,18 @@ class DormandPrince853StepInterpolator } - /** Really copy the finalized instance. - * @return a copy of the finalized instance - */ + /** {@inheritDoc} */ protected StepInterpolator doCopy() { return new DormandPrince853StepInterpolator(this); } - /** Reinitialize the instance - * Some embedded Runge-Kutta integrators need fewer functions - * evaluations than their counterpart step interpolators. So the - * interpolator should perform the last evaluations they need by - * themselves. The {@link EmbeddedRungeKuttaIntegrator - * EmbeddedRungeKuttaIntegrator} abstract class calls this method in - * order to let the step interpolator perform the evaluations it - * needs. These evaluations will be performed during the call to - * doFinalize if any, i.e. only if the step handler - * either calls the {@link AbstractStepInterpolator#finalizeStep - * finalizeStep} method or the {@link - * AbstractStepInterpolator#getInterpolatedState getInterpolatedState} - * method (for an interpolator which needs a finalization) or if it clones - * the step interpolator. - * @param equations set of differential equations being integrated - * @param y reference to the integrator array holding the state at - * the end of the step - * @param yDotK reference to the integrator array holding all the - * intermediate slopes - * @param forward integration direction indicator - */ - public void reinitialize(FirstOrderDifferentialEquations equations, - double[] y, double[][] yDotK, boolean forward) { + /** {@inheritDoc} */ + public void reinitialize(final FirstOrderDifferentialEquations equations, + final double[] y, final double[][] yDotK, final boolean forward) { super.reinitialize(equations, y, yDotK, forward); - int dimension = currentState.length; + final int dimension = currentState.length; yDotKLast = new double[3][]; for (int k = 0; k < yDotKLast.length; ++k) { @@ -137,26 +120,15 @@ class DormandPrince853StepInterpolator } - /** Store the current step time. - * @param t current time - */ - public void storeTime(double t) { + /** {@inheritDoc} */ + public void storeTime(final double t) { super.storeTime(t); vectorsInitialized = false; } - /** Compute the state at the interpolated time. - * This is the main processing method that should be implemented by - * the derived classes to perform the interpolation. - * @param theta normalized interpolation abscissa within the step - * (theta is zero at the previous time step and one at the current time step) - * @param oneMinusThetaH time gap between the interpolated time and - * the current time - * @throws DerivativeException this exception is propagated to the caller if the - * underlying user function triggers one - */ - protected void computeInterpolatedState(double theta, - double oneMinusThetaH) + /** {@inheritDoc} */ + protected void computeInterpolatedState(final double theta, + final double oneMinusThetaH) throws DerivativeException { if (! vectorsInitialized) { @@ -173,42 +145,62 @@ class DormandPrince853StepInterpolator // compute the interpolation vectors for this time step for (int i = 0; i < interpolatedState.length; ++i) { - v[0][i] = h * (b_01 * yDotK[0][i] + b_06 * yDotK[5][i] + b_07 * yDotK[6][i] + - b_08 * yDotK[7][i] + b_09 * yDotK[8][i] + b_10 * yDotK[9][i] + - b_11 * yDotK[10][i] + b_12 * yDotK[11][i]); - v[1][i] = h * yDotK[0][i] - v[0][i]; - v[2][i] = v[0][i] - v[1][i] - h * yDotK[12][i]; - for (int k = 0; k < d.length; ++k) { - v[k+3][i] = h * (d[k][0] * yDotK[0][i] + d[k][1] * yDotK[5][i] + d[k][2] * yDotK[6][i] + - d[k][3] * yDotK[7][i] + d[k][4] * yDotK[8][i] + d[k][5] * yDotK[9][i] + - d[k][6] * yDotK[10][i] + d[k][7] * yDotK[11][i] + d[k][8] * yDotK[12][i] + - d[k][9] * yDotKLast[0][i] + - d[k][10] * yDotKLast[1][i] + - d[k][11] * yDotKLast[2][i]); - } + final double yDot1 = yDotK[0][i]; + final double yDot6 = yDotK[5][i]; + final double yDot7 = yDotK[6][i]; + final double yDot8 = yDotK[7][i]; + final double yDot9 = yDotK[8][i]; + final double yDot10 = yDotK[9][i]; + final double yDot11 = yDotK[10][i]; + final double yDot12 = yDotK[11][i]; + final double yDot13 = yDotK[12][i]; + final double yDot14 = yDotKLast[0][i]; + final double yDot15 = yDotKLast[1][i]; + final double yDot16 = yDotKLast[2][i]; + v[0][i] = b_01 * yDot1 + b_06 * yDot6 + b_07 * yDot7 + + b_08 * yDot8 + b_09 * yDot9 + b_10 * yDot10 + + b_11 * yDot11 + b_12 * yDot12; + v[1][i] = yDot1 - v[0][i]; + v[2][i] = v[0][i] - v[1][i] - yDotK[12][i]; + for (int k = 0; k < d.length; ++k) { + v[k+3][i] = d[k][0] * yDot1 + d[k][1] * yDot6 + d[k][2] * yDot7 + + d[k][3] * yDot8 + d[k][4] * yDot9 + d[k][5] * yDot10 + + d[k][6] * yDot11 + d[k][7] * yDot12 + d[k][8] * yDot13 + + d[k][9] * yDot14 + d[k][10] * yDot15 + d[k][11] * yDot16; + } } vectorsInitialized = true; } - double eta = oneMinusThetaH / h; + final double eta = 1 - theta; + final double twoTheta = 2 * theta; + final double theta2 = theta * theta; + final double dot1 = 1 - twoTheta; + final double dot2 = theta * (2 - 3 * theta); + final double dot3 = twoTheta * (1 + theta * (twoTheta -3)); + final double dot4 = theta2 * (3 + theta * (5 * theta - 8)); + final double dot5 = theta2 * (3 + theta * (-12 + theta * (15 - 6 * theta))); + final double dot6 = theta2 * theta * (4 + theta * (-15 + theta * (18 - 7 * theta))); for (int i = 0; i < interpolatedState.length; ++i) { - interpolatedState[i] = - currentState[i] - eta * (v[0][i] - theta * (v[1][i] + - theta * (v[2][i] + eta * (v[3][i] + theta * (v[4][i] + - eta * (v[5][i] + theta * (v[6][i]))))))); + interpolatedState[i] = currentState[i] - + oneMinusThetaH * (v[0][i] - + theta * (v[1][i] + + theta * (v[2][i] + + eta * (v[3][i] + + theta * (v[4][i] + + eta * (v[5][i] + + theta * (v[6][i]))))))); + interpolatedDerivatives[i] = v[0][i] + dot1 * v[1][i] + dot2 * v[2][i] + + dot3 * v[3][i] + dot4 * v[4][i] + + dot5 * v[5][i] + dot6 * v[6][i]; } } - /** - * Really finalize the step. - * Perform the last 3 functions evaluations (k14, k15, k16) - * @throws DerivativeException this exception is propagated to the caller if the - * underlying user function triggers one - */ + /** {@inheritDoc} */ protected void doFinalize() throws DerivativeException { @@ -218,7 +210,7 @@ class DormandPrince853StepInterpolator } double s; - double[] yTmp = new double[currentState.length]; + final double[] yTmp = new double[currentState.length]; // k14 for (int j = 0; j < currentState.length; ++j) { @@ -251,18 +243,15 @@ class DormandPrince853StepInterpolator } - /** Save the state of the instance. - * @param out stream where to save the state - * @exception IOException in case of write error - */ - public void writeExternal(ObjectOutput out) + /** {@inheritDoc} */ + public void writeExternal(final ObjectOutput out) throws IOException { try { // save the local attributes finalizeStep(); } catch (DerivativeException e) { - throw new IOException(e.getMessage()); + throw MathRuntimeException.createIOException(e); } out.writeInt(currentState.length); for (int i = 0; i < currentState.length; ++i) { @@ -276,16 +265,13 @@ class DormandPrince853StepInterpolator } - /** Read the state of the instance. - * @param in stream where to read the state from - * @exception IOException in case of read error - */ - public void readExternal(ObjectInput in) + /** {@inheritDoc} */ + public void readExternal(final ObjectInput in) throws IOException { // read the local attributes yDotKLast = new double[3][]; - int dimension = in.readInt(); + final int dimension = in.readInt(); yDotKLast[0] = new double[dimension]; yDotKLast[1] = new double[dimension]; yDotKLast[2] = new double[dimension]; diff --git a/src/java/org/apache/commons/math/ode/EmbeddedRungeKuttaIntegrator.java b/src/java/org/apache/commons/math/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java similarity index 69% rename from src/java/org/apache/commons/math/ode/EmbeddedRungeKuttaIntegrator.java rename to src/java/org/apache/commons/math/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java index 585846a42..63a65ee20 100644 --- a/src/java/org/apache/commons/math/ode/EmbeddedRungeKuttaIntegrator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java @@ -15,7 +15,15 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; + +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderDifferentialEquations; +import org.apache.commons.math.ode.IntegratorException; +import org.apache.commons.math.ode.events.CombinedEventsManager; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; +import org.apache.commons.math.ode.sampling.DummyStepInterpolator; +import org.apache.commons.math.ode.sampling.StepHandler; /** * This class implements the common part of all embedded Runge-Kutta @@ -57,7 +65,11 @@ package org.apache.commons.math.ode; public abstract class EmbeddedRungeKuttaIntegrator extends AdaptiveStepsizeIntegrator { + /** Serializable version identifier. */ + private static final long serialVersionUID = -8436701741819010959L; + /** Build a Runge-Kutta integrator with the given Butcher array. + * @param name name of the method * @param fsal indicate that the method is an fsal * @param c time steps from Butcher array (without the first zero) * @param a internal weights from Butcher array (without the first empty row) @@ -70,14 +82,14 @@ public abstract class EmbeddedRungeKuttaIntegrator * @param scalAbsoluteTolerance allowed absolute error * @param scalRelativeTolerance allowed relative error */ - protected EmbeddedRungeKuttaIntegrator(boolean fsal, - double[] c, double[][] a, double[] b, - RungeKuttaStepInterpolator prototype, - double minStep, double maxStep, - double scalAbsoluteTolerance, - double scalRelativeTolerance) { + protected EmbeddedRungeKuttaIntegrator(final String name, final boolean fsal, + final double[] c, final double[][] a, final double[] b, + final RungeKuttaStepInterpolator prototype, + final double minStep, final double maxStep, + final double scalAbsoluteTolerance, + final double scalRelativeTolerance) { - super(minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance); + super(name, minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance); this.fsal = fsal; this.c = c; @@ -95,6 +107,7 @@ public abstract class EmbeddedRungeKuttaIntegrator } /** Build a Runge-Kutta integrator with the given Butcher array. + * @param name name of the method * @param fsal indicate that the method is an fsal * @param c time steps from Butcher array (without the first zero) * @param a internal weights from Butcher array (without the first empty row) @@ -107,14 +120,14 @@ public abstract class EmbeddedRungeKuttaIntegrator * @param vecAbsoluteTolerance allowed absolute error * @param vecRelativeTolerance allowed relative error */ - protected EmbeddedRungeKuttaIntegrator(boolean fsal, - double[] c, double[][] a, double[] b, - RungeKuttaStepInterpolator prototype, - double minStep, double maxStep, - double[] vecAbsoluteTolerance, - double[] vecRelativeTolerance) { + protected EmbeddedRungeKuttaIntegrator(final String name, final boolean fsal, + final double[] c, final double[][] a, final double[] b, + final RungeKuttaStepInterpolator prototype, + final double minStep, final double maxStep, + final double[] vecAbsoluteTolerance, + final double[] vecRelativeTolerance) { - super(minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance); + super(name, minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance); this.fsal = fsal; this.c = c; @@ -131,11 +144,6 @@ public abstract class EmbeddedRungeKuttaIntegrator } - /** Get the name of the method. - * @return name of the method - */ - public abstract String getName(); - /** Get the order of the method. * @return order of the method */ @@ -151,49 +159,34 @@ public abstract class EmbeddedRungeKuttaIntegrator /** Set the safety factor for stepsize control. * @param safety safety factor */ - public void setSafety(double safety) { + public void setSafety(final double safety) { this.safety = safety; } - /** Integrate the differential equations up to the given time. - *

This method solves an Initial Value Problem (IVP).

- *

Since this method stores some internal state variables made - * available in its public interface during integration ({@link - * #getCurrentSignedStepsize()}), it is not thread-safe.

- * @param equations differential equations to integrate - * @param t0 initial time - * @param y0 initial value of the state vector at t0 - * @param t target time for the integration - * (can be set to a value smaller than t0 for backward integration) - * @param y placeholder where to put the state vector at each successful - * step (and hence at the end of integration), can be the same object as y0 - * @throws IntegratorException if the integrator cannot perform integration - * @throws DerivativeException this exception is propagated to the caller if - * the underlying user function triggers one - */ - public void integrate(FirstOrderDifferentialEquations equations, - double t0, double[] y0, - double t, double[] y) + /** {@inheritDoc} */ + public double integrate(final FirstOrderDifferentialEquations equations, + final double t0, final double[] y0, + final double t, final double[] y) throws DerivativeException, IntegratorException { sanityChecks(equations, t0, y0, t, y); - boolean forward = (t > t0); + final boolean forward = (t > t0); // create some internal working arrays - int stages = c.length + 1; + final int stages = c.length + 1; if (y != y0) { System.arraycopy(y0, 0, y, 0, y0.length); } - double[][] yDotK = new double[stages][]; + final double[][] yDotK = new double[stages][]; for (int i = 0; i < stages; ++i) { yDotK [i] = new double[y0.length]; } - double[] yTmp = new double[y0.length]; + final double[] yTmp = new double[y0.length]; // set up an interpolator sharing the integrator arrays AbstractStepInterpolator interpolator; - if (handler.requiresDenseOutput() || (! switchesHandler.isEmpty())) { - RungeKuttaStepInterpolator rki = (RungeKuttaStepInterpolator) prototype.copy(); + if (requiresDenseOutput() || (! eventsHandlersManager.isEmpty())) { + final RungeKuttaStepInterpolator rki = (RungeKuttaStepInterpolator) prototype.copy(); rki.reinitialize(equations, yTmp, yDotK, forward); interpolator = rki; } else { @@ -201,12 +194,18 @@ public abstract class EmbeddedRungeKuttaIntegrator } interpolator.storeTime(t0); - stepStart = t0; + // set up integration control objects + stepStart = t0; double hNew = 0; boolean firstTime = true; - boolean lastStep; - handler.reset(); - do { + for (StepHandler handler : stepHandlers) { + handler.reset(); + } + CombinedEventsManager manager = addEndTimeChecker(t0, t, eventsHandlersManager); + boolean lastStep = false; + + // main integration loop + while (!lastStep) { interpolator.shift(); @@ -219,7 +218,7 @@ public abstract class EmbeddedRungeKuttaIntegrator } if (firstTime) { - double[] scale; + final double[] scale; if (vecAbsoluteTolerance != null) { scale = vecAbsoluteTolerance; } else { @@ -235,12 +234,6 @@ public abstract class EmbeddedRungeKuttaIntegrator stepSize = hNew; - // step adjustment near bounds - if ((forward && (stepStart + stepSize > t)) || - ((! forward) && (stepStart + stepSize < t))) { - stepSize = t - stepStart; - } - // next stages for (int k = 1; k < stages; ++k) { @@ -269,11 +262,11 @@ public abstract class EmbeddedRungeKuttaIntegrator error = estimateError(yDotK, y, yTmp, stepSize); if (error <= 1.0) { - // Switching functions handling + // discrete events handling interpolator.storeTime(stepStart + stepSize); - if (switchesHandler.evaluateStep(interpolator)) { + if (manager.evaluateStep(interpolator)) { // reject the step to match exactly the next switch time - hNew = switchesHandler.getEventTime() - stepStart; + hNew = manager.getEventTime() - stepStart; } else { // accept the step loop = false; @@ -281,27 +274,25 @@ public abstract class EmbeddedRungeKuttaIntegrator } else { // reject the step and attempt to reduce error by stepsize control - double factor = Math.min(maxGrowth, - Math.max(minReduction, - safety * Math.pow(error, exp))); - hNew = filterStep(stepSize * factor, false); + final double factor = + Math.min(maxGrowth, + Math.max(minReduction, safety * Math.pow(error, exp))); + hNew = filterStep(stepSize * factor, forward, false); } } // the step has been accepted - double nextStep = stepStart + stepSize; + final double nextStep = stepStart + stepSize; System.arraycopy(yTmp, 0, y, 0, y0.length); - switchesHandler.stepAccepted(nextStep, y); - if (switchesHandler.stop()) { - lastStep = true; - } else { - lastStep = forward ? (nextStep >= t) : (nextStep <= t); - } + manager.stepAccepted(nextStep, y); + lastStep = manager.stop(); // provide the step data to the step handler interpolator.storeTime(nextStep); - handler.handleStep(interpolator, lastStep); + for (StepHandler handler : stepHandlers) { + handler.handleStep(interpolator, lastStep); + } stepStart = nextStep; if (fsal) { @@ -309,26 +300,33 @@ public abstract class EmbeddedRungeKuttaIntegrator System.arraycopy(yDotK[stages - 1], 0, yDotK[0], 0, y0.length); } - if (switchesHandler.reset(stepStart, y) && ! lastStep) { - // some switching function has triggered changes that + if (manager.reset(stepStart, y) && ! lastStep) { + // some event handler has triggered changes that // invalidate the derivatives, we need to recompute them equations.computeDerivatives(stepStart, y, yDotK[0]); } if (! lastStep) { + // in some rare cases we may get here with stepSize = 0, for example + // when an event occurs at integration start, reducing the first step + // to zero; we have to reset the step to some safe non zero value + stepSize = filterStep(stepSize, forward, true); + // stepsize control for next step - double factor = Math.min(maxGrowth, - Math.max(minReduction, - safety * Math.pow(error, exp))); - double scaledH = stepSize * factor; - double nextT = stepStart + scaledH; - boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t); - hNew = filterStep(scaledH, nextIsLast); + final double factor = Math.min(maxGrowth, + Math.max(minReduction, + safety * Math.pow(error, exp))); + final double scaledH = stepSize * factor; + final double nextT = stepStart + scaledH; + final boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t); + hNew = filterStep(scaledH, forward, nextIsLast); } - } while (! lastStep); + } + final double stopTime = stepStart; resetInternalState(); + return stopTime; } @@ -342,7 +340,7 @@ public abstract class EmbeddedRungeKuttaIntegrator /** Set the minimal reduction factor for stepsize control. * @param minReduction minimal reduction factor */ - public void setMinReduction(double minReduction) { + public void setMinReduction(final double minReduction) { this.minReduction = minReduction; } @@ -356,7 +354,7 @@ public abstract class EmbeddedRungeKuttaIntegrator /** Set the maximal growth factor for stepsize control. * @param maxGrowth maximal growth factor */ - public void setMaxGrowth(double maxGrowth) { + public void setMaxGrowth(final double maxGrowth) { this.maxGrowth = maxGrowth; } diff --git a/src/java/org/apache/commons/math/ode/EulerIntegrator.java b/src/java/org/apache/commons/math/ode/nonstiff/EulerIntegrator.java similarity index 86% rename from src/java/org/apache/commons/math/ode/EulerIntegrator.java rename to src/java/org/apache/commons/math/ode/nonstiff/EulerIntegrator.java index 6ad7c3e75..f8463d6a3 100644 --- a/src/java/org/apache/commons/math/ode/EulerIntegrator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/EulerIntegrator.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; /** * This class implements a simple Euler integrator for Ordinary @@ -48,8 +48,8 @@ package org.apache.commons.math.ode; public class EulerIntegrator extends RungeKuttaIntegrator { - /** Integrator method name. */ - private static final String methodName = "Euler"; + /** Serializable version identifier. */ + private static final long serialVersionUID = 1828811360890387657L; /** Time steps Butcher array. */ private static final double[] c = { @@ -68,15 +68,8 @@ public class EulerIntegrator * Build an Euler integrator with the given step. * @param step integration step */ - public EulerIntegrator(double step) { - super(c, a, b, new EulerStepInterpolator(), step); - } - - /** Get the name of the method. - * @return name of the method - */ - public String getName() { - return methodName; + public EulerIntegrator(final double step) { + super("Euler", c, a, b, new EulerStepInterpolator(), step); } } diff --git a/src/java/org/apache/commons/math/ode/EulerStepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/EulerStepInterpolator.java similarity index 72% rename from src/java/org/apache/commons/math/ode/EulerStepInterpolator.java rename to src/java/org/apache/commons/math/ode/nonstiff/EulerStepInterpolator.java index 6fabe0d62..26b92857e 100644 --- a/src/java/org/apache/commons/math/ode/EulerStepInterpolator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/EulerStepInterpolator.java @@ -15,12 +15,16 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; + +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; +import org.apache.commons.math.ode.sampling.StepInterpolator; /** * This class implements a linear interpolator for step. * - *

This interpolator allow to compute dense output inside the last + *

This interpolator computes dense output inside the last * step computed. The interpolation equation is consistent with the * integration scheme : * @@ -39,6 +43,9 @@ package org.apache.commons.math.ode; class EulerStepInterpolator extends RungeKuttaStepInterpolator { + /** Serializable version identifier */ + private static final long serialVersionUID = -7179861704951334960L; + /** Simple constructor. * This constructor builds an instance that is not usable yet, the * {@link AbstractStepInterpolator#reinitialize} method should be called @@ -56,39 +63,26 @@ class EulerStepInterpolator * copy: its arrays are separated from the original arrays of the * instance */ - public EulerStepInterpolator(EulerStepInterpolator interpolator) { + public EulerStepInterpolator(final EulerStepInterpolator interpolator) { super(interpolator); } - /** Really copy the finalized instance. - * @return a copy of the finalized instance - */ + /** {@inheritDoc} */ protected StepInterpolator doCopy() { return new EulerStepInterpolator(this); } - /** Compute the state at the interpolated time. - * This is the main processing method that should be implemented by - * the derived classes to perform the interpolation. - * @param theta normalized interpolation abscissa within the step - * (theta is zero at the previous time step and one at the current time step) - * @param oneMinusThetaH time gap between the interpolated time and - * the current time - * @throws DerivativeException this exception is propagated to the caller if the - * underlying user function triggers one - */ - protected void computeInterpolatedState(double theta, - double oneMinusThetaH) + /** {@inheritDoc} */ + protected void computeInterpolatedState(final double theta, + final double oneMinusThetaH) throws DerivativeException { for (int i = 0; i < interpolatedState.length; ++i) { interpolatedState[i] = currentState[i] - oneMinusThetaH * yDotK[0][i]; } + System.arraycopy(yDotK[0], 0, interpolatedDerivatives, 0, interpolatedDerivatives.length); } - /** Serializable version identifier */ - private static final long serialVersionUID = -7179861704951334960L; - } diff --git a/src/java/org/apache/commons/math/ode/GillIntegrator.java b/src/java/org/apache/commons/math/ode/nonstiff/GillIntegrator.java similarity index 86% rename from src/java/org/apache/commons/math/ode/GillIntegrator.java rename to src/java/org/apache/commons/math/ode/nonstiff/GillIntegrator.java index a791ea167..03de17f02 100644 --- a/src/java/org/apache/commons/math/ode/GillIntegrator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/GillIntegrator.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; /** * This class implements the Gill fourth order Runge-Kutta @@ -44,8 +44,8 @@ package org.apache.commons.math.ode; public class GillIntegrator extends RungeKuttaIntegrator { - /** Integrator method name. */ - private static final String methodName = "Gill"; + /** Serializable version identifier. */ + private static final long serialVersionUID = 5566682259665027132L; /** Time steps Butcher array. */ private static final double[] c = { @@ -68,15 +68,8 @@ public class GillIntegrator * Build a fourth-order Gill integrator with the given step. * @param step integration step */ - public GillIntegrator(double step) { - super(c, a, b, new GillStepInterpolator(), step); - } - - /** Get the name of the method. - * @return name of the method - */ - public String getName() { - return methodName; + public GillIntegrator(final double step) { + super("Gill", c, a, b, new GillStepInterpolator(), step); } } diff --git a/src/java/org/apache/commons/math/ode/GillStepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/GillStepInterpolator.java similarity index 65% rename from src/java/org/apache/commons/math/ode/GillStepInterpolator.java rename to src/java/org/apache/commons/math/ode/nonstiff/GillStepInterpolator.java index 9bf536185..d7a94d5b5 100644 --- a/src/java/org/apache/commons/math/ode/GillStepInterpolator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/GillStepInterpolator.java @@ -15,7 +15,11 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; + +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; +import org.apache.commons.math.ode.sampling.StepInterpolator; /** * This class implements a step interpolator for the Gill fourth @@ -61,45 +65,46 @@ class GillStepInterpolator * copy: its arrays are separated from the original arrays of the * instance */ - public GillStepInterpolator(GillStepInterpolator interpolator) { + public GillStepInterpolator(final GillStepInterpolator interpolator) { super(interpolator); } - /** Really copy the finalized instance. - * @return a copy of the finalized instance - */ + /** {@inheritDoc} */ protected StepInterpolator doCopy() { return new GillStepInterpolator(this); } - /** Compute the state at the interpolated time. - * This is the main processing method that should be implemented by - * the derived classes to perform the interpolation. - * @param theta normalized interpolation abscissa within the step - * (theta is zero at the previous time step and one at the current time step) - * @param oneMinusThetaH time gap between the interpolated time and - * the current time - * @throws DerivativeException this exception is propagated to the caller if the - * underlying user function triggers one - */ - protected void computeInterpolatedState(double theta, - double oneMinusThetaH) + /** {@inheritDoc} */ + protected void computeInterpolatedState(final double theta, + final double oneMinusThetaH) throws DerivativeException { - double fourTheta = 4 * theta; - double s = oneMinusThetaH / 6.0; - double soMt = s * (1 - theta); - double c23 = soMt * (1 + 2 * theta); - double coeff1 = soMt * (1 - fourTheta); - double coeff2 = c23 * tMq; - double coeff3 = c23 * tPq; - double coeff4 = s * (1 + theta * (1 + fourTheta)); + final double twoTheta = 2 * theta; + final double fourTheta = 4 * theta; + final double s = oneMinusThetaH / 6.0; + final double oMt = 1 - theta; + final double soMt = s * oMt; + final double c23 = soMt * (1 + twoTheta); + final double coeff1 = soMt * (1 - fourTheta); + final double coeff2 = c23 * tMq; + final double coeff3 = c23 * tPq; + final double coeff4 = s * (1 + theta * (1 + fourTheta)); + final double coeffDot1 = theta * (twoTheta - 3) + 1; + final double cDot23 = theta * oMt; + final double coeffDot2 = cDot23 * tMq; + final double coeffDot3 = cDot23 * tPq; + final double coeffDot4 = theta * (twoTheta - 1); for (int i = 0; i < interpolatedState.length; ++i) { - interpolatedState[i] = currentState[i] - - coeff1 * yDotK[0][i] - coeff2 * yDotK[1][i] - - coeff3 * yDotK[2][i] - coeff4 * yDotK[3][i]; + final double yDot1 = yDotK[0][i]; + final double yDot2 = yDotK[1][i]; + final double yDot3 = yDotK[2][i]; + final double yDot4 = yDotK[3][i]; + interpolatedState[i] = + currentState[i] - coeff1 * yDot1 - coeff2 * yDot2 - coeff3 * yDot3 - coeff4 * yDot4; + interpolatedDerivatives[i] = + coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 + coeffDot4 * yDot4; } } diff --git a/src/java/org/apache/commons/math/ode/GraggBulirschStoerIntegrator.java b/src/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerIntegrator.java similarity index 81% rename from src/java/org/apache/commons/math/ode/GraggBulirschStoerIntegrator.java rename to src/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerIntegrator.java index 6db761f24..ceb387002 100644 --- a/src/java/org/apache/commons/math/ode/GraggBulirschStoerIntegrator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerIntegrator.java @@ -15,7 +15,15 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; + +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderDifferentialEquations; +import org.apache.commons.math.ode.IntegratorException; +import org.apache.commons.math.ode.events.EventHandler; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; +import org.apache.commons.math.ode.sampling.DummyStepInterpolator; +import org.apache.commons.math.ode.sampling.StepHandler; /** * This class implements a Gragg-Bulirsch-Stoer integrator for @@ -87,8 +95,11 @@ package org.apache.commons.math.ode; public class GraggBulirschStoerIntegrator extends AdaptiveStepsizeIntegrator { + /** Serializable version identifier. */ + private static final long serialVersionUID = 7364884082146325264L; + /** Integrator method name. */ - private static final String methodName = "Gragg-Bulirsch-Stoer"; + private static final String METHOD_NAME = "Gragg-Bulirsch-Stoer"; /** Simple constructor. * Build a Gragg-Bulirsch-Stoer integrator with the given step @@ -101,11 +112,12 @@ public class GraggBulirschStoerIntegrator * @param scalAbsoluteTolerance allowed absolute error * @param scalRelativeTolerance allowed relative error */ - public GraggBulirschStoerIntegrator(double minStep, double maxStep, - double scalAbsoluteTolerance, - double scalRelativeTolerance) { - super(minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance); - denseOutput = (handler.requiresDenseOutput() || (! switchesHandler.isEmpty())); + public GraggBulirschStoerIntegrator(final double minStep, final double maxStep, + final double scalAbsoluteTolerance, + final double scalRelativeTolerance) { + super(METHOD_NAME, minStep, maxStep, + scalAbsoluteTolerance, scalRelativeTolerance); + denseOutput = requiresDenseOutput() || (! eventsHandlersManager.isEmpty()); setStabilityCheck(true, -1, -1, -1); setStepsizeControl(-1, -1, -1, -1); setOrderControl(-1, -1, -1); @@ -123,11 +135,12 @@ public class GraggBulirschStoerIntegrator * @param vecAbsoluteTolerance allowed absolute error * @param vecRelativeTolerance allowed relative error */ - public GraggBulirschStoerIntegrator(double minStep, double maxStep, - double[] vecAbsoluteTolerance, - double[] vecRelativeTolerance) { - super(minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance); - denseOutput = (handler.requiresDenseOutput() || (! switchesHandler.isEmpty())); + public GraggBulirschStoerIntegrator(final double minStep, final double maxStep, + final double[] vecAbsoluteTolerance, + final double[] vecRelativeTolerance) { + super(METHOD_NAME, minStep, maxStep, + vecAbsoluteTolerance, vecRelativeTolerance); + denseOutput = requiresDenseOutput() || (! eventsHandlersManager.isEmpty()); setStabilityCheck(true, -1, -1, -1); setStepsizeControl(-1, -1, -1, -1); setOrderControl(-1, -1, -1); @@ -152,9 +165,9 @@ public class GraggBulirschStoerIntegrator * failure (the factor is reset to default if lower than 0.0001 or * greater than 0.9999) */ - public void setStabilityCheck(boolean performTest, - int maxIter, int maxChecks, - double stabilityReduction) { + public void setStabilityCheck(final boolean performTest, + final int maxIter, final int maxChecks, + final double stabilityReduction) { this.performTest = performTest; this.maxIter = (maxIter <= 0) ? 2 : maxIter; @@ -192,8 +205,8 @@ public class GraggBulirschStoerIntegrator * @param stepControl4 fourth stepsize control factor (the factor * is reset to default if lower than 1.0001 or greater than 999.9) */ - public void setStepsizeControl(double stepControl1, double stepControl2, - double stepControl3, double stepControl4) { + public void setStepsizeControl(final double stepControl1, final double stepControl2, + final double stepControl3, final double stepControl4) { if ((stepControl1 < 0.0001) || (stepControl1 > 0.9999)) { this.stepControl1 = 0.65; @@ -244,8 +257,8 @@ public class GraggBulirschStoerIntegrator * @param orderControl2 second order control factor (the factor * is reset to default if lower than 0.0001 or greater than 0.9999) */ - public void setOrderControl(int maxOrder, - double orderControl1, double orderControl2) { + public void setOrderControl(final int maxOrder, + final double orderControl1, final double orderControl2) { if ((maxOrder <= 6) || (maxOrder % 2 != 0)) { this.maxOrder = 18; @@ -268,36 +281,24 @@ public class GraggBulirschStoerIntegrator } - /** Set the step handler for this integrator. - * The handler will be called by the integrator for each accepted - * step. - * @param handler handler for the accepted steps - */ - public void setStepHandler (StepHandler handler) { + /** {@inheritDoc} */ + public void addStepHandler (final StepHandler handler) { - super.setStepHandler(handler); - denseOutput = (handler.requiresDenseOutput() || (! switchesHandler.isEmpty())); + super.addStepHandler(handler); + denseOutput = requiresDenseOutput() || (! eventsHandlersManager.isEmpty()); // reinitialize the arrays initializeArrays(); } - /** Add a switching function to the integrator. - * @param function switching function - * @param maxCheckInterval maximal time interval between switching - * function checks (this interval prevents missing sign changes in - * case the integration steps becomes very large) - * @param convergence convergence threshold in the event time search - * @param maxIterationCount upper limit of the iteration count in - * the event time search - */ - public void addSwitchingFunction(SwitchingFunction function, - double maxCheckInterval, - double convergence, - int maxIterationCount) { - super.addSwitchingFunction(function, maxCheckInterval, convergence, maxIterationCount); - denseOutput = (handler.requiresDenseOutput() || (! switchesHandler.isEmpty())); + /** {@inheritDoc} */ + public void addEventHandler(final EventHandler function, + final double maxCheckInterval, + final double convergence, + final int maxIterationCount) { + super.addEventHandler(function, maxCheckInterval, convergence, maxIterationCount); + denseOutput = requiresDenseOutput() || (! eventsHandlersManager.isEmpty()); // reinitialize the arrays initializeArrays(); @@ -307,7 +308,7 @@ public class GraggBulirschStoerIntegrator /** Initialize the integrator internal arrays. */ private void initializeArrays() { - int size = maxOrder / 2; + final int size = maxOrder / 2; if ((sequence == null) || (sequence.length != size)) { // all arrays should be reallocated with the right size @@ -341,7 +342,7 @@ public class GraggBulirschStoerIntegrator for (int k = 0; k < size; ++k) { coeff[k] = (k > 0) ? new double[k] : null; for (int l = 0; l < k; ++l) { - double ratio = ((double) sequence[k]) / sequence[k-l-1]; + final double ratio = ((double) sequence[k]) / sequence[k-l-1]; coeff[k][l] = 1.0 / (ratio * ratio - 1.0); } } @@ -358,8 +359,8 @@ public class GraggBulirschStoerIntegrator * @param mudif interpolation order control parameter (the parameter * is reset to default if <= 0 or >= 7) */ - public void setInterpolationControl(boolean useInterpolationError, - int mudif) { + public void setInterpolationControl(final boolean useInterpolationError, + final int mudif) { this.useInterpolationError = useInterpolationError; @@ -371,27 +372,20 @@ public class GraggBulirschStoerIntegrator } - /** Get the name of the method. - * @return name of the method - */ - public String getName() { - return methodName; - } - /** Update scaling array. * @param y1 first state vector to use for scaling * @param y2 second state vector to use for scaling * @param scale scaling array to update */ - private void rescale(double[] y1, double[] y2, double[] scale) { + private void rescale(final double[] y1, final double[] y2, final double[] scale) { if (vecAbsoluteTolerance == null) { for (int i = 0; i < scale.length; ++i) { - double yi = Math.max(Math.abs(y1[i]), Math.abs(y2[i])); + final double yi = Math.max(Math.abs(y1[i]), Math.abs(y2[i])); scale[i] = scalAbsoluteTolerance + scalRelativeTolerance * yi; } } else { for (int i = 0; i < scale.length; ++i) { - double yi = Math.max(Math.abs(y1[i]), Math.abs(y2[i])); + final double yi = Math.max(Math.abs(y1[i]), Math.abs(y2[i])); scale[i] = vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * yi; } } @@ -415,16 +409,16 @@ public class GraggBulirschStoerIntegrator * @throws DerivativeException this exception is propagated to the caller if the * underlying user function triggers one */ - private boolean tryStep(FirstOrderDifferentialEquations equations, - double t0, double[] y0, double step, int k, - double[] scale, double[][] f, - double[] yMiddle, double[] yEnd, - double[] yTmp) + private boolean tryStep(final FirstOrderDifferentialEquations equations, + final double t0, final double[] y0, final double step, final int k, + final double[] scale, final double[][] f, + final double[] yMiddle, final double[] yEnd, + final double[] yTmp) throws DerivativeException { - int n = sequence[k]; - double subStep = step / n; - double subStep2 = 2 * subStep; + final int n = sequence[k]; + final double subStep = step / n; + final double subStep2 = 2 * subStep; // first substep double t = t0 + subStep; @@ -444,7 +438,7 @@ public class GraggBulirschStoerIntegrator t += subStep; for (int i = 0; i < y0.length; ++i) { - double middle = yEnd[i]; + final double middle = yEnd[i]; yEnd[i] = yTmp[i] + subStep2 * f[j][i]; yTmp[i] = middle; } @@ -455,12 +449,12 @@ public class GraggBulirschStoerIntegrator if (performTest && (j <= maxChecks) && (k < maxIter)) { double initialNorm = 0.0; for (int l = 0; l < y0.length; ++l) { - double ratio = f[0][l] / scale[l]; + final double ratio = f[0][l] / scale[l]; initialNorm += ratio * ratio; } double deltaNorm = 0.0; for (int l = 0; l < y0.length; ++l) { - double ratio = (f[j+1][l] - f[0][l]) / scale[l]; + final double ratio = (f[j+1][l] - f[0][l]) / scale[l]; deltaNorm += ratio * ratio; } if (deltaNorm > 4 * Math.max(1.0e-15, initialNorm)) { @@ -486,7 +480,8 @@ public class GraggBulirschStoerIntegrator * triangle, without the last element * @param last last element */ - private void extrapolate(int offset, int k, double[][] diag, double[] last) { + private void extrapolate(final int offset, final int k, + final double[][] diag, final double[] last) { // update the diagonal for (int j = 1; j < k; ++j) { @@ -504,43 +499,28 @@ public class GraggBulirschStoerIntegrator } } - /** Integrate the differential equations up to the given time. - *

This method solves an Initial Value Problem (IVP).

- *

Since this method stores some internal state variables made - * available in its public interface during integration ({@link - * #getCurrentSignedStepsize()}), it is not thread-safe.

- * @param equations differential equations to integrate - * @param t0 initial time - * @param y0 initial value of the state vector at t0 - * @param t target time for the integration - * (can be set to a value smaller than t0 for backward integration) - * @param y placeholder where to put the state vector at each successful - * step (and hence at the end of integration), can be the same object as y0 - * @throws IntegratorException if the integrator cannot perform integration - * @throws DerivativeException this exception is propagated to the caller if - * the underlying user function triggers one - */ - public void integrate(FirstOrderDifferentialEquations equations, - double t0, double[] y0, double t, double[] y) + /** {@inheritDoc} */ + public double integrate(final FirstOrderDifferentialEquations equations, + final double t0, final double[] y0, final double t, final double[] y) throws DerivativeException, IntegratorException { sanityChecks(equations, t0, y0, t, y); - boolean forward = (t > t0); + final boolean forward = (t > t0); // create some internal working arrays - double[] yDot0 = new double[y0.length]; - double[] y1 = new double[y0.length]; - double[] yTmp = new double[y0.length]; - double[] yTmpDot = new double[y0.length]; + final double[] yDot0 = new double[y0.length]; + final double[] y1 = new double[y0.length]; + final double[] yTmp = new double[y0.length]; + final double[] yTmpDot = new double[y0.length]; - double[][] diagonal = new double[sequence.length-1][]; - double[][] y1Diag = new double[sequence.length-1][]; + final double[][] diagonal = new double[sequence.length-1][]; + final double[][] y1Diag = new double[sequence.length-1][]; for (int k = 0; k < sequence.length-1; ++k) { diagonal[k] = new double[y0.length]; y1Diag[k] = new double[y0.length]; } - double[][][] fk = new double[sequence.length][][]; + final double[][][] fk = new double[sequence.length][][]; for (int k = 0; k < sequence.length; ++k) { fk[k] = new double[sequence[k] + 1][]; @@ -572,19 +552,19 @@ public class GraggBulirschStoerIntegrator } // initial scaling - double[] scale = new double[y0.length]; + final double[] scale = new double[y0.length]; rescale(y, y, scale); // initial order selection - double tol = + final double tol = (vecRelativeTolerance == null) ? scalRelativeTolerance : vecRelativeTolerance[0]; - double log10R = Math.log(Math.max(1.0e-10, tol)) / Math.log(10.0); + final double log10R = Math.log(Math.max(1.0e-10, tol)) / Math.log(10.0); int targetIter = Math.max(1, Math.min(sequence.length - 2, (int) Math.floor(0.5 - 0.6 * log10R))); // set up an interpolator sharing the integrator arrays AbstractStepInterpolator interpolator = null; - if (denseOutput || (! switchesHandler.isEmpty())) { + if (denseOutput || (! eventsHandlersManager.isEmpty())) { interpolator = new GraggBulirschStoerStepInterpolator(y, yDot0, y1, yDot1, yMidDots, forward); @@ -601,7 +581,9 @@ public class GraggBulirschStoerIntegrator boolean newStep = true; boolean lastStep = false; boolean firstStepAlreadyComputed = false; - handler.reset(); + for (StepHandler handler : stepHandlers) { + handler.reset(); + } costPerTimeUnit[0] = 0; while (! lastStep) { @@ -640,7 +622,7 @@ public class GraggBulirschStoerIntegrator ((! forward) && (stepStart + stepSize < t))) { stepSize = t - stepStart; } - double nextT = stepStart + stepSize; + final double nextT = stepStart + stepSize; lastStep = forward ? (nextT >= t) : (nextT <= t); // iterate over several substep sizes @@ -656,7 +638,7 @@ public class GraggBulirschStoerIntegrator yTmp)) { // the stability check failed, we reduce the global step - hNew = Math.abs(filterStep(stepSize * stabilityReduction, false)); + hNew = Math.abs(filterStep(stepSize * stabilityReduction, forward, false)); reject = true; loop = false; @@ -673,14 +655,14 @@ public class GraggBulirschStoerIntegrator // estimate the error at the end of the step. error = 0; for (int j = 0; j < y0.length; ++j) { - double e = Math.abs(y1[j] - y1Diag[0][j]) / scale[j]; + final double e = Math.abs(y1[j] - y1Diag[0][j]) / scale[j]; error += e * e; } error = Math.sqrt(error / y0.length); if ((error > 1.0e15) || ((k > 1) && (error > maxError))) { // error is too big, we reduce the global step - hNew = Math.abs(filterStep(stepSize * stabilityReduction, false)); + hNew = Math.abs(filterStep(stepSize * stabilityReduction, forward, false)); reject = true; loop = false; } else { @@ -688,11 +670,11 @@ public class GraggBulirschStoerIntegrator maxError = Math.max(4 * error, 1.0); // compute optimal stepsize for this order - double exp = 1.0 / (2 * k + 1); + final double exp = 1.0 / (2 * k + 1); double fac = stepControl2 / Math.pow(error / stepControl1, exp); - double pow = Math.pow(stepControl3, exp); + final double pow = Math.pow(stepControl3, exp); fac = Math.max(pow / stepControl4, Math.min(1 / pow, fac)); - optimalStep[k] = Math.abs(filterStep(stepSize * fac, true)); + optimalStep[k] = Math.abs(filterStep(stepSize * fac, forward, true)); costPerTimeUnit[k] = costPerStep[k] / optimalStep[k]; // check convergence @@ -709,8 +691,8 @@ public class GraggBulirschStoerIntegrator // estimate if there is a chance convergence will // be reached on next iteration, using the // asymptotic evolution of error - double ratio = ((double) sequence [k] * sequence[k+1]) / - (sequence[0] * sequence[0]); + final double ratio = ((double) sequence [k] * sequence[k+1]) / + (sequence[0] * sequence[0]); if (error > ratio * ratio) { // we don't expect to converge on next iteration // we reject the step immediately and reduce order @@ -736,7 +718,7 @@ public class GraggBulirschStoerIntegrator // estimate if there is a chance convergence will // be reached on next iteration, using the // asymptotic evolution of error - double ratio = ((double) sequence[k+1]) / sequence[0]; + final double ratio = ((double) sequence[k+1]) / sequence[0]; if (error > ratio * ratio) { // we don't expect to converge on next iteration // we reject the step immediately @@ -790,12 +772,12 @@ public class GraggBulirschStoerIntegrator // derivative at end of step equations.computeDerivatives(stepStart + stepSize, y1, yDot1); - int mu = 2 * k - mudif + 3; + final int mu = 2 * k - mudif + 3; for (int l = 0; l < mu; ++l) { // derivative at middle point of the step - int l2 = l / 2; + final int l2 = l / 2; double factor = Math.pow(0.5 * sequence[l2], l); int middleIndex = fk[l2].length / 2; for (int i = 0; i < y0.length; ++i) { @@ -827,13 +809,13 @@ public class GraggBulirschStoerIntegrator if (mu >= 0) { // estimate the dense output coefficients - GraggBulirschStoerStepInterpolator gbsInterpolator + final GraggBulirschStoerStepInterpolator gbsInterpolator = (GraggBulirschStoerStepInterpolator) interpolator; gbsInterpolator.computeCoefficients(mu, stepSize); if (useInterpolationError) { // use the interpolation error to limit stepsize - double interpError = gbsInterpolator.estimateError(scale); + final double interpError = gbsInterpolator.estimateError(scale); hInt = Math.abs(stepSize / Math.max(Math.pow(interpError, 1.0 / (mu+4)), 0.01)); if (interpError > 10.0) { @@ -842,12 +824,12 @@ public class GraggBulirschStoerIntegrator } } - // Switching functions handling + // Discrete events handling if (!reject) { interpolator.storeTime(stepStart + stepSize); - if (switchesHandler.evaluateStep(interpolator)) { + if (eventsHandlersManager.evaluateStep(interpolator)) { reject = true; - hNew = Math.abs(switchesHandler.getEventTime() - stepStart); + hNew = Math.abs(eventsHandlersManager.getEventTime() - stepStart); } } @@ -864,20 +846,22 @@ public class GraggBulirschStoerIntegrator if (! reject) { // store end of step state - double nextStep = stepStart + stepSize; + final double nextStep = stepStart + stepSize; System.arraycopy(y1, 0, y, 0, y0.length); - switchesHandler.stepAccepted(nextStep, y); - if (switchesHandler.stop()) { + eventsHandlersManager.stepAccepted(nextStep, y); + if (eventsHandlersManager.stop()) { lastStep = true; } // provide the step data to the step handler interpolator.storeTime(nextStep); - handler.handleStep(interpolator, lastStep); + for (StepHandler handler : stepHandlers) { + handler.handleStep(interpolator, lastStep); + } stepStart = nextStep; - if (switchesHandler.reset(stepStart, y) && ! lastStep) { + if (eventsHandlersManager.reset(stepStart, y) && ! lastStep) { // some switching function has triggered changes that // invalidate the derivatives, we need to recompute them firstStepAlreadyComputed = false; @@ -919,13 +903,11 @@ public class GraggBulirschStoerIntegrator } else { if ((k < targetIter) && (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1])) { - hNew = filterStep(optimalStep[k] * - costPerStep[optimalIter+1] / costPerStep[k], - false); + hNew = filterStep(optimalStep[k] * costPerStep[optimalIter+1] / costPerStep[k], + forward, false); } else { - hNew = filterStep(optimalStep[k] * - costPerStep[optimalIter] / costPerStep[k], - false); + hNew = filterStep(optimalStep[k] * costPerStep[optimalIter] / costPerStep[k], + forward, false); } } @@ -953,6 +935,8 @@ public class GraggBulirschStoerIntegrator } + return stepStart; + } /** maximal order. */ diff --git a/src/java/org/apache/commons/math/ode/GraggBulirschStoerStepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerStepInterpolator.java similarity index 71% rename from src/java/org/apache/commons/math/ode/GraggBulirschStoerStepInterpolator.java rename to src/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerStepInterpolator.java index fc102d64c..3c55d9fc6 100644 --- a/src/java/org/apache/commons/math/ode/GraggBulirschStoerStepInterpolator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerStepInterpolator.java @@ -15,12 +15,17 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.IOException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; +import org.apache.commons.math.ode.sampling.StepInterpolator; + /** * This class implements an interpolator for the Gragg-Bulirsch-Stoer * integrator. @@ -102,7 +107,7 @@ class GraggBulirschStoerStepInterpolator * interpolation polynoms up to the given degree * @param maxDegree maximal degree to handle */ - private void resetTables(int maxDegree) { + private void resetTables(final int maxDegree) { if (maxDegree < 0) { polynoms = null; @@ -110,7 +115,7 @@ class GraggBulirschStoerStepInterpolator currentDegree = -1; } else { - double[][] newPols = new double[maxDegree + 1][]; + final double[][] newPols = new double[maxDegree + 1][]; if (polynoms != null) { System.arraycopy(polynoms, 0, newPols, 0, polynoms.length); for (int i = polynoms.length; i < newPols.length; ++i) { @@ -129,9 +134,9 @@ class GraggBulirschStoerStepInterpolator } else { errfac = new double[maxDegree - 4]; for (int i = 0; i < errfac.length; ++i) { - int ip5 = i + 5; + final int ip5 = i + 5; errfac[i] = 1.0 / (ip5 * ip5); - double e = 0.5 * Math.sqrt (((double) (i + 1)) / ip5); + final double e = 0.5 * Math.sqrt (((double) (i + 1)) / ip5); for (int j = 0; j <= i; ++j) { errfac[i] *= e / (j + 1); } @@ -168,10 +173,10 @@ class GraggBulirschStoerStepInterpolator * derivatives at the middle point of the step * @param forward integration direction indicator */ - public GraggBulirschStoerStepInterpolator(double[] y, double[] y0Dot, - double[] y1, double[] y1Dot, - double[][] yMidDots, - boolean forward) { + public GraggBulirschStoerStepInterpolator(final double[] y, final double[] y0Dot, + final double[] y1, final double[] y1Dot, + final double[][] yMidDots, + final boolean forward) { super(y, forward); this.y0Dot = y0Dot; @@ -189,11 +194,11 @@ class GraggBulirschStoerStepInterpolator * instance */ public GraggBulirschStoerStepInterpolator - (GraggBulirschStoerStepInterpolator interpolator) { + (final GraggBulirschStoerStepInterpolator interpolator) { super(interpolator); - int dimension = currentState.length; + final int dimension = currentState.length; // the interpolator has been finalized, // the following arrays are not needed anymore @@ -218,19 +223,17 @@ class GraggBulirschStoerStepInterpolator } - /** Really copy the finalized instance. - * @return a copy of the finalized instance - */ + /** {@inheritDoc} */ protected StepInterpolator doCopy() { return new GraggBulirschStoerStepInterpolator(this); } /** Compute the interpolation coefficients for dense output. - * @param mu degree of the interpolation polynom + * @param mu degree of the interpolation polynomial * @param h current step */ - public void computeCoefficients(int mu, double h) { + public void computeCoefficients(final int mu, final double h) { if ((polynoms == null) || (polynoms.length <= (mu + 4))) { resetTables(mu + 4); @@ -240,11 +243,11 @@ class GraggBulirschStoerStepInterpolator for (int i = 0; i < currentState.length; ++i) { - double yp0 = h * y0Dot[i]; - double yp1 = h * y1Dot[i]; - double ydiff = y1[i] - currentState[i]; - double aspl = ydiff - yp1; - double bspl = yp0 - ydiff; + final double yp0 = h * y0Dot[i]; + final double yp1 = h * y1Dot[i]; + final double ydiff = y1[i] - currentState[i]; + final double aspl = ydiff - yp1; + final double bspl = yp0 - ydiff; polynoms[0][i] = currentState[i]; polynoms[1][i] = ydiff; @@ -256,24 +259,24 @@ class GraggBulirschStoerStepInterpolator } // compute the remaining coefficients - double ph0 = 0.5 * (currentState[i] + y1[i]) + 0.125 * (aspl + bspl); + final double ph0 = 0.5 * (currentState[i] + y1[i]) + 0.125 * (aspl + bspl); polynoms[4][i] = 16 * (yMidDots[0][i] - ph0); if (mu > 0) { - double ph1 = ydiff + 0.25 * (aspl - bspl); + final double ph1 = ydiff + 0.25 * (aspl - bspl); polynoms[5][i] = 16 * (yMidDots[1][i] - ph1); if (mu > 1) { - double ph2 = yp1 - yp0; + final double ph2 = yp1 - yp0; polynoms[6][i] = 16 * (yMidDots[2][i] - ph2 + polynoms[4][i]); if (mu > 2) { - double ph3 = 6 * (bspl - aspl); + final double ph3 = 6 * (bspl - aspl); polynoms[7][i] = 16 * (yMidDots[3][i] - ph3 + 3 * polynoms[5][i]); for (int j = 4; j <= mu; ++j) { - double fac1 = 0.5 * j * (j - 1); - double fac2 = 2 * fac1 * (j - 2) * (j - 3); + final double fac1 = 0.5 * j * (j - 1); + final double fac2 = 2 * fac1 * (j - 2) * (j - 3); polynoms[j+4][i] = 16 * (yMidDots[j][i] + fac1 * polynoms[j+2][i] - fac2 * polynoms[j][i]); } @@ -289,11 +292,11 @@ class GraggBulirschStoerStepInterpolator * @param scale scaling array * @return estimate of the interpolation error */ - public double estimateError(double[] scale) { + public double estimateError(final double[] scale) { double error = 0; if (currentDegree >= 5) { for (int i = 0; i < currentState.length; ++i) { - double e = polynoms[currentDegree][i] / scale[i]; + final double e = polynoms[currentDegree][i] / scale[i]; error += e * e; } error = Math.sqrt(error / currentState.length) * errfac[currentDegree-5]; @@ -301,52 +304,58 @@ class GraggBulirschStoerStepInterpolator return error; } - /** Compute the state at the interpolated time. - * This is the main processing method that should be implemented by - * the derived classes to perform the interpolation. - * @param theta normalized interpolation abscissa within the step - * (theta is zero at the previous time step and one at the current time step) - * @param oneMinusThetaH time gap between the interpolated time and - * the current time - * @throws DerivativeException this exception is propagated to the caller if the - * underlying user function triggers one - */ - protected void computeInterpolatedState(double theta, - double oneMinusThetaH) + /** {@inheritDoc} */ + protected void computeInterpolatedState(final double theta, + final double oneMinusThetaH) throws DerivativeException { - int dimension = currentState.length; + final int dimension = currentState.length; - double oneMinusTheta = 1.0 - theta; - double theta05 = theta - 0.5; - double t4 = theta * oneMinusTheta; - t4 = t4 * t4; + final double oneMinusTheta = 1.0 - theta; + final double theta05 = theta - 0.5; + final double tOmT = theta * oneMinusTheta; + final double t4 = tOmT * tOmT; + final double t4Dot = 2 * tOmT * (1 - 2 * theta); + final double dot1 = 1.0 / h; + final double dot2 = theta * (2 - 3 * theta) / h; + final double dot3 = ((3 * theta - 4) * theta + 1) / h; for (int i = 0; i < dimension; ++i) { - interpolatedState[i] = polynoms[0][i] + - theta * (polynoms[1][i] + - oneMinusTheta * (polynoms[2][i] * theta + - polynoms[3][i] * oneMinusTheta)); - if (currentDegree > 3) { - double c = polynoms[currentDegree][i]; - for (int j = currentDegree - 1; j > 3; --j) { - c = polynoms[j][i] + c * theta05 / (j - 3); + final double p0 = polynoms[0][i]; + final double p1 = polynoms[1][i]; + final double p2 = polynoms[2][i]; + final double p3 = polynoms[3][i]; + interpolatedState[i] = p0 + theta * (p1 + oneMinusTheta * (p2 * theta + p3 * oneMinusTheta)); + interpolatedDerivatives[i] = dot1 * p1 + dot2 * p2 + dot3 * p3; + + if (currentDegree > 3) { + double cDot = 0; + double c = polynoms[currentDegree][i]; + for (int j = currentDegree - 1; j > 3; --j) { + final double d = 1.0 / (j - 3); + cDot = d * (theta05 * cDot + c); + c = polynoms[j][i] + c * d * theta05; + } + interpolatedState[i] += t4 * c; + interpolatedDerivatives[i] += (t4 * cDot + t4Dot * c) / h; } - interpolatedState[i] += t4 * c; - } + + } + + if (h == 0) { + // in this degenerated case, the previous computation leads to NaN for derivatives + // we fix this by using the derivatives at midpoint + System.arraycopy(yMidDots[1], 0, interpolatedDerivatives, 0, dimension); } } - /** Save the state of the instance. - * @param out stream where to save the state - * @exception IOException in case of write error - */ - public void writeExternal(ObjectOutput out) + /** {@inheritDoc} */ + public void writeExternal(final ObjectOutput out) throws IOException { - int dimension = currentState.length; + final int dimension = currentState.length; // save the state of the base class writeBaseExternal(out); @@ -361,19 +370,16 @@ class GraggBulirschStoerStepInterpolator } - /** Read the state of the instance. - * @param in stream where to read the state from - * @exception IOException in case of read error - */ - public void readExternal(ObjectInput in) + /** {@inheritDoc} */ + public void readExternal(final ObjectInput in) throws IOException { // read the base class - double t = readBaseExternal(in); - int dimension = currentState.length; + final double t = readBaseExternal(in); + final int dimension = currentState.length; // read the local attributes - int degree = in.readInt(); + final int degree = in.readInt(); resetTables(degree); currentDegree = degree; @@ -387,7 +393,7 @@ class GraggBulirschStoerStepInterpolator // we can now set the interpolated time and state setInterpolatedTime(t); } catch (DerivativeException e) { - throw new IOException(e.getMessage()); + throw MathRuntimeException.createIOException(e); } } diff --git a/src/java/org/apache/commons/math/ode/HighamHall54Integrator.java b/src/java/org/apache/commons/math/ode/nonstiff/HighamHall54Integrator.java similarity index 68% rename from src/java/org/apache/commons/math/ode/HighamHall54Integrator.java rename to src/java/org/apache/commons/math/ode/nonstiff/HighamHall54Integrator.java index ef6e0ceae..e899b8d33 100644 --- a/src/java/org/apache/commons/math/ode/HighamHall54Integrator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/HighamHall54Integrator.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; /** * This class implements the 5(4) Higham and Hall integrator for @@ -34,8 +34,11 @@ package org.apache.commons.math.ode; public class HighamHall54Integrator extends EmbeddedRungeKuttaIntegrator { + /** Serializable version identifier. */ + private static final long serialVersionUID = 1462328766749870097L; + /** Integrator method name. */ - private static final String methodName = "Higham-Hall 5(4)"; + private static final String METHOD_NAME = "Higham-Hall 5(4)"; /** Time steps Butcher array. */ private static final double[] staticC = { @@ -71,10 +74,10 @@ public class HighamHall54Integrator * @param scalAbsoluteTolerance allowed absolute error * @param scalRelativeTolerance allowed relative error */ - public HighamHall54Integrator(double minStep, double maxStep, - double scalAbsoluteTolerance, - double scalRelativeTolerance) { - super(false, staticC, staticA, staticB, new HighamHall54StepInterpolator(), + public HighamHall54Integrator(final double minStep, final double maxStep, + final double scalAbsoluteTolerance, + final double scalRelativeTolerance) { + super(METHOD_NAME, false, staticC, staticA, staticB, new HighamHall54StepInterpolator(), minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance); } @@ -87,37 +90,22 @@ public class HighamHall54Integrator * @param vecAbsoluteTolerance allowed absolute error * @param vecRelativeTolerance allowed relative error */ - public HighamHall54Integrator(double minStep, double maxStep, - double[] vecAbsoluteTolerance, - double[] vecRelativeTolerance) { - super(false, staticC, staticA, staticB, new HighamHall54StepInterpolator(), + public HighamHall54Integrator(final double minStep, final double maxStep, + final double[] vecAbsoluteTolerance, + final double[] vecRelativeTolerance) { + super(METHOD_NAME, false, staticC, staticA, staticB, new HighamHall54StepInterpolator(), minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance); } - /** Get the name of the method. - * @return name of the method - */ - public String getName() { - return methodName; - } - - /** Get the order of the method. - * @return order of the method - */ + /** {@inheritDoc} */ public int getOrder() { return 5; } - /** Compute the error ratio. - * @param yDotK derivatives computed during the first stages - * @param y0 estimate of the step at the start of the step - * @param y1 estimate of the step at the end of the step - * @param h current step - * @return error ratio, greater than 1 if step should be rejected - */ - protected double estimateError(double[][] yDotK, - double[] y0, double[] y1, - double h) { + /** {@inheritDoc} */ + protected double estimateError(final double[][] yDotK, + final double[] y0, final double[] y1, + final double h) { double error = 0; @@ -127,11 +115,11 @@ public class HighamHall54Integrator errSum += staticE[l] * yDotK[l][j]; } - double yScale = Math.max(Math.abs(y0[j]), Math.abs(y1[j])); - double tol = (vecAbsoluteTolerance == null) ? - (scalAbsoluteTolerance + scalRelativeTolerance * yScale) : - (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale); - double ratio = h * errSum / tol; + final double yScale = Math.max(Math.abs(y0[j]), Math.abs(y1[j])); + final double tol = (vecAbsoluteTolerance == null) ? + (scalAbsoluteTolerance + scalRelativeTolerance * yScale) : + (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale); + final double ratio = h * errSum / tol; error += ratio * ratio; } diff --git a/src/java/org/apache/commons/math/ode/HighamHall54StepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/HighamHall54StepInterpolator.java similarity index 53% rename from src/java/org/apache/commons/math/ode/HighamHall54StepInterpolator.java rename to src/java/org/apache/commons/math/ode/nonstiff/HighamHall54StepInterpolator.java index a97dcdf49..04763d67a 100644 --- a/src/java/org/apache/commons/math/ode/HighamHall54StepInterpolator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/HighamHall54StepInterpolator.java @@ -15,7 +15,11 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; + +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; +import org.apache.commons.math.ode.sampling.StepInterpolator; /** * This class represents an interpolator over the last step during an @@ -48,42 +52,44 @@ class HighamHall54StepInterpolator * copy: its arrays are separated from the original arrays of the * instance */ - public HighamHall54StepInterpolator(HighamHall54StepInterpolator interpolator) { + public HighamHall54StepInterpolator(final HighamHall54StepInterpolator interpolator) { super(interpolator); } - /** Really copy the finalized instance. - * @return a copy of the finalized instance - */ + /** {@inheritDoc} */ protected StepInterpolator doCopy() { return new HighamHall54StepInterpolator(this); } - /** Compute the state at the interpolated time. - * @param theta normalized interpolation abscissa within the step - * (theta is zero at the previous time step and one at the current time step) - * @param oneMinusThetaH time gap between the interpolated time and - * the current time - * @throws DerivativeException this exception is propagated to the caller if the - * underlying user function triggers one - */ - protected void computeInterpolatedState(double theta, - double oneMinusThetaH) + /** {@inheritDoc} */ + protected void computeInterpolatedState(final double theta, + final double oneMinusThetaH) throws DerivativeException { - double theta2 = theta * theta; + final double theta2 = theta * theta; - double b0 = h * (-1.0/12.0 + theta * (1.0 + theta * (-15.0/4.0 + theta * (16.0/3.0 + theta * -5.0/2.0)))); - double b2 = h * (-27.0/32.0 + theta2 * (459.0/32.0 + theta * (-243.0/8.0 + theta * 135.0/8.0))); - double b3 = h * (4.0/3.0 + theta2 * (-22.0 + theta * (152.0/3.0 + theta * -30.0))); - double b4 = h * (-125.0/96.0 + theta2 * (375.0/32.0 + theta * (-625.0/24.0 + theta * 125.0/8.0))); - double b5 = h * (-5.0/48.0 + theta2 * (-5.0/16.0 + theta * 5.0/12.0)); + final double b0 = h * (-1.0/12.0 + theta * (1.0 + theta * (-15.0/4.0 + theta * (16.0/3.0 + theta * -5.0/2.0)))); + final double b2 = h * (-27.0/32.0 + theta2 * (459.0/32.0 + theta * (-243.0/8.0 + theta * 135.0/8.0))); + final double b3 = h * (4.0/3.0 + theta2 * (-22.0 + theta * (152.0/3.0 + theta * -30.0))); + final double b4 = h * (-125.0/96.0 + theta2 * (375.0/32.0 + theta * (-625.0/24.0 + theta * 125.0/8.0))); + final double b5 = h * (-5.0/48.0 + theta2 * (-5.0/16.0 + theta * 5.0/12.0)); + final double bDot0 = 1 + theta * (-15.0/2.0 + theta * (16.0 - 10.0 * theta)); + final double bDot2 = theta * (459.0/16.0 + theta * (-729.0/8.0 + 135.0/2.0 * theta)); + final double bDot3 = theta * (-44.0 + theta * (152.0 - 120.0 * theta)); + final double bDot4 = theta * (375.0/16.0 + theta * (-625.0/8.0 + 125.0/2.0 * theta)); + final double bDot5 = theta * 5.0/8.0 * (2 * theta - 1); for (int i = 0; i < interpolatedState.length; ++i) { - interpolatedState[i] = currentState[i] + - b0 * yDotK[0][i] + b2 * yDotK[2][i] + b3 * yDotK[3][i] + - b4 * yDotK[4][i] + b5 * yDotK[5][i]; + final double yDot0 = yDotK[0][i]; + final double yDot2 = yDotK[2][i]; + final double yDot3 = yDotK[3][i]; + final double yDot4 = yDotK[4][i]; + final double yDot5 = yDotK[5][i]; + interpolatedState[i] = + currentState[i] + b0 * yDot0 + b2 * yDot2 + b3 * yDot3 + b4 * yDot4 + b5 * yDot5; + interpolatedDerivatives[i] = + bDot0 * yDot0 + bDot2 * yDot2 + bDot3 * yDot3 + bDot4 * yDot4 + bDot5 * yDot5; } } diff --git a/src/java/org/apache/commons/math/ode/MidpointIntegrator.java b/src/java/org/apache/commons/math/ode/nonstiff/MidpointIntegrator.java similarity index 83% rename from src/java/org/apache/commons/math/ode/MidpointIntegrator.java rename to src/java/org/apache/commons/math/ode/nonstiff/MidpointIntegrator.java index ba330b716..b80abc309 100644 --- a/src/java/org/apache/commons/math/ode/MidpointIntegrator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/MidpointIntegrator.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; /** * This class implements a second order Runge-Kutta integrator for @@ -42,8 +42,8 @@ package org.apache.commons.math.ode; public class MidpointIntegrator extends RungeKuttaIntegrator { - /** Integrator method name. */ - private static final String methodName = "midpoint"; + /** Serializable version identifier. */ + private static final long serialVersionUID = -7690774342890000483L; /** Time steps Butcher array. */ private static final double[] c = { @@ -64,15 +64,8 @@ public class MidpointIntegrator * Build a midpoint integrator with the given step. * @param step integration step */ - public MidpointIntegrator(double step) { - super(c, a, b, new MidpointStepInterpolator(), step); - } - - /** Get the name of the method. - * @return name of the method - */ - public String getName() { - return methodName; + public MidpointIntegrator(final double step) { + super("midpoint", c, a, b, new MidpointStepInterpolator(), step); } } diff --git a/src/java/org/apache/commons/math/ode/MidpointStepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/MidpointStepInterpolator.java similarity index 68% rename from src/java/org/apache/commons/math/ode/MidpointStepInterpolator.java rename to src/java/org/apache/commons/math/ode/nonstiff/MidpointStepInterpolator.java index ec9a4bbd2..7e92e5ffd 100644 --- a/src/java/org/apache/commons/math/ode/MidpointStepInterpolator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/MidpointStepInterpolator.java @@ -15,13 +15,17 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; + +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; +import org.apache.commons.math.ode.sampling.StepInterpolator; /** * This class implements a step interpolator for second order * Runge-Kutta integrator. * - *

This interpolator allow to compute dense output inside the last + *

This interpolator computes dense output inside the last * step computed. The interpolation equation is consistent with the * integration scheme : * @@ -58,38 +62,31 @@ class MidpointStepInterpolator * copy: its arrays are separated from the original arrays of the * instance */ - public MidpointStepInterpolator(MidpointStepInterpolator interpolator) { + public MidpointStepInterpolator(final MidpointStepInterpolator interpolator) { super(interpolator); } - /** Really copy the finalized instance. - * @return a copy of the finalized instance - */ + /** {@inheritDoc} */ protected StepInterpolator doCopy() { return new MidpointStepInterpolator(this); } - /** Compute the state at the interpolated time. - * This is the main processing method that should be implemented by - * the derived classes to perform the interpolation. - * @param theta normalized interpolation abscissa within the step - * (theta is zero at the previous time step and one at the current time step) - * @param oneMinusThetaH time gap between the interpolated time and - * the current time - * @throws DerivativeException this exception is propagated to the caller if the - * underlying user function triggers one - */ - protected void computeInterpolatedState(double theta, - double oneMinusThetaH) + /** {@inheritDoc} */ + protected void computeInterpolatedState(final double theta, + final double oneMinusThetaH) throws DerivativeException { - double coeff1 = oneMinusThetaH * theta; - double coeff2 = oneMinusThetaH * (1.0 + theta); + final double coeff1 = oneMinusThetaH * theta; + final double coeff2 = oneMinusThetaH * (1.0 + theta); + final double coeffDot2 = 2 * theta; + final double coeffDot1 = 1 - coeffDot2; for (int i = 0; i < interpolatedState.length; ++i) { - interpolatedState[i] = currentState[i] + - coeff1 * yDotK[0][i] - coeff2 * yDotK[1][i]; + final double yDot1 = yDotK[0][i]; + final double yDot2 = yDotK[1][i]; + interpolatedState[i] = currentState[i] + coeff1 * yDot1 - coeff2 * yDot2; + interpolatedDerivatives[i] = coeffDot1 * yDot1 + coeffDot2 * yDot2; } } diff --git a/src/java/org/apache/commons/math/ode/nonstiff/MultistepIntegrator.java b/src/java/org/apache/commons/math/ode/nonstiff/MultistepIntegrator.java new file mode 100644 index 000000000..0688215f8 --- /dev/null +++ b/src/java/org/apache/commons/math/ode/nonstiff/MultistepIntegrator.java @@ -0,0 +1,328 @@ +/* + * 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.ode.nonstiff; + +import java.util.Arrays; + +import org.apache.commons.math.ode.AbstractIntegrator; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderDifferentialEquations; +import org.apache.commons.math.ode.FirstOrderIntegrator; +import org.apache.commons.math.ode.IntegratorException; +import org.apache.commons.math.ode.ODEIntegrator; +import org.apache.commons.math.ode.events.CombinedEventsManager; +import org.apache.commons.math.ode.events.EventException; +import org.apache.commons.math.ode.events.EventHandler; +import org.apache.commons.math.ode.events.EventState; +import org.apache.commons.math.ode.sampling.FixedStepHandler; +import org.apache.commons.math.ode.sampling.StepHandler; +import org.apache.commons.math.ode.sampling.StepInterpolator; +import org.apache.commons.math.ode.sampling.StepNormalizer; + +/** + * This class is the base class for multistep integrators for Ordinary + * Differential Equations. + * + * @see AdamsBashforthIntegrator + * @see AdamsMoultonIntegrator + * @see BDFIntegrator + * @version $Revision$ $Date$ + * @since 2.0 + */ +public abstract class MultistepIntegrator extends AbstractIntegrator { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -1705864253238417163L; + + /** Starter integrator. */ + private FirstOrderIntegrator starter; + + /** Previous steps times. */ + protected double[] previousT; + + /** Previous steps derivatives. */ + protected double[][] previousF; + + /** Time of last detected reset. */ + private double resetTime; + + /** Prototype of the step interpolator. */ + protected MultistepStepInterpolator prototype; + + /** + * Build a multistep integrator with the given number of steps. + *

The default starter integrator is set to the {@link + * DormandPrince853Integrator Dormand-Prince 8(5,3)} integrator with + * some defaults settings.

+ * @param name name of the method + * @param k number of steps of the multistep method + * (including the one being computed) + * @param prototype prototype of the step interpolator to use + */ + protected MultistepIntegrator(final String name, final int k, + final MultistepStepInterpolator prototype) { + super(name); + starter = new DormandPrince853Integrator(1.0e-6, 1.0e6, 1.0e-5, 1.0e-6); + previousT = new double[k]; + previousF = new double[k][]; + this.prototype = prototype; + } + + /** + * Get the starter integrator. + * @return starter integrator + */ + public ODEIntegrator getStarterIntegrator() { + return starter; + } + + /** + * Set the starter integrator. + *

The various step and event handlers for this starter integrator + * will be managed automatically by the multi-step integrator. Any + * user configuration for these elements will be cleared before use.

+ * @param starter starter integrator + */ + public void setStarterIntegrator(FirstOrderIntegrator starter) { + this.starter = starter; + } + + /** Start the integration. + *

This method computes the first few steps of the multistep method, + * using the underlying starter integrator, ensuring the returned steps + * all belong to the same smooth range.

+ *

In order to ensure smoothness, the start phase is automatically + * restarted when a state or derivative reset is triggered by the + * registered events handlers before this start phase is completed. As + * an example, consider integrating a differential equation from t=0 + * to t=100 with a 4 steps method and step size equal to 0.2. If an event + * resets the state at t=0.5, the start phase will not end at t=0.7 with + * steps at [0.0, 0.2, 0.4, 0.6] but instead will end at t=1.1 with steps + * at [0.5, 0.7, 0.9, 1.1].

+ *

A side effect of the need for smoothness is that an ODE triggering + * short period regular resets will remain in the start phase throughout + * the integration range if the step size or the number of steps to store + * are too large.

+ *

If the start phase ends prematurely (because of some triggered event + * for example), then the time of latest previous steps will be set to + * Double.NaN.

+ * @param n number of steps to store + * @param h signed step size to use for the first steps + * @param manager discrete events manager to use + * @param equations differential equations to integrate + * @param t0 initial time + * @param y state vector: contains the initial value of the state vector at t0, + * will be used to put the state vector at each successful step and hence + * contains the final value at the end of the start phase + * @return time of the end of the start phase + * @throws IntegratorException if the integrator cannot perform integration + * @throws DerivativeException this exception is propagated to the caller if + * the underlying user function triggers one + */ + protected double start(final int n, final double h, + final CombinedEventsManager manager, + final FirstOrderDifferentialEquations equations, + final double t0, final double[] y) + throws DerivativeException, IntegratorException { + + // clear the first steps + Arrays.fill(previousT, Double.NaN); + Arrays.fill(previousF, null); + + // configure the event handlers + starter.clearEventHandlers(); + for (EventState state : manager.getEventsStates()) { + starter.addEventHandler(new ResetCheckingWrapper(state.getEventHandler()), + state.getMaxCheckInterval(), + state.getConvergence(), state.getMaxIterationCount()); + } + + // configure the step handlers + starter.clearStepHandlers(); + for (final StepHandler handler : stepHandlers) { + // add the user defined step handlers, filtering out the isLast indicator + starter.addStepHandler(new FilteringWrapper(handler)); + } + + // add one specific step handler to store the first steps + final StoringStepHandler store = new StoringStepHandler(n); + starter.addStepHandler(new StepNormalizer(h, store)); + + // integrate over the first few steps, ensuring no intermediate reset occurs + double t = t0; + double stopTime = Double.NaN; + do { + resetTime = Double.NaN; + store.restart(); + // we overshoot by 1/10000 step the end to make sure we get don't miss the last point + stopTime = starter.integrate(equations, t, y, t + (n - 0.9999) * h, y); + if (!Double.isNaN(resetTime)) { + // there was an intermediate reset, we restart + t = resetTime; + } + } while (!Double.isNaN(resetTime)); + + // clear configuration + starter.clearEventHandlers(); + starter.clearStepHandlers(); + + if (store.getFinalState() != null) { + System.arraycopy(store.getFinalState(), 0, y, 0, y.length); + } + return stopTime; + + } + + /** Rotate the previous steps arrays. + */ + protected void rotatePreviousSteps() { + final double[] rolled = previousF[previousT.length - 1]; + for (int k = previousF.length - 1; k > 0; --k) { + previousT[k] = previousT[k - 1]; + previousF[k] = previousF[k - 1]; + } + previousF[0] = rolled; + } + + /** Event handler wrapper to check if state or derivatives have been reset. */ + private class ResetCheckingWrapper implements EventHandler { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 4922660285376467937L; + + /** Wrapped event handler. */ + private final EventHandler handler; + + /** Build a new instance. + * @param handler event handler to wrap + */ + public ResetCheckingWrapper(final EventHandler handler) { + this.handler = handler; + } + + /** {@inheritDoc} */ + public int eventOccurred(double t, double[] y) throws EventException { + final int action = handler.eventOccurred(t, y); + if ((action == RESET_DERIVATIVES) || (action == RESET_STATE)) { + // a singularity has been encountered + // we need to restart the start phase + resetTime = t; + return STOP; + } + return action; + } + + /** {@inheritDoc} */ + public double g(double t, double[] y) throws EventException { + return handler.g(t, y); + } + + /** {@inheritDoc} */ + public void resetState(double t, double[] y) throws EventException { + handler.resetState(t, y); + } + + } + + /** Step handler wrapper filtering out the isLast indicator. */ + private class FilteringWrapper implements StepHandler { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 4607975253344802232L; + + /** Wrapped step handler. */ + private final StepHandler handler; + + /** Build a new instance. + * @param handler step handler to wrap + */ + public FilteringWrapper(final StepHandler handler) { + this.handler = handler; + } + + /** {@inheritDoc} */ + public void handleStep(StepInterpolator interpolator, boolean isLast) + throws DerivativeException { + // we force the isLast indicator to false EXCEPT if some event handler triggered a stop + handler.handleStep(interpolator, eventsHandlersManager.stop()); + } + + /** {@inheritDoc} */ + public boolean requiresDenseOutput() { + return handler.requiresDenseOutput(); + } + + /** {@inheritDoc} */ + public void reset() { + handler.reset(); + } + + } + + /** Specialized step handler storing the first few steps. */ + private class StoringStepHandler implements FixedStepHandler { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 4592974435520688797L; + + /** Number of steps to store. */ + private final int n; + + /** Counter for already stored steps. */ + private int count; + + /** Final state. */ + private double[] finalState; + + /** Build a new instance. + * @param n number of steps to store + */ + public StoringStepHandler(final int n) { + this.n = n; + restart(); + } + + /** Restart storage. + */ + public void restart() { + count = 0; + finalState = null; + } + + /** Get the final state. + * @return final state + */ + public double[] getFinalState() { + return finalState; + } + + /** {@inheritDoc} */ + public void handleStep(final double t, final double[] y, final double[] yDot, + final boolean isLast) { + if (count++ < n) { + previousT[n - count] = t; + previousF[n - count] = yDot.clone(); + if (count == n) { + finalState = y.clone(); + } + } + } + + } + +} diff --git a/src/java/org/apache/commons/math/ode/nonstiff/MultistepStepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/MultistepStepInterpolator.java new file mode 100644 index 000000000..07eaa48ac --- /dev/null +++ b/src/java/org/apache/commons/math/ode/nonstiff/MultistepStepInterpolator.java @@ -0,0 +1,166 @@ +/* + * 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.ode.nonstiff; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; + +/** This class represents an interpolator over the last step during an + * ODE integration for multistep integrators. + * + * @see MultistepIntegrator + * + * @version $Revision$ $Date$ + * @since 2.0 + */ + +abstract class MultistepStepInterpolator + extends AbstractStepInterpolator { + + /** Previous steps times. */ + protected double[] previousT; + + /** Previous steps derivatives. */ + protected double[][] previousF; + + /** Simple constructor. + * This constructor builds an instance that is not usable yet, the + * {@link #reinitialize} method should be called before using the + * instance in order to initialize the internal arrays. This + * constructor is used only in order to delay the initialization in + * some cases. The {@link MultistepIntegrator} classe uses the + * prototyping design pattern to create the step interpolators by + * cloning an uninitialized model and latter initializing the copy. + */ + protected MultistepStepInterpolator() { + previousT = null; + previousF = null; + } + + /** Copy constructor. + + *

The copied interpolator should have been finalized before the + * copy, otherwise the copy will not be able to perform correctly any + * interpolation and will throw a {@link NullPointerException} + * later. Since we don't want this constructor to throw the + * exceptions finalization may involve and since we don't want this + * method to modify the state of the copied interpolator, + * finalization is not done automatically, it + * remains under user control.

+ + *

The copy is a deep copy: its arrays are separated from the + * original arrays of the instance.

+ + * @param interpolator interpolator to copy from. + + */ + public MultistepStepInterpolator(final MultistepStepInterpolator interpolator) { + + super(interpolator); + + if (interpolator.currentState != null) { + previousT = interpolator.previousT.clone(); + previousF = new double[interpolator.previousF.length][]; + for (int k = 0; k < interpolator.previousF.length; ++k) { + previousF[k] = interpolator.previousF[k].clone(); + } + initializeCoefficients(); + } else { + previousT = null; + previousF = null; + } + + } + + /** Reinitialize the instance + * @param y reference to the integrator array holding the state at + * the end of the step + * @param previousT reference to the integrator array holding the times + * of the previous steps + * @param previousF reference to the integrator array holding the + * previous slopes + * @param forward integration direction indicator + */ + public void reinitialize(final double[] y, + final double[] previousT, final double[][] previousF, + final boolean forward) { + reinitialize(y, forward); + this.previousT = previousT; + this.previousF = previousF; + initializeCoefficients(); + } + + /** Initialize the coefficients arrays. + */ + protected abstract void initializeCoefficients(); + + /** {@inheritDoc} */ + public void writeExternal(final ObjectOutput out) + throws IOException { + + // save the state of the base class + writeBaseExternal(out); + + // save the local attributes + out.writeInt(previousT.length); + for (int k = 0; k < previousF.length; ++k) { + out.writeDouble(previousT[k]); + for (int i = 0; i < currentState.length; ++i) { + out.writeDouble(previousF[k][i]); + } + } + + } + + /** {@inheritDoc} */ + public void readExternal(final ObjectInput in) + throws IOException { + + // read the base class + final double t = readBaseExternal(in); + + // read the local attributes + final int kMax = in.readInt(); + previousT = new double[kMax]; + previousF = new double[kMax][]; + for (int k = 0; k < kMax; ++k) { + previousT[k] = in.readDouble(); + previousF[k] = new double[currentState.length]; + for (int i = 0; i < currentState.length; ++i) { + previousF[k][i] = in.readDouble(); + } + } + + // initialize the coefficients + initializeCoefficients(); + + try { + // we can now set the interpolated time and state + setInterpolatedTime(t); + } catch (DerivativeException e) { + throw MathRuntimeException.createIOException(e); + } + + } + +} diff --git a/src/java/org/apache/commons/math/ode/nonstiff/RungeKuttaIntegrator.java b/src/java/org/apache/commons/math/ode/nonstiff/RungeKuttaIntegrator.java new file mode 100644 index 000000000..6303bcf0a --- /dev/null +++ b/src/java/org/apache/commons/math/ode/nonstiff/RungeKuttaIntegrator.java @@ -0,0 +1,212 @@ +/* + * 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.ode.nonstiff; + + +import org.apache.commons.math.ode.AbstractIntegrator; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderDifferentialEquations; +import org.apache.commons.math.ode.IntegratorException; +import org.apache.commons.math.ode.events.CombinedEventsManager; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; +import org.apache.commons.math.ode.sampling.DummyStepInterpolator; +import org.apache.commons.math.ode.sampling.StepHandler; + +/** + * This class implements the common part of all fixed step Runge-Kutta + * integrators for Ordinary Differential Equations. + * + *

These methods are explicit Runge-Kutta methods, their Butcher + * arrays are as follows : + *

+ *    0  |
+ *   c2  | a21
+ *   c3  | a31  a32
+ *   ... |        ...
+ *   cs  | as1  as2  ...  ass-1
+ *       |--------------------------
+ *       |  b1   b2  ...   bs-1  bs
+ * 
+ *

+ * + * @see EulerIntegrator + * @see ClassicalRungeKuttaIntegrator + * @see GillIntegrator + * @see MidpointIntegrator + * @version $Revision$ $Date$ + * @since 1.2 + */ + +public abstract class RungeKuttaIntegrator extends AbstractIntegrator { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -5346558921715095559L; + + /** Simple constructor. + * Build a Runge-Kutta integrator with the given + * step. The default step handler does nothing. + * @param name name of the method + * @param c time steps from Butcher array (without the first zero) + * @param a internal weights from Butcher array (without the first empty row) + * @param b propagation weights for the high order method from Butcher array + * @param prototype prototype of the step interpolator to use + * @param step integration step + */ + protected RungeKuttaIntegrator(final String name, + final double[] c, final double[][] a, final double[] b, + final RungeKuttaStepInterpolator prototype, + final double step) { + super(name); + this.c = c; + this.a = a; + this.b = b; + this.prototype = prototype; + this.step = Math.abs(step); + } + + /** {@inheritDoc} */ + public double integrate(final FirstOrderDifferentialEquations equations, + final double t0, final double[] y0, + final double t, final double[] y) + throws DerivativeException, IntegratorException { + + sanityChecks(equations, t0, y0, t, y); + final boolean forward = (t > t0); + + // create some internal working arrays + final int stages = c.length + 1; + if (y != y0) { + System.arraycopy(y0, 0, y, 0, y0.length); + } + final double[][] yDotK = new double[stages][]; + for (int i = 0; i < stages; ++i) { + yDotK [i] = new double[y0.length]; + } + final double[] yTmp = new double[y0.length]; + + // set up an interpolator sharing the integrator arrays + AbstractStepInterpolator interpolator; + if (requiresDenseOutput() || (! eventsHandlersManager.isEmpty())) { + final RungeKuttaStepInterpolator rki = (RungeKuttaStepInterpolator) prototype.copy(); + rki.reinitialize(equations, yTmp, yDotK, forward); + interpolator = rki; + } else { + interpolator = new DummyStepInterpolator(yTmp, forward); + } + interpolator.storeTime(t0); + + // set up integration control objects + stepStart = t0; + stepSize = forward ? step : -step; + for (StepHandler handler : stepHandlers) { + handler.reset(); + } + CombinedEventsManager manager = addEndTimeChecker(t0, t, eventsHandlersManager); + boolean lastStep = false; + + // main integration loop + while (!lastStep) { + + interpolator.shift(); + + for (boolean loop = true; loop;) { + + // first stage + equations.computeDerivatives(stepStart, y, yDotK[0]); + + // next stages + for (int k = 1; k < stages; ++k) { + + for (int j = 0; j < y0.length; ++j) { + double sum = a[k-1][0] * yDotK[0][j]; + for (int l = 1; l < k; ++l) { + sum += a[k-1][l] * yDotK[l][j]; + } + yTmp[j] = y[j] + stepSize * sum; + } + + equations.computeDerivatives(stepStart + c[k-1] * stepSize, yTmp, yDotK[k]); + + } + + // estimate the state at the end of the step + for (int j = 0; j < y0.length; ++j) { + double sum = b[0] * yDotK[0][j]; + for (int l = 1; l < stages; ++l) { + sum += b[l] * yDotK[l][j]; + } + yTmp[j] = y[j] + stepSize * sum; + } + + // discrete events handling + interpolator.storeTime(stepStart + stepSize); + if (manager.evaluateStep(interpolator)) { + stepSize = manager.getEventTime() - stepStart; + } else { + loop = false; + } + + } + + // the step has been accepted + final double nextStep = stepStart + stepSize; + System.arraycopy(yTmp, 0, y, 0, y0.length); + manager.stepAccepted(nextStep, y); + lastStep = manager.stop(); + + // provide the step data to the step handler + interpolator.storeTime(nextStep); + for (StepHandler handler : stepHandlers) { + handler.handleStep(interpolator, lastStep); + } + stepStart = nextStep; + + if (manager.reset(stepStart, y) && ! lastStep) { + // some events handler has triggered changes that + // invalidate the derivatives, we need to recompute them + equations.computeDerivatives(stepStart, y, yDotK[0]); + } + + // make sure step size is set to default before next step + stepSize = forward ? step : -step; + + } + + final double stopTime = stepStart; + stepStart = Double.NaN; + stepSize = Double.NaN; + return stopTime; + + } + + /** Time steps from Butcher array (without the first zero). */ + private double[] c; + + /** Internal weights from Butcher array (without the first empty row). */ + private double[][] a; + + /** External weights for the high order method from Butcher array. */ + private double[] b; + + /** Prototype of the step interpolator. */ + private RungeKuttaStepInterpolator prototype; + + /** Integration step. */ + private double step; + +} diff --git a/src/java/org/apache/commons/math/ode/RungeKuttaStepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/RungeKuttaStepInterpolator.java similarity index 83% rename from src/java/org/apache/commons/math/ode/RungeKuttaStepInterpolator.java rename to src/java/org/apache/commons/math/ode/nonstiff/RungeKuttaStepInterpolator.java index 217598762..46c7d57e7 100644 --- a/src/java/org/apache/commons/math/ode/RungeKuttaStepInterpolator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/RungeKuttaStepInterpolator.java @@ -15,12 +15,17 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.IOException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderDifferentialEquations; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; + /** This class represents an interpolator over the last step during an * ODE integration for Runge-Kutta and embedded Runge-Kutta integrators. * @@ -40,7 +45,7 @@ abstract class RungeKuttaStepInterpolator * instance in order to initialize the internal arrays. This * constructor is used only in order to delay the initialization in * some cases. The {@link RungeKuttaIntegrator} and {@link - * EmbeddedRungeKuttaIntegrator} classes uses the prototyping design + * EmbeddedRungeKuttaIntegrator} classes use the prototyping design * pattern to create the step interpolators by cloning an * uninitialized model and latter initializing the copy. */ @@ -67,12 +72,12 @@ abstract class RungeKuttaStepInterpolator * @param interpolator interpolator to copy from. */ - public RungeKuttaStepInterpolator(RungeKuttaStepInterpolator interpolator) { + public RungeKuttaStepInterpolator(final RungeKuttaStepInterpolator interpolator) { super(interpolator); if (interpolator.currentState != null) { - int dimension = currentState.length; + final int dimension = currentState.length; yDotK = new double[interpolator.yDotK.length][]; for (int k = 0; k < interpolator.yDotK.length; ++k) { @@ -112,18 +117,15 @@ abstract class RungeKuttaStepInterpolator * intermediate slopes * @param forward integration direction indicator */ - public void reinitialize(FirstOrderDifferentialEquations equations, - double[] y, double[][] yDotK, boolean forward) { + public void reinitialize(final FirstOrderDifferentialEquations equations, + final double[] y, final double[][] yDotK, final boolean forward) { reinitialize(y, forward); this.yDotK = yDotK; this.equations = equations; } - /** Save the state of the instance. - * @param out stream where to save the state - * @exception IOException in case of write error - */ - public void writeExternal(ObjectOutput out) + /** {@inheritDoc} */ + public void writeExternal(final ObjectOutput out) throws IOException { // save the state of the base class @@ -141,18 +143,15 @@ abstract class RungeKuttaStepInterpolator } - /** Read the state of the instance. - * @param in stream where to read the state from - * @exception IOException in case of read error - */ - public void readExternal(ObjectInput in) + /** {@inheritDoc} */ + public void readExternal(final ObjectInput in) throws IOException { // read the base class - double t = readBaseExternal(in); + final double t = readBaseExternal(in); // read the local attributes - int kMax = in.readInt(); + final int kMax = in.readInt(); yDotK = new double[kMax][]; for (int k = 0; k < kMax; ++k) { yDotK[k] = new double[currentState.length]; @@ -167,7 +166,7 @@ abstract class RungeKuttaStepInterpolator // we can now set the interpolated time and state setInterpolatedTime(t); } catch (DerivativeException e) { - throw new IOException(e.getMessage()); + throw MathRuntimeException.createIOException(e); } } @@ -175,7 +174,7 @@ abstract class RungeKuttaStepInterpolator /** Slopes at the intermediate points */ protected double[][] yDotK; - /** Reference to the differential equations beeing integrated. */ + /** Reference to the differential equations being integrated. */ protected FirstOrderDifferentialEquations equations; } diff --git a/src/java/org/apache/commons/math/ode/ThreeEighthesIntegrator.java b/src/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesIntegrator.java similarity index 84% rename from src/java/org/apache/commons/math/ode/ThreeEighthesIntegrator.java rename to src/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesIntegrator.java index da1fda1be..aa5913d51 100644 --- a/src/java/org/apache/commons/math/ode/ThreeEighthesIntegrator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesIntegrator.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; /** * This class implements the 3/8 fourth order Runge-Kutta @@ -44,8 +44,8 @@ package org.apache.commons.math.ode; public class ThreeEighthesIntegrator extends RungeKuttaIntegrator { - /** Integrator method name. */ - private static final String methodName = "3/8"; + /** Serializable version identifier. */ + private static final long serialVersionUID = -2480984691453028021L; /** Time steps Butcher array. */ private static final double[] c = { @@ -68,15 +68,8 @@ public class ThreeEighthesIntegrator * Build a 3/8 integrator with the given step. * @param step integration step */ - public ThreeEighthesIntegrator(double step) { - super(c, a, b, new ThreeEighthesStepInterpolator(), step); - } - - /** Get the name of the method. - * @return name of the method - */ - public String getName() { - return methodName; + public ThreeEighthesIntegrator(final double step) { + super("3/8", c, a, b, new ThreeEighthesStepInterpolator(), step); } } diff --git a/src/java/org/apache/commons/math/ode/ThreeEighthesStepInterpolator.java b/src/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesStepInterpolator.java similarity index 63% rename from src/java/org/apache/commons/math/ode/ThreeEighthesStepInterpolator.java rename to src/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesStepInterpolator.java index 11137ab84..72e6995ee 100644 --- a/src/java/org/apache/commons/math/ode/ThreeEighthesStepInterpolator.java +++ b/src/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesStepInterpolator.java @@ -15,7 +15,11 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.nonstiff; + +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; +import org.apache.commons.math.ode.sampling.StepInterpolator; /** * This class implements a step interpolator for the 3/8 fourth @@ -63,44 +67,43 @@ class ThreeEighthesStepInterpolator * copy: its arrays are separated from the original arrays of the * instance */ - public ThreeEighthesStepInterpolator(ThreeEighthesStepInterpolator interpolator) { + public ThreeEighthesStepInterpolator(final ThreeEighthesStepInterpolator interpolator) { super(interpolator); } - /** Really copy the finalized instance. - * @return a copy of the finalized instance - */ + /** {@inheritDoc} */ protected StepInterpolator doCopy() { return new ThreeEighthesStepInterpolator(this); } - /** Compute the state at the interpolated time. - * This is the main processing method that should be implemented by - * the derived classes to perform the interpolation. - * @param theta normalized interpolation abscissa within the step - * (theta is zero at the previous time step and one at the current time step) - * @param oneMinusThetaH time gap between the interpolated time and - * the current time - * @throws DerivativeException this exception is propagated to the caller if the - * underlying user function triggers one - */ - protected void computeInterpolatedState(double theta, - double oneMinusThetaH) - throws DerivativeException { + /** {@inheritDoc} */ + protected void computeInterpolatedState(final double theta, + final double oneMinusThetaH) + throws DerivativeException { - double fourTheta2 = 4 * theta * theta; - double s = oneMinusThetaH / 8.0; - double coeff1 = s * (1 - 7 * theta + 2 * fourTheta2); - double coeff2 = 3 * s * (1 + theta - fourTheta2); - double coeff3 = 3 * s * (1 + theta); - double coeff4 = s * (1 + theta + fourTheta2); + final double fourTheta2 = 4 * theta * theta; + final double s = oneMinusThetaH / 8.0; + final double coeff1 = s * (1 - 7 * theta + 2 * fourTheta2); + final double coeff2 = 3 * s * (1 + theta - fourTheta2); + final double coeff3 = 3 * s * (1 + theta); + final double coeff4 = s * (1 + theta + fourTheta2); + final double coeffDot3 = 0.75 * theta; + final double coeffDot1 = coeffDot3 * (4 * theta - 5) + 1; + final double coeffDot2 = coeffDot3 * (5 - 6 * theta); + final double coeffDot4 = coeffDot3 * (2 * theta - 1); - for (int i = 0; i < interpolatedState.length; ++i) { - interpolatedState[i] = currentState[i] - - coeff1 * yDotK[0][i] - coeff2 * yDotK[1][i] - - coeff3 * yDotK[2][i] - coeff4 * yDotK[3][i]; - } + for (int i = 0; i < interpolatedState.length; ++i) { + final double yDot1 = yDotK[0][i]; + final double yDot2 = yDotK[1][i]; + final double yDot3 = yDotK[2][i]; + final double yDot4 = yDotK[3][i]; + interpolatedState[i] = + currentState[i] - coeff1 * yDot1 - coeff2 * yDot2 - coeff3 * yDot3 - coeff4 * yDot4; + interpolatedDerivatives[i] = + coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 + coeffDot4 * yDot4; + + } } diff --git a/src/java/org/apache/commons/math/ode/nonstiff/package.html b/src/java/org/apache/commons/math/ode/nonstiff/package.html new file mode 100644 index 000000000..3e68e8232 --- /dev/null +++ b/src/java/org/apache/commons/math/ode/nonstiff/package.html @@ -0,0 +1,25 @@ + + + + +

+This package provides classes to solve non-stiff Ordinary Differential Equations problems. +

+ + + diff --git a/src/java/org/apache/commons/math/ode/package.html b/src/java/org/apache/commons/math/ode/package.html index 840c7b16a..07fd13da5 100644 --- a/src/java/org/apache/commons/math/ode/package.html +++ b/src/java/org/apache/commons/math/ode/package.html @@ -17,7 +17,9 @@ --> +

This package provides classes to solve Ordinary Differential Equations problems. +

This package solves Initial Value Problems of the form @@ -32,31 +34,21 @@ All integrators provide dense output. This means that besides computing the state vector at discrete times, they also provide a cheap mean to get the state between the time steps. They do so through classes extending the {@link -org.apache.commons.math.ode.StepInterpolator StepInterpolator} +org.apache.commons.math.ode.sampling.StepInterpolator StepInterpolator} abstract class, which are made available to the user at the end of each step.

-All integrators handle multiple switching functions. This means that -the integrator can be driven by discrete events (occurring when the -signs of user-supplied {@link -org.apache.commons.math.ode.SwitchingFunction switching functions} -change). The steps are shortened as needed to ensure the events occur +All integrators handle multiple discrete events detection based on switching +functions. This means that the integrator can be driven by user specified +discrete events. The steps are shortened as needed to ensure the events occur at step boundaries (even if the integrator is a fixed-step integrator). When the events are triggered, integration can be stopped (this is called a G-stop facility), the state vector can be changed, or integration can simply go on. The latter case is useful to handle discontinuities in the differential equations gracefully and get -accurate dense output even close to the discontinuity. The events are -detected when the functions signs are different at the beginning and -end of the current step, or at several equidistant points inside the -step if its length becomes larger than the maximal checking interval -specified for the given switching function. This time interval should -be set appropriately to avoid missing some switching function sign -changes (it is possible to set it to -Double.POSITIVE_INFINITY if the sign changes cannot be -missed). +accurate dense output even close to the discontinuity.

@@ -77,10 +69,10 @@ the integration process is copied in the y array of the FirstOrderIntegrator.integrate} method. The second one should be used when more in-depth information is needed throughout the integration process. The user can register an object implementing the {@link -org.apache.commons.math.ode.StepHandler StepHandler} interface or a -{@link org.apache.commons.math.ode.StepNormalizer StepNormalizer} +org.apache.commons.math.ode.sampling.StepHandler StepHandler} interface or a +{@link org.apache.commons.math.ode.sampling.StepNormalizer StepNormalizer} object wrapping a user-specified object implementing the {@link -org.apache.commons.math.ode.FixedStepHandler FixedStepHandler} +org.apache.commons.math.ode.sampling.FixedStepHandler FixedStepHandler} interface into the integrator before calling the {@link org.apache.commons.math.ode.FirstOrderIntegrator#integrate FirstOrderIntegrator.integrate} method. The user object will be called @@ -104,11 +96,11 @@ integrated problem by itself.

Other default implementations of the {@link -org.apache.commons.math.ode.StepHandler StepHandler} interface are +org.apache.commons.math.ode.sampling.StepHandler StepHandler} interface are available for general needs ({@link -org.apache.commons.math.ode.DummyStepHandler DummyStepHandler}, {@link -org.apache.commons.math.ode.StepNormalizer StepNormalizer}) and custom -implementations can be developped for specific needs. As an example, +org.apache.commons.math.ode.sampling.DummyStepHandler DummyStepHandler}, {@link +org.apache.commons.math.ode.sampling.StepNormalizer StepNormalizer}) and custom +implementations can be developed for specific needs. As an example, if an application is to be completely driven by the integration process, then most of the application code will be run inside a step handler specific to this application. @@ -119,14 +111,14 @@ Some integrators (the simple ones) use fixed steps that are set at creation time. The more efficient integrators use variable steps that are handled internally in order to control the integration error with respect to a specified accuracy (these integrators extend the {@link -org.apache.commons.math.ode.AdaptiveStepsizeIntegrator +org.apache.commons.math.ode.nonstiff.AdaptiveStepsizeIntegrator AdaptiveStepsizeIntegrator} abstract class). In this case, the step handler which is called after each successful step shows up the variable stepsize. The {@link -org.apache.commons.math.ode.StepNormalizer StepNormalizer} class can +org.apache.commons.math.ode.sampling.StepNormalizer StepNormalizer} class can be used to convert the variable stepsize into a fixed stepsize that can be handled by classes implementing the {@link -org.apache.commons.math.ode.FixedStepHandler FixedStepHandler} +org.apache.commons.math.ode.sampling.FixedStepHandler FixedStepHandler} interface. Adaptive stepsize integrators can automatically compute the initial stepsize by themselves, however the user can specify it if he prefers to retain full control over the integration or if the @@ -137,21 +129,21 @@ automatic guess is wrong. - - - - - + + + + +
Fixed Step Integrators
NameOrder
{@link org.apache.commons.math.ode.EulerIntegrator Euler}1
{@link org.apache.commons.math.ode.MidpointIntegrator Midpoint}2
{@link org.apache.commons.math.ode.ClassicalRungeKuttaIntegrator Classical Runge-Kutta}4
{@link org.apache.commons.math.ode.GillIntegrator Gill}4
{@link org.apache.commons.math.ode.ThreeEighthesIntegrator 3/8}4
{@link org.apache.commons.math.ode.nonstiff.EulerIntegrator Euler}1
{@link org.apache.commons.math.ode.nonstiff.MidpointIntegrator Midpoint}2
{@link org.apache.commons.math.ode.nonstiff.ClassicalRungeKuttaIntegrator Classical Runge-Kutta}4
{@link org.apache.commons.math.ode.nonstiff.GillIntegrator Gill}4
{@link org.apache.commons.math.ode.nonstiff.ThreeEighthesIntegrator 3/8}4

- - - - + + + +
Adaptive Stepsize Integrators
NameIntegration OrderError Estimation Order
{@link org.apache.commons.math.ode.HighamHall54Integrator Higham and Hall}54
{@link org.apache.commons.math.ode.DormandPrince54Integrator Dormand-Prince 5(4)}54
{@link org.apache.commons.math.ode.DormandPrince853Integrator Dormand-Prince 8(5,3)}85 and 3
{@link org.apache.commons.math.ode.GraggBulirschStoerIntegrator Gragg-Bulirsch-Stoer}variable (up to 18 by default)variable
{@link org.apache.commons.math.ode.nonstiff.HighamHall54Integrator Higham and Hall}54
{@link org.apache.commons.math.ode.nonstiff.DormandPrince54Integrator Dormand-Prince 5(4)}54
{@link org.apache.commons.math.ode.nonstiff.DormandPrince853Integrator Dormand-Prince 8(5,3)}85 and 3
{@link org.apache.commons.math.ode.nonstiff.GraggBulirschStoerIntegrator Gragg-Bulirsch-Stoer}variable (up to 18 by default)variable

diff --git a/src/java/org/apache/commons/math/ode/AbstractStepInterpolator.java b/src/java/org/apache/commons/math/ode/sampling/AbstractStepInterpolator.java similarity index 74% rename from src/java/org/apache/commons/math/ode/AbstractStepInterpolator.java rename to src/java/org/apache/commons/math/ode/sampling/AbstractStepInterpolator.java index 3efcbdcb8..3ac9a3d8c 100644 --- a/src/java/org/apache/commons/math/ode/AbstractStepInterpolator.java +++ b/src/java/org/apache/commons/math/ode/sampling/AbstractStepInterpolator.java @@ -15,12 +15,18 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.sampling; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.IOException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderIntegrator; +import org.apache.commons.math.ode.SecondOrderIntegrator; +import org.apache.commons.math.ode.nonstiff.EmbeddedRungeKuttaIntegrator; + /** This abstract class represents an interpolator over the last step * during an ODE integration. * @@ -59,6 +65,9 @@ public abstract class AbstractStepInterpolator /** interpolated state */ protected double[] interpolatedState; + /** interpolated derivatives */ + protected double[] interpolatedDerivatives; + /** indicate if the step has been finalized or not. */ private boolean finalized; @@ -76,14 +85,15 @@ public abstract class AbstractStepInterpolator * model and latter initializing the copy. */ protected AbstractStepInterpolator() { - previousTime = Double.NaN; - currentTime = Double.NaN; - h = Double.NaN; - interpolatedTime = Double.NaN; - currentState = null; - interpolatedState = null; - finalized = false; - this.forward = true; + previousTime = Double.NaN; + currentTime = Double.NaN; + h = Double.NaN; + interpolatedTime = Double.NaN; + currentState = null; + interpolatedState = null; + interpolatedDerivatives = null; + finalized = false; + this.forward = true; } /** Simple constructor. @@ -91,15 +101,16 @@ public abstract class AbstractStepInterpolator * the end of the step * @param forward integration direction indicator */ - protected AbstractStepInterpolator(double[] y, boolean forward) { + protected AbstractStepInterpolator(final double[] y, final boolean forward) { previousTime = Double.NaN; currentTime = Double.NaN; h = Double.NaN; interpolatedTime = Double.NaN; - currentState = y; - interpolatedState = new double[y.length]; + currentState = y; + interpolatedState = new double[y.length]; + interpolatedDerivatives = new double[y.length]; finalized = false; this.forward = forward; @@ -123,7 +134,7 @@ public abstract class AbstractStepInterpolator * @param interpolator interpolator to copy from. */ - protected AbstractStepInterpolator(AbstractStepInterpolator interpolator) { + protected AbstractStepInterpolator(final AbstractStepInterpolator interpolator) { previousTime = interpolator.previousTime; currentTime = interpolator.currentTime; @@ -131,11 +142,13 @@ public abstract class AbstractStepInterpolator interpolatedTime = interpolator.interpolatedTime; if (interpolator.currentState != null) { - currentState = (double[]) interpolator.currentState.clone(); - interpolatedState = (double[]) interpolator.interpolatedState.clone(); + currentState = (double[]) interpolator.currentState.clone(); + interpolatedState = (double[]) interpolator.interpolatedState.clone(); + interpolatedDerivatives = (double[]) interpolator.interpolatedDerivatives.clone(); } else { - currentState = null; - interpolatedState = null; + currentState = null; + interpolatedState = null; + interpolatedDerivatives = null; } finalized = interpolator.finalized; @@ -148,30 +161,23 @@ public abstract class AbstractStepInterpolator * the end of the step * @param forward integration direction indicator */ - protected void reinitialize(double[] y, boolean forward) { + protected void reinitialize(final double[] y, final boolean forward) { previousTime = Double.NaN; currentTime = Double.NaN; h = Double.NaN; interpolatedTime = Double.NaN; - currentState = y; - interpolatedState = new double[y.length]; + currentState = y; + interpolatedState = new double[y.length]; + interpolatedDerivatives = new double[y.length]; finalized = false; this.forward = forward; } - /** Copy the instance. - *

The copied instance is guaranteed to be independent from the - * original one. Both can be used with different settings for - * interpolated time without any side effect.

- * @return a deep copy of the instance, which can be used independently. - * @throws DerivativeException if this call induces an automatic - * step finalization that throws one - * @see #setInterpolatedTime(double) - */ + /** {@inheritDoc} */ public StepInterpolator copy() throws DerivativeException { // finalize the step before performing copy @@ -202,7 +208,7 @@ public abstract class AbstractStepInterpolator /** Store the current step time. * @param t current time */ - public void storeTime(double t) { + public void storeTime(final double t) { currentTime = t; h = currentTime - previousTime; @@ -215,59 +221,31 @@ public abstract class AbstractStepInterpolator } - /** - * Get the previous grid point time. - * @return previous grid point time - */ + /** {@inheritDoc} */ public double getPreviousTime() { return previousTime; } - /** - * Get the current grid point time. - * @return current grid point time - */ + /** {@inheritDoc} */ public double getCurrentTime() { return currentTime; } - /** - * Get the time of the interpolated point. - * If {@link #setInterpolatedTime} has not been called, it returns - * the current grid point time. - * @return interpolation point time - */ + /** {@inheritDoc} */ public double getInterpolatedTime() { return interpolatedTime; } - /** - * Set the time of the interpolated point. - *

Setting the time outside of the current step is now allowed - * (it was not allowed up to version 5.4 of Mantissa), but should be - * used with care since the accuracy of the interpolator will - * probably be very poor far from this step. This allowance has been - * added to simplify implementation of search algorithms near the - * step endpoints.

- * @param time time of the interpolated point - * @throws DerivativeException if this call induces an automatic - * step finalization that throws one - */ - public void setInterpolatedTime(double time) - throws DerivativeException { - interpolatedTime = time; - double oneMinusThetaH = currentTime - interpolatedTime; - computeInterpolatedState((h - oneMinusThetaH) / h, oneMinusThetaH); + /** {@inheritDoc} */ + public void setInterpolatedTime(final double time) + throws DerivativeException { + interpolatedTime = time; + final double oneMinusThetaH = currentTime - interpolatedTime; + final double theta = (h == 0) ? 0 : (h - oneMinusThetaH) / h; + computeInterpolatedState(theta, oneMinusThetaH); } - /** Check if the natural integration direction is forward. - *

This method provides the integration direction as specified by the - * integrator itself, it avoid some nasty problems in degenerated - * cases like null steps due to cancellation at step initialization, - * step control or switching function triggering.

- * @return true if the integration variable (time) increases during - * integration - */ + /** {@inheritDoc} */ public boolean isForward() { return forward; } @@ -286,14 +264,15 @@ public abstract class AbstractStepInterpolator double oneMinusThetaH) throws DerivativeException; - /** - * Get the state vector of the interpolated point. - * @return state vector at time {@link #getInterpolatedTime} - */ + /** {@inheritDoc} */ public double[] getInterpolatedState() { - return (double[]) interpolatedState.clone(); + return interpolatedState; } + /** {@inheritDoc} */ + public double[] getInterpolatedDerivatives() { + return interpolatedDerivatives; + } /** * Finalize the step. @@ -355,17 +334,11 @@ public abstract class AbstractStepInterpolator throws DerivativeException { } - /** Write the instance to an output channel. - * @param out output channel - * @exception IOException if the instance cannot be written - */ + /** {@inheritDoc} */ public abstract void writeExternal(ObjectOutput out) throws IOException; - /** Read the instance from an input channel. - * @param in input channel - * @exception IOException if the instance cannot be read - */ + /** {@inheritDoc} */ public abstract void readExternal(ObjectInput in) throws IOException; @@ -375,7 +348,7 @@ public abstract class AbstractStepInterpolator * @param out stream where to save the state * @exception IOException in case of write error */ - protected void writeBaseExternal(ObjectOutput out) + protected void writeBaseExternal(final ObjectOutput out) throws IOException { out.writeInt(currentState.length); @@ -397,7 +370,7 @@ public abstract class AbstractStepInterpolator try { finalizeStep(); } catch (DerivativeException e) { - throw new IOException(e.getMessage()); + throw MathRuntimeException.createIOException(e); } } @@ -411,10 +384,10 @@ public abstract class AbstractStepInterpolator * @return interpolated time be set later by the caller * @exception IOException in case of read error */ - protected double readBaseExternal(ObjectInput in) + protected double readBaseExternal(final ObjectInput in) throws IOException { - int dimension = in.readInt(); + final int dimension = in.readInt(); previousTime = in.readDouble(); currentTime = in.readDouble(); h = in.readDouble(); @@ -426,8 +399,9 @@ public abstract class AbstractStepInterpolator } // we do NOT handle the interpolated time and state here - interpolatedTime = Double.NaN; - interpolatedState = new double[dimension]; + interpolatedTime = Double.NaN; + interpolatedState = new double[dimension]; + interpolatedDerivatives = new double[dimension]; finalized = true; diff --git a/src/java/org/apache/commons/math/ode/DummyStepHandler.java b/src/java/org/apache/commons/math/ode/sampling/DummyStepHandler.java similarity index 95% rename from src/java/org/apache/commons/math/ode/DummyStepHandler.java rename to src/java/org/apache/commons/math/ode/sampling/DummyStepHandler.java index 14ff484d9..4025bbf77 100644 --- a/src/java/org/apache/commons/math/ode/DummyStepHandler.java +++ b/src/java/org/apache/commons/math/ode/sampling/DummyStepHandler.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.sampling; import java.io.Serializable; @@ -80,7 +80,7 @@ public class DummyStepHandler * copy. * @param isLast true if the step is the last one */ - public void handleStep(StepInterpolator interpolator, boolean isLast) { + public void handleStep(final StepInterpolator interpolator, final boolean isLast) { } /** The only instance. */ diff --git a/src/java/org/apache/commons/math/ode/DummyStepInterpolator.java b/src/java/org/apache/commons/math/ode/sampling/DummyStepInterpolator.java similarity index 85% rename from src/java/org/apache/commons/math/ode/DummyStepInterpolator.java rename to src/java/org/apache/commons/math/ode/sampling/DummyStepInterpolator.java index 9d9aee7f6..880f6576b 100644 --- a/src/java/org/apache/commons/math/ode/DummyStepInterpolator.java +++ b/src/java/org/apache/commons/math/ode/sampling/DummyStepInterpolator.java @@ -15,12 +15,16 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.sampling; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.IOException; +import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.nonstiff.EmbeddedRungeKuttaIntegrator; + /** This class is a step interpolator that does nothing. * *

This class is used when the {@link StepHandler "step handler"} @@ -57,7 +61,7 @@ public class DummyStepInterpolator * the end of the step * @param forward integration direction indicator */ - protected DummyStepInterpolator(double[] y, boolean forward) { + public DummyStepInterpolator(final double[] y, final boolean forward) { super(y, forward); } @@ -66,7 +70,7 @@ public class DummyStepInterpolator * copy: its arrays are separated from the original arrays of the * instance */ - public DummyStepInterpolator(DummyStepInterpolator interpolator) { + public DummyStepInterpolator(final DummyStepInterpolator interpolator) { super(interpolator); } @@ -87,7 +91,7 @@ public class DummyStepInterpolator * @throws DerivativeException this exception is propagated to the caller if the * underlying user function triggers one */ - protected void computeInterpolatedState(double theta, double oneMinusThetaH) + protected void computeInterpolatedState(final double theta, final double oneMinusThetaH) throws DerivativeException { System.arraycopy(currentState, 0, interpolatedState, 0, currentState.length); } @@ -96,7 +100,7 @@ public class DummyStepInterpolator * @param out output channel * @exception IOException if the instance cannot be written */ - public void writeExternal(ObjectOutput out) + public void writeExternal(final ObjectOutput out) throws IOException { // save the state of the base class writeBaseExternal(out); @@ -106,17 +110,17 @@ public class DummyStepInterpolator * @param in input channel * @exception IOException if the instance cannot be read */ - public void readExternal(ObjectInput in) + public void readExternal(final ObjectInput in) throws IOException { // read the base class - double t = readBaseExternal(in); + final double t = readBaseExternal(in); try { // we can now set the interpolated time and state setInterpolatedTime(t); } catch (DerivativeException e) { - throw new IOException(e.getMessage()); + throw MathRuntimeException.createIOException(e); } } diff --git a/src/java/org/apache/commons/math/ode/FixedStepHandler.java b/src/java/org/apache/commons/math/ode/sampling/FixedStepHandler.java similarity index 70% rename from src/java/org/apache/commons/math/ode/FixedStepHandler.java rename to src/java/org/apache/commons/math/ode/sampling/FixedStepHandler.java index 915931ccc..d0d8f842a 100644 --- a/src/java/org/apache/commons/math/ode/FixedStepHandler.java +++ b/src/java/org/apache/commons/math/ode/sampling/FixedStepHandler.java @@ -15,7 +15,11 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.sampling; + +import java.io.Serializable; + +import org.apache.commons.math.ode.DerivativeException; /** * This interface represents a handler that should be called after @@ -36,20 +40,26 @@ package org.apache.commons.math.ode; * @since 1.2 */ -public interface FixedStepHandler { +public interface FixedStepHandler extends Serializable { /** * Handle the last accepted step * @param t time of the current step - * @param y state vector at t. For efficiency purposes, the {@link - * StepNormalizer} class reuse the same array on each call, so if + * StepNormalizer} class reuses the same array on each call, so if + * the instance wants to keep it across all calls (for example to + * provide at the end of the integration a complete array of all + * steps), it should build a local copy store this copy. + * @param yDot derivatives of the state vector state vector at t. + * For efficiency purposes, the {@link StepNormalizer} class reuses + * the same array on each call, so if * the instance wants to keep it across all calls (for example to * provide at the end of the integration a complete array of all * steps), it should build a local copy store this copy. - * @param isLast true if the step is the last one + * @throws DerivativeException if some error condition is encountered */ - public void handleStep(double t, double[] y, boolean isLast); - + public void handleStep(double t, double[] y, double[] yDot, boolean isLast) + throws DerivativeException; + } diff --git a/src/java/org/apache/commons/math/ode/StepHandler.java b/src/java/org/apache/commons/math/ode/sampling/StepHandler.java similarity index 89% rename from src/java/org/apache/commons/math/ode/StepHandler.java rename to src/java/org/apache/commons/math/ode/sampling/StepHandler.java index a5f6cfe82..1b8c03432 100644 --- a/src/java/org/apache/commons/math/ode/StepHandler.java +++ b/src/java/org/apache/commons/math/ode/sampling/StepHandler.java @@ -15,7 +15,14 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.sampling; + +import java.io.Serializable; + +import org.apache.commons.math.ode.ContinuousOutputModel; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderIntegrator; +import org.apache.commons.math.ode.SecondOrderIntegrator; /** * This interface represents a handler that should be called after @@ -37,7 +44,7 @@ package org.apache.commons.math.ode; * @since 1.2 */ -public interface StepHandler { +public interface StepHandler extends Serializable { /** Determines whether this handler needs dense output. *

This method allows the integrator to avoid performing extra diff --git a/src/java/org/apache/commons/math/ode/StepInterpolator.java b/src/java/org/apache/commons/math/ode/sampling/StepInterpolator.java similarity index 80% rename from src/java/org/apache/commons/math/ode/StepInterpolator.java rename to src/java/org/apache/commons/math/ode/sampling/StepInterpolator.java index 5bdd06b7d..ee3c9100b 100644 --- a/src/java/org/apache/commons/math/ode/StepInterpolator.java +++ b/src/java/org/apache/commons/math/ode/sampling/StepInterpolator.java @@ -15,10 +15,14 @@ * limitations under the License. */ -package org.apache.commons.math.ode; +package org.apache.commons.math.ode.sampling; import java.io.Externalizable; +import org.apache.commons.math.ode.DerivativeException; +import org.apache.commons.math.ode.FirstOrderIntegrator; +import org.apache.commons.math.ode.SecondOrderIntegrator; + /** This interface represents an interpolator over the last step * during an ODE integration. * @@ -78,15 +82,29 @@ public interface StepInterpolator /** * Get the state vector of the interpolated point. + *

The returned vector is a reference to a reused array, so + * it should not be modified and it should be copied if it needs + * to be preserved across several calls.

* @return state vector at time {@link #getInterpolatedTime} + * @see #getInterpolatedDerivatives() */ public double[] getInterpolatedState(); + /** + * Get the derivatives of the state vector of the interpolated point. + *

The returned vector is a reference to a reused array, so + * it should not be modified and it should be copied if it needs + * to be preserved across several calls.

+ * @return derivatives of the state vector at time {@link #getInterpolatedTime} + * @see #getInterpolatedState() + */ + public double[] getInterpolatedDerivatives(); + /** Check if the natural integration direction is forward. *

This method provides the integration direction as specified by * the integrator itself, it avoid some nasty problems in * degenerated cases like null steps due to cancellation at step - * initialization, step control or switching function + * initialization, step control or discrete events * triggering.

* @return true if the integration variable (time) increases during * integration diff --git a/src/java/org/apache/commons/math/ode/sampling/StepNormalizer.java b/src/java/org/apache/commons/math/ode/sampling/StepNormalizer.java new file mode 100644 index 000000000..6cdde737c --- /dev/null +++ b/src/java/org/apache/commons/math/ode/sampling/StepNormalizer.java @@ -0,0 +1,163 @@ +/* + * 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.ode.sampling; + +import org.apache.commons.math.ode.DerivativeException; + +/** + * This class wraps an object implementing {@link FixedStepHandler} + * into a {@link StepHandler}. + + *

This wrapper allows to use fixed step handlers with general + * integrators which cannot guaranty their integration steps will + * remain constant and therefore only accept general step + * handlers.

+ * + *

The stepsize used is selected at construction time. The {@link + * FixedStepHandler#handleStep handleStep} method of the underlying + * {@link FixedStepHandler} object is called at the beginning time of + * the integration t0 and also at times t0+h, t0+2h, ... If the + * integration range is an integer multiple of the stepsize, then the + * last point handled will be the endpoint of the integration tend, if + * not, the last point will belong to the interval [tend - h ; + * tend].

+ * + *

There is no constraint on the integrator, it can use any + * timestep it needs (time steps longer or shorter than the fixed time + * step and non-integer ratios are all allowed).

+ * + * @see StepHandler + * @see FixedStepHandler + * @version $Revision$ $Date$ + * @since 1.2 + */ + +public class StepNormalizer implements StepHandler { + + /** Serializable version identifier. */ + private static final long serialVersionUID = -789699939659144654L; + + /** Fixed time step. */ + private double h; + + /** Underlying step handler. */ + private final FixedStepHandler handler; + + /** Last step time. */ + private double lastTime; + + /** Last State vector. */ + private double[] lastState; + + /** Last Derivatives vector. */ + private double[] lastDerivatives; + + /** Integration direction indicator. */ + private boolean forward; + + /** Simple constructor. + * @param h fixed time step (sign is not used) + * @param handler fixed time step handler to wrap + */ + public StepNormalizer(final double h, final FixedStepHandler handler) { + this.h = Math.abs(h); + this.handler = handler; + reset(); + } + + /** Determines whether this handler needs dense output. + * This handler needs dense output in order to provide data at + * regularly spaced steps regardless of the steps the integrator + * uses, so this method always returns true. + * @return always true + */ + public boolean requiresDenseOutput() { + return true; + } + + /** Reset the step handler. + * Initialize the internal data as required before the first step is + * handled. + */ + public void reset() { + lastTime = Double.NaN; + lastState = null; + lastDerivatives = null; + forward = true; + } + + /** + * Handle the last accepted step + * @param interpolator interpolator for the last accepted step. For + * efficiency purposes, the various integrators reuse the same + * object on each call, so if the instance wants to keep it across + * all calls (for example to provide at the end of the integration a + * continuous model valid throughout the integration range), it + * should build a local copy using the clone method and store this + * copy. + * @param isLast true if the step is the last one + * @throws DerivativeException this exception is propagated to the + * caller if the underlying user function triggers one + */ + public void handleStep(final StepInterpolator interpolator, final boolean isLast) + throws DerivativeException { + + if (lastState == null) { + + lastTime = interpolator.getPreviousTime(); + interpolator.setInterpolatedTime(lastTime); + lastState = interpolator.getInterpolatedState().clone(); + lastDerivatives = interpolator.getInterpolatedDerivatives().clone(); + + // take the integration direction into account + forward = (interpolator.getCurrentTime() >= lastTime); + if (! forward) { + h = -h; + } + + } + + double nextTime = lastTime + h; + boolean nextInStep = forward ^ (nextTime > interpolator.getCurrentTime()); + while (nextInStep) { + + // output the stored previous step + handler.handleStep(lastTime, lastState, lastDerivatives, false); + + // store the next step + lastTime = nextTime; + interpolator.setInterpolatedTime(lastTime); + System.arraycopy(interpolator.getInterpolatedState(), 0, + lastState, 0, lastState.length); + System.arraycopy(interpolator.getInterpolatedDerivatives(), 0, + lastDerivatives, 0, lastDerivatives.length); + + nextTime += h; + nextInStep = forward ^ (nextTime > interpolator.getCurrentTime()); + + } + + if (isLast) { + // there will be no more steps, + // the stored one should be flagged as being the last + handler.handleStep(lastTime, lastState, lastDerivatives, true); + } + + } + +} diff --git a/src/java/org/apache/commons/math/ode/sampling/package.html b/src/java/org/apache/commons/math/ode/sampling/package.html new file mode 100644 index 000000000..46de4ef94 --- /dev/null +++ b/src/java/org/apache/commons/math/ode/sampling/package.html @@ -0,0 +1,60 @@ + + + + +

+This package provides classes to handle sampling steps during +Ordinary Differential Equations integration. +

+ +

+In addition to computing the evolution of the state vector at some grid points, all +ODE integrators also build up interpolation models of this evolution inside the +last computed step. If users are interested in these interpolators, they can register a +{@link org.apache.commons.math.ode.sampling.StepHandler StepHandler} instance using the +{@link org.apache.commons.math.ode.FirstOrderIntegrator#addStepHandler addStepHandler} +method which is supported by all integrators. The integrator will call this instance +at the end of each accepted step and provide it the interpolator. The user can do +whatever he wants with this interpolator, which computes both the state and its +time-derivative. A typical use of step handler is to provide some output to monitor +the integration process. +

+ +

+In a sense, this is a kind of Inversion Of Control: rather than having the master +application driving the slave integrator by providing the target end value for +the free variable, we get a master integrator scheduling the free variable +evolution and calling the slave application callbacks that were registered at +configuration time. +

+ +

+Since some integrators may use variable step size, the generic {@link +org.apache.commons.math.ode.sampling.StepHandler StepHandler} interface can be called +either at regular or irregular rate. This interface allows to navigate to any location +within the last computed step, thanks to the provided {@link +org.apache.commons.math.ode.sampling.StepInterpolator StepInterpolator} object. +If regular output is desired (for example in order to write an ephemeris file), then +the simpler {@link org.apache.commons.math.ode.sampling.FixedStepHandler FixedStepHandler} +interface can be used. Objects implementing this interface should be wrapped within a +{@link org.apache.commons.math.ode.sampling.StepNormalizer StepNormalizer} instance +in order to be registered to the integrator. +

+ + + diff --git a/src/java/org/apache/commons/math/optimization/DirectSearchOptimizer.java b/src/java/org/apache/commons/math/optimization/DirectSearchOptimizer.java index 1316d6e9e..582f03865 100644 --- a/src/java/org/apache/commons/math/optimization/DirectSearchOptimizer.java +++ b/src/java/org/apache/commons/math/optimization/DirectSearchOptimizer.java @@ -22,6 +22,7 @@ import java.util.Comparator; import org.apache.commons.math.ConvergenceException; import org.apache.commons.math.DimensionMismatchException; +import org.apache.commons.math.MathRuntimeException; import org.apache.commons.math.linear.RealMatrix; import org.apache.commons.math.random.CorrelatedRandomVectorGenerator; import org.apache.commons.math.random.JDKRandomGenerator; @@ -269,7 +270,7 @@ public abstract class DirectSearchOptimizer { } catch (DimensionMismatchException dme) { // this should not happen - throw new RuntimeException("internal error"); + throw new MathRuntimeException("unexpected exception caught", null, dme); } } @@ -571,16 +572,15 @@ public abstract class DirectSearchOptimizer { } /** Comparator for {@link PointCostPair PointCostPair} objects. */ - private static Comparator pointCostPairComparator = new Comparator() { - public int compare(Object o1, Object o2) { + private static Comparator pointCostPairComparator = + new Comparator() { + public int compare(PointCostPair o1, PointCostPair o2) { if (o1 == null) { return (o2 == null) ? 0 : +1; } else if (o2 == null) { return -1; } - double cost1 = ((PointCostPair) o1).getCost(); - double cost2 = ((PointCostPair) o2).getCost(); - return (cost1 < cost2) ? -1 : ((o1 == o2) ? 0 : +1); + return (o1.getCost() < o2.getCost()) ? -1 : ((o1 == o2) ? 0 : +1); } }; diff --git a/src/java/org/apache/commons/math/random/AbstractRandomGenerator.java b/src/java/org/apache/commons/math/random/AbstractRandomGenerator.java index fd633464d..aee3b37c2 100644 --- a/src/java/org/apache/commons/math/random/AbstractRandomGenerator.java +++ b/src/java/org/apache/commons/math/random/AbstractRandomGenerator.java @@ -32,6 +32,9 @@ package org.apache.commons.math.random; */ public abstract class AbstractRandomGenerator implements RandomGenerator { + /** Serializable version identifier. */ + private static final long serialVersionUID = 7026539017027693474L; + /** * Cached random normal value. The default implementation for * {@link #nextGaussian} generates pairs of values and this field caches the diff --git a/src/java/org/apache/commons/math/random/CorrelatedRandomVectorGenerator.java b/src/java/org/apache/commons/math/random/CorrelatedRandomVectorGenerator.java index 06de22249..fd7a925d9 100644 --- a/src/java/org/apache/commons/math/random/CorrelatedRandomVectorGenerator.java +++ b/src/java/org/apache/commons/math/random/CorrelatedRandomVectorGenerator.java @@ -253,7 +253,7 @@ implements RandomVectorGenerator { // build the root matrix root = new RealMatrixImpl(order, rank); for (int i = 0; i < order; ++i) { - System.arraycopy(b[i], 0, root.getDataRef()[swap[i]], 0, rank); + System.arraycopy(b[i], 0, root.getDataRef()[index[i]], 0, rank); } } diff --git a/src/java/org/apache/commons/math/random/EmpiricalDistribution.java b/src/java/org/apache/commons/math/random/EmpiricalDistribution.java index 60696fed3..94bdbdb2a 100644 --- a/src/java/org/apache/commons/math/random/EmpiricalDistribution.java +++ b/src/java/org/apache/commons/math/random/EmpiricalDistribution.java @@ -23,6 +23,7 @@ import java.net.URL; import java.util.List; import org.apache.commons.math.stat.descriptive.StatisticalSummary; +import org.apache.commons.math.stat.descriptive.SummaryStatistics; /** * Represents an @@ -118,7 +119,7 @@ public interface EmpiricalDistribution { * * @return List of bin statistics */ - List getBinStats(); + List getBinStats(); /** * Returns the array of upper bounds for the bins. Bins are:
diff --git a/src/java/org/apache/commons/math/random/EmpiricalDistributionImpl.java b/src/java/org/apache/commons/math/random/EmpiricalDistributionImpl.java index 909fcc35b..d64b828b2 100644 --- a/src/java/org/apache/commons/math/random/EmpiricalDistributionImpl.java +++ b/src/java/org/apache/commons/math/random/EmpiricalDistributionImpl.java @@ -17,19 +17,19 @@ package org.apache.commons.math.random; -import java.io.EOFException; -import java.io.Serializable; import java.io.BufferedReader; -import java.io.FileReader; import java.io.File; +import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; +import java.io.Serializable; import java.net.URL; import java.util.ArrayList; import java.util.List; -import org.apache.commons.math.stat.descriptive.SummaryStatistics; +import org.apache.commons.math.MathRuntimeException; import org.apache.commons.math.stat.descriptive.StatisticalSummary; +import org.apache.commons.math.stat.descriptive.SummaryStatistics; /** * Implements EmpiricalDistribution interface. This implementation @@ -61,10 +61,10 @@ import org.apache.commons.math.stat.descriptive.StatisticalSummary; public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistribution { /** Serializable version identifier */ - private static final long serialVersionUID = -6773236347582113490L; + private static final long serialVersionUID = 5729073523949762654L; /** List of SummaryStatistics objects characterizing the bins */ - private List binStats = null; + private List binStats = null; /** Sample statistics */ private SummaryStatistics sampleStats = null; @@ -85,7 +85,7 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib * Creates a new EmpiricalDistribution with the default bin count. */ public EmpiricalDistributionImpl() { - binStats = new ArrayList(); + binStats = new ArrayList(); } /** @@ -95,7 +95,7 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib */ public EmpiricalDistributionImpl(int binCount) { this.binCount = binCount; - binStats = new ArrayList(); + binStats = new ArrayList(); } /** @@ -110,7 +110,7 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib da.computeStats(); fillBinStats(in); } catch (Exception e) { - throw new RuntimeException(e.getMessage()); + throw new MathRuntimeException(e); } loaded = true; @@ -129,11 +129,18 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib DataAdapter da = new StreamDataAdapter(in); try { da.computeStats(); + } catch (IOException ioe) { + // don't wrap exceptions which are already IOException + throw ioe; + } catch (RuntimeException rte) { + // don't wrap RuntimeExceptions + throw rte; } catch (Exception e) { - throw new IOException(e.getMessage()); + throw MathRuntimeException.createIOException(e); } if (sampleStats.getN() == 0) { - throw new EOFException("URL " + url + " contains no data"); + throw MathRuntimeException.createEOFException("URL {0} contains no data", + new Object[] { url }); } in = new BufferedReader(new InputStreamReader(url.openStream())); fillBinStats(in); @@ -161,8 +168,14 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib DataAdapter da = new StreamDataAdapter(in); try { da.computeStats(); + } catch (IOException ioe) { + // don't wrap exceptions which are already IOException + throw ioe; + } catch (RuntimeException rte) { + // don't wrap RuntimeExceptions + throw rte; } catch (Exception e) { - throw new IOException(e.getMessage()); + throw MathRuntimeException.createIOException(e); } in = new BufferedReader(new FileReader(file)); fillBinStats(in); @@ -254,8 +267,7 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib double val = 0.0d; while ((str = inputStream.readLine()) != null) { val = Double.parseDouble(str); - SummaryStatistics stats = - (SummaryStatistics) binStats.get(findBin(min, val, delta)); + SummaryStatistics stats = binStats.get(findBin(min, val, delta)); stats.addValue(val); } @@ -272,7 +284,7 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib double val = 0.0; sampleStats = new SummaryStatistics(); while ((str = inputStream.readLine()) != null) { - val = new Double(str).doubleValue(); + val = Double.valueOf(str).doubleValue(); sampleStats.addValue(val); } inputStream.close(); @@ -319,8 +331,7 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib throws IOException { for (int i = 0; i < inputArray.length; i++) { SummaryStatistics stats = - (SummaryStatistics) binStats.get( - findBin(min, inputArray[i], delta)); + binStats.get(findBin(min, inputArray[i], delta)); stats.addValue(inputArray[i]); } } @@ -336,7 +347,7 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib // Load array of bin upper bounds -- evenly spaced from min - max double min = sampleStats.getMin(); double max = sampleStats.getMax(); - double delta = (max - min)/(new Double(binCount)).doubleValue(); + double delta = (max - min)/(Double.valueOf(binCount)).doubleValue(); double[] binUpperBounds = new double[binCount]; binUpperBounds[0] = min + delta; for (int i = 1; i< binCount - 1; i++) { @@ -358,23 +369,23 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib DataAdapter da = aFactory.getAdapter(in); try { da.computeBinStats(min, delta); + } catch (IOException ioe) { + // don't wrap exceptions which are already IOException + throw ioe; + } catch (RuntimeException rte) { + // don't wrap RuntimeExceptions + throw rte; } catch (Exception e) { - if(e instanceof RuntimeException){ - throw new RuntimeException(e.getMessage()); - }else{ - throw new IOException(e.getMessage()); - } + throw MathRuntimeException.createIOException(e); } // Assign upperBounds based on bin counts upperBounds = new double[binCount]; upperBounds[0] = - ((double)((SummaryStatistics)binStats.get(0)).getN())/ - (double)sampleStats.getN(); + ((double) binStats.get(0).getN()) / (double) sampleStats.getN(); for (int i = 1; i < binCount-1; i++) { upperBounds[i] = upperBounds[i-1] + - ((double)((SummaryStatistics)binStats.get(i)).getN())/ - (double)sampleStats.getN(); + ((double) binStats.get(i).getN()) / (double) sampleStats.getN(); } upperBounds[binCount-1] = 1.0d; } @@ -402,7 +413,8 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib public double getNextValue() throws IllegalStateException { if (!loaded) { - throw new IllegalStateException("distribution not loaded"); + throw MathRuntimeException.createIllegalStateException("distribution not loaded", + null); } // Start with a uniformly distributed random number in (0,1) @@ -411,7 +423,7 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib // Use this to select the bin and generate a Gaussian within the bin for (int i = 0; i < binCount; i++) { if (x <= upperBounds[i]) { - SummaryStatistics stats = (SummaryStatistics)binStats.get(i); + SummaryStatistics stats = binStats.get(i); if (stats.getN() > 0) { if (stats.getStandardDeviation() > 0) { // more than one obs return randomData.nextGaussian @@ -422,7 +434,7 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib } } } - throw new RuntimeException("No bin selected"); + throw new MathRuntimeException("no bin selected", null); } /** @@ -453,7 +465,7 @@ public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistrib * * @return List of bin statistics. */ - public List getBinStats() { + public List getBinStats() { return binStats; } diff --git a/src/java/org/apache/commons/math/random/JDKRandomGenerator.java b/src/java/org/apache/commons/math/random/JDKRandomGenerator.java index 736790802..278991f2a 100644 --- a/src/java/org/apache/commons/math/random/JDKRandomGenerator.java +++ b/src/java/org/apache/commons/math/random/JDKRandomGenerator.java @@ -23,7 +23,7 @@ import java.util.Random; * {@link RandomGenerator}. * * @since 1.1 - * @version $Revision:$ $Date$ + * @version $Revision$ $Date$ */ public class JDKRandomGenerator extends Random implements RandomGenerator { /** Serializable version identifier */ diff --git a/src/java/org/apache/commons/math/random/NotPositiveDefiniteMatrixException.java b/src/java/org/apache/commons/math/random/NotPositiveDefiniteMatrixException.java index 8b7ae9ac4..581dd7a2f 100644 --- a/src/java/org/apache/commons/math/random/NotPositiveDefiniteMatrixException.java +++ b/src/java/org/apache/commons/math/random/NotPositiveDefiniteMatrixException.java @@ -36,7 +36,7 @@ public class NotPositiveDefiniteMatrixException extends MathException { * build an exception with a default message. */ public NotPositiveDefiniteMatrixException() { - super("not positive definite matrix", new Object[0]); + super("not positive definite matrix", null); } } diff --git a/src/java/org/apache/commons/math/random/RandomAdaptor.java b/src/java/org/apache/commons/math/random/RandomAdaptor.java index c065bd100..8eb56decf 100644 --- a/src/java/org/apache/commons/math/random/RandomAdaptor.java +++ b/src/java/org/apache/commons/math/random/RandomAdaptor.java @@ -23,7 +23,7 @@ import java.util.Random; * {@link RandomGenerator}. * * @since 1.1 - * @version $Revision:$ $Date$ + * @version $Revision$ $Date$ */ public class RandomAdaptor extends Random implements RandomGenerator { diff --git a/src/java/org/apache/commons/math/random/RandomData.java b/src/java/org/apache/commons/math/random/RandomData.java index 53f8f4ca0..842c83fdb 100644 --- a/src/java/org/apache/commons/math/random/RandomData.java +++ b/src/java/org/apache/commons/math/random/RandomData.java @@ -268,5 +268,5 @@ public interface RandomData { * @param k size of the sample * @return random sample of k elements from c */ - Object[] nextSample(Collection c, int k); + Object[] nextSample(Collection c, int k); } diff --git a/src/java/org/apache/commons/math/random/RandomDataImpl.java b/src/java/org/apache/commons/math/random/RandomDataImpl.java index 8d2e7ae5d..d8f84540e 100644 --- a/src/java/org/apache/commons/math/random/RandomDataImpl.java +++ b/src/java/org/apache/commons/math/random/RandomDataImpl.java @@ -137,7 +137,7 @@ public class RandomDataImpl implements RandomData, Serializable { //Convert each byte to 2 hex digits for (int i = 0; i < randomBytes.length; i++) { - Integer c = new Integer(randomBytes[i]); + Integer c = Integer.valueOf(randomBytes[i]); /* Add 128 to byte value to make interval 0-255 before * doing hex conversion. @@ -236,7 +236,7 @@ public class RandomDataImpl implements RandomData, Serializable { //Loop over the hash, converting each byte to 2 hex digits for (int i = 0; i < hash.length; i++) { - Integer c = new Integer(hash[i]); + Integer c = Integer.valueOf(hash[i]); /* Add 128 to byte value to make interval 0-255 * This guarantees <= 2 hex digits from toHexString() @@ -504,13 +504,27 @@ public class RandomDataImpl implements RandomData, Serializable { } /** - * Uses a 2-cycle permutation shuffle to generate a random permutation. - * The shuffling process is described + * Generates an integer array of length k whose entries + * are selected randomly, without repetition, from the integers + * 0 through n-1 (inclusive). + *

+ * Generated arrays represent permutations + * of n taken k at a time.

+ *

+ * Preconditions:

    + *
  • k <= n
  • + *
  • n > 0
  • + *
+ * If the preconditions are not met, an IllegalArgumentException is + * thrown.

+ *

+ * Uses a 2-cycle permutation shuffle. The shuffling process is described * - * here. - * @param n the population size. - * @param k the number to choose. - * @return the random permutation. + * here.

+ * + * @param n domain of the permutation (must be positive) + * @param k size of the permutation (must satisfy 0 < k <= n). + * @return the random permutation as an int array */ public int[] nextPermutation(int n, int k) { if (k > n) { @@ -545,7 +559,7 @@ public class RandomDataImpl implements RandomData, Serializable { * @param k sample size. * @return the random sample. */ - public Object[] nextSample(Collection c, int k) { + public Object[] nextSample(Collection c, int k) { int len = c.size(); if (k > len) { throw new IllegalArgumentException diff --git a/src/java/org/apache/commons/math/random/RandomGenerator.java b/src/java/org/apache/commons/math/random/RandomGenerator.java index c9fac56a7..02ee3d9c3 100644 --- a/src/java/org/apache/commons/math/random/RandomGenerator.java +++ b/src/java/org/apache/commons/math/random/RandomGenerator.java @@ -23,7 +23,7 @@ import java.io.Serializable; * implemented by {@link AbstractRandomGenerator}. * * @since 1.1 - * @version $Revision:$ $Date$ + * @version $Revision$ $Date$ */ public interface RandomGenerator extends Serializable { diff --git a/src/java/org/apache/commons/math/random/ValueServer.java b/src/java/org/apache/commons/math/random/ValueServer.java index 0bbcb985b..6e8269aff 100644 --- a/src/java/org/apache/commons/math/random/ValueServer.java +++ b/src/java/org/apache/commons/math/random/ValueServer.java @@ -17,11 +17,12 @@ package org.apache.commons.math.random; import java.io.BufferedReader; -import java.io.EOFException; -import java.io.InputStreamReader; import java.io.IOException; -import java.net.URL; +import java.io.InputStreamReader; import java.net.MalformedURLException; +import java.net.URL; + +import org.apache.commons.math.MathRuntimeException; /** * Generates values for use in simulation applications. @@ -105,8 +106,18 @@ public class ValueServer { case EXPONENTIAL_MODE: return getNextExponential(); case GAUSSIAN_MODE: return getNextGaussian(); case CONSTANT_MODE: return mu; - default: throw new IllegalStateException - ("Bad mode: " + mode); + default: throw MathRuntimeException.createIllegalStateException("unknown mode {0}, known modes: " + + "{1} ({2}), {3} ({4}), {5} ({6}), " + + "{7} ({8}), {9} ({10}) and {11} ({12})", + new Object[] { + mode, + "DIGEST_MODE", DIGEST_MODE, + "REPLAY_MODE", REPLAY_MODE, + "UNIFORM_MODE", UNIFORM_MODE, + "EXPONENTIAL_MODE", EXPONENTIAL_MODE, + "GAUSSIAN_MODE", GAUSSIAN_MODE, + "CONSTANT_MODE", CONSTANT_MODE + }); } } @@ -295,7 +306,7 @@ public class ValueServer { private double getNextDigest() { if ((empiricalDistribution == null) || (empiricalDistribution.getBinStats().size() == 0)) { - throw new IllegalStateException("Digest not initialized"); + throw MathRuntimeException.createIllegalStateException("digest not initialized", null); } return empiricalDistribution.getNextValue(); } @@ -328,7 +339,8 @@ public class ValueServer { closeReplayFile(); resetReplayFile(); if ((str = filePointer.readLine()) == null) { - throw new EOFException("URL " + valuesFileURL + " contains no data"); + throw MathRuntimeException.createEOFException("URL {0} contains no data", + new Object[] { valuesFileURL }); } } return Double.valueOf(str).doubleValue(); diff --git a/src/java/org/apache/commons/math/stat/Frequency.java b/src/java/org/apache/commons/math/stat/Frequency.java index 8254b709b..6b55cfabf 100644 --- a/src/java/org/apache/commons/math/stat/Frequency.java +++ b/src/java/org/apache/commons/math/stat/Frequency.java @@ -30,7 +30,7 @@ import java.util.TreeMap; * throw an IllegalArgumentException.

*

* Integer values (int, long, Integer, Long) are not distinguished by type -- - * i.e. addValue(new Long(2)), addValue(2), addValue(2l) all have + * i.e. addValue(Long.valueOf(2)), addValue(2), addValue(2l) all have * the same effect (similarly for arguments to getCount, etc.).

*

* The values are ordered using the default (natural order), unless a @@ -96,14 +96,14 @@ public class Frequency implements Serializable { public void addValue(Object v) { Object obj = v; if (v instanceof Integer) { - obj = new Long(((Integer) v).longValue()); + obj = Long.valueOf(((Integer) v).longValue()); } try { Long count = (Long) freqTable.get(obj); if (count == null) { - freqTable.put(obj, new Long(1)); + freqTable.put(obj, Long.valueOf(1)); } else { - freqTable.put(obj, new Long(count.longValue() + 1)); + freqTable.put(obj, Long.valueOf(count.longValue() + 1)); } } catch (ClassCastException ex) { //TreeMap will throw ClassCastException if v is not comparable @@ -117,7 +117,7 @@ public class Frequency implements Serializable { * @param v the value to add. */ public void addValue(int v) { - addValue(new Long(v)); + addValue(Long.valueOf(v)); } /** @@ -126,7 +126,7 @@ public class Frequency implements Serializable { * @param v the value to add. */ public void addValue(Integer v) { - addValue(new Long(v.longValue())); + addValue(Long.valueOf(v.longValue())); } /** @@ -135,7 +135,7 @@ public class Frequency implements Serializable { * @param v the value to add. */ public void addValue(long v) { - addValue(new Long(v)); + addValue(Long.valueOf(v)); } /** @@ -144,7 +144,7 @@ public class Frequency implements Serializable { * @param v the value to add. */ public void addValue(char v) { - addValue(new Character(v)); + addValue(Character.valueOf(v)); } /** Clears the frequency table */ @@ -155,7 +155,7 @@ public class Frequency implements Serializable { /** * Returns an Iterator over the set of values that have been added. *

- * If added values are itegral (i.e., integers, longs, Integers, or Longs), + * If added values are integral (i.e., integers, longs, Integers, or Longs), * they are converted to Longs when they are added, so the objects returned * by the Iterator will in this case be Longs.

* @@ -210,7 +210,7 @@ public class Frequency implements Serializable { * @return the frequency of v. */ public long getCount(int v) { - return getCount(new Long(v)); + return getCount(Long.valueOf(v)); } /** @@ -220,7 +220,7 @@ public class Frequency implements Serializable { * @return the frequency of v. */ public long getCount(long v) { - return getCount(new Long(v)); + return getCount(Long.valueOf(v)); } /** @@ -230,7 +230,7 @@ public class Frequency implements Serializable { * @return the frequency of v. */ public long getCount(char v) { - return getCount(new Character(v)); + return getCount(Character.valueOf(v)); } //------------------------------------------------------------- @@ -259,7 +259,7 @@ public class Frequency implements Serializable { * @return the proportion of values equal to v */ public double getPct(int v) { - return getPct(new Long(v)); + return getPct(Long.valueOf(v)); } /** @@ -270,7 +270,7 @@ public class Frequency implements Serializable { * @return the proportion of values equal to v */ public double getPct(long v) { - return getPct(new Long(v)); + return getPct(Long.valueOf(v)); } /** @@ -281,7 +281,7 @@ public class Frequency implements Serializable { * @return the proportion of values equal to v */ public double getPct(char v) { - return getPct(new Character(v)); + return getPct(Character.valueOf(v)); } //----------------------------------------------------------------------------------------- @@ -345,7 +345,7 @@ public class Frequency implements Serializable { * @return the proportion of values equal to v */ public long getCumFreq(int v) { - return getCumFreq(new Long(v)); + return getCumFreq(Long.valueOf(v)); } /** @@ -357,7 +357,7 @@ public class Frequency implements Serializable { * @return the proportion of values equal to v */ public long getCumFreq(long v) { - return getCumFreq(new Long(v)); + return getCumFreq(Long.valueOf(v)); } /** @@ -369,7 +369,7 @@ public class Frequency implements Serializable { * @return the proportion of values equal to v */ public long getCumFreq(char v) { - return getCumFreq(new Character(v)); + return getCumFreq(Character.valueOf(v)); } //---------------------------------------------------------------------------------------------- @@ -402,7 +402,7 @@ public class Frequency implements Serializable { * @return the proportion of values less than or equal to v */ public double getCumPct(int v) { - return getCumPct(new Long(v)); + return getCumPct(Long.valueOf(v)); } /** @@ -415,7 +415,7 @@ public class Frequency implements Serializable { * @return the proportion of values less than or equal to v */ public double getCumPct(long v) { - return getCumPct(new Long(v)); + return getCumPct(Long.valueOf(v)); } /** @@ -428,7 +428,7 @@ public class Frequency implements Serializable { * @return the proportion of values less than or equal to v */ public double getCumPct(char v) { - return getCumPct(new Character(v)); + return getCumPct(Character.valueOf(v)); } /** diff --git a/src/java/org/apache/commons/math/stat/descriptive/AbstractStorelessUnivariateStatistic.java b/src/java/org/apache/commons/math/stat/descriptive/AbstractStorelessUnivariateStatistic.java index d679db03b..a1b0d64d0 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/AbstractStorelessUnivariateStatistic.java +++ b/src/java/org/apache/commons/math/stat/descriptive/AbstractStorelessUnivariateStatistic.java @@ -51,7 +51,8 @@ public abstract class AbstractStorelessUnivariateStatistic * input array.

*

* If the array is null, an IllegalArgumentException is thrown.

- * + * @param values input array + * @return the value of the statistic applied to the input array * @see org.apache.commons.math.stat.descriptive.UnivariateStatistic#evaluate(double[]) */ public double evaluate(final double[] values) { @@ -76,7 +77,10 @@ public abstract class AbstractStorelessUnivariateStatistic *

* If the array is null or the index parameters are not valid, an * IllegalArgumentException is thrown.

- * + * @param values the input array + * @param begin the index of the first element to include + * @param length the number of elements to include + * @return the value of the statistic applied to the included array entries * @see org.apache.commons.math.stat.descriptive.UnivariateStatistic#evaluate(double[], int, int) */ public double evaluate(final double[] values, final int begin, final int length) { @@ -88,17 +92,17 @@ public abstract class AbstractStorelessUnivariateStatistic } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public abstract void clear(); /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public abstract double getResult(); /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * {@inheritDoc} */ public abstract void increment(final double d); diff --git a/src/java/org/apache/commons/math/stat/descriptive/AbstractUnivariateStatistic.java b/src/java/org/apache/commons/math/stat/descriptive/AbstractUnivariateStatistic.java index d03d51502..68281f69f 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/AbstractUnivariateStatistic.java +++ b/src/java/org/apache/commons/math/stat/descriptive/AbstractUnivariateStatistic.java @@ -38,7 +38,7 @@ public abstract class AbstractUnivariateStatistic private static final long serialVersionUID = -8007759382851708045L; /** - * @see org.apache.commons.math.stat.descriptive.UnivariateStatistic#evaluate(double[]) + * {@inheritDoc} */ public double evaluate(final double[] values) { test(values, 0, 0); @@ -46,9 +46,14 @@ public abstract class AbstractUnivariateStatistic } /** - * @see org.apache.commons.math.stat.descriptive.UnivariateStatistic#evaluate(double[], int, int) + * {@inheritDoc} */ public abstract double evaluate(final double[] values, final int begin, final int length); + + /** + * {@inheritDoc} + */ + public abstract UnivariateStatistic copy(); /** * This method is used by evaluate(double[], int, int) methods diff --git a/src/java/org/apache/commons/math/stat/descriptive/DescriptiveStatistics.java b/src/java/org/apache/commons/math/stat/descriptive/DescriptiveStatistics.java index 6fa020b17..b234bd4fe 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/DescriptiveStatistics.java +++ b/src/java/org/apache/commons/math/stat/descriptive/DescriptiveStatistics.java @@ -20,7 +20,6 @@ import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; -import org.apache.commons.discovery.tools.DiscoverClass; import org.apache.commons.math.stat.descriptive.moment.GeometricMean; import org.apache.commons.math.stat.descriptive.moment.Kurtosis; import org.apache.commons.math.stat.descriptive.moment.Mean; @@ -56,8 +55,8 @@ import org.apache.commons.math.util.ResizableDoubleArray; public class DescriptiveStatistics implements StatisticalSummary, Serializable { /** Serialization UID */ - private static final long serialVersionUID = -2734185686570407433L; - + private static final long serialVersionUID = 4133067267405273064L; + /** hold the window size **/ protected int windowSize = INFINITE_WINDOW; @@ -113,36 +112,13 @@ public class DescriptiveStatistics implements StatisticalSummary, Serializable { } /** - * Create an instance of a DescriptiveStatistics - * @param cls the type of DescriptiveStatistics object to - * create. - * @return a new instance. - * @throws InstantiationException is thrown if the object can not be - * created. - * @throws IllegalAccessException is thrown if the type's default - * constructor is not accessible. - * @deprecated to be removed in commons-math 2.0 + * Copy constructor. Construct a new DescriptiveStatistics instance that + * is a copy of original. + * + * @param original DescriptiveStatistics instance to copy */ - public static DescriptiveStatistics newInstance(Class cls) throws InstantiationException, IllegalAccessException { - return (DescriptiveStatistics)cls.newInstance(); - } - - /** - * Create an instance of a DescriptiveStatistics - * @return a new DescriptiveStatistics instance. - * @deprecated to be removed in commons-math 2.0 - */ - public static DescriptiveStatistics newInstance() { - DescriptiveStatistics factory = null; - try { - DiscoverClass dc = new DiscoverClass(); - factory = (DescriptiveStatistics) dc.newInstance( - DescriptiveStatistics.class, - "org.apache.commons.math.stat.descriptive.DescriptiveStatisticsImpl"); - } catch(Throwable t) { - return new DescriptiveStatisticsImpl(); - } - return factory; + public DescriptiveStatistics(DescriptiveStatistics original) { + copy(original, this); } /** @@ -172,6 +148,24 @@ public class DescriptiveStatistics implements StatisticalSummary, Serializable { } } + /** + * Removes the most recent value from the dataset. + */ + public void removeMostRecentValue() { + eDA.discardMostRecentElements(1); + } + + /** + * Replaces the most recently stored value with the given value. + * There must be at least one element stored to call this method. + * + * @param v the value to replace the most recent stored value + * @return replaced value + */ + public double replaceMostRecentValue(double v) { + return eDA.substituteMostRecentElement(v); + } + /** * Returns the * arithmetic mean of the available values @@ -185,7 +179,7 @@ public class DescriptiveStatistics implements StatisticalSummary, Serializable { * Returns the * geometric mean of the available values * @return The geometricMean, Double.NaN if no values have been added, - * or if the productof the available values is less than or equal to 0. + * or if the product of the available values is less than or equal to 0. */ public double getGeometricMean() { return apply(geometricMeanImpl); @@ -387,7 +381,7 @@ public class DescriptiveStatistics implements StatisticalSummary, Serializable { try { percentileImpl.getClass().getMethod("setQuantile", new Class[] {Double.TYPE}).invoke(percentileImpl, - new Object[] {new Double(p)}); + new Object[] {Double.valueOf(p)}); } catch (NoSuchMethodException e1) { // Setter guard should prevent throw new IllegalArgumentException( "Percentile implementation does not support setQuantile"); @@ -568,7 +562,7 @@ public class DescriptiveStatistics implements StatisticalSummary, Serializable { try { percentileImpl.getClass().getMethod("setQuantile", new Class[] {Double.TYPE}).invoke(percentileImpl, - new Object[] {new Double(50.0d)}); + new Object[] {Double.valueOf(50.0d)}); } catch (NoSuchMethodException e1) { throw new IllegalArgumentException( "Percentile implementation does not support setQuantile"); @@ -666,5 +660,42 @@ public class DescriptiveStatistics implements StatisticalSummary, Serializable { */ public synchronized void setSumImpl(UnivariateStatistic sumImpl) { this.sumImpl = sumImpl; - } + } + + /** + * Returns a copy of this DescriptiveStatistics instance with the same internal state. + * + * @return a copy of this + */ + public DescriptiveStatistics copy() { + DescriptiveStatistics result = new DescriptiveStatistics(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source DescriptiveStatistics to copy + * @param dest DescriptiveStatistics to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(DescriptiveStatistics source, DescriptiveStatistics dest) { + // Copy data and window size + dest.eDA = source.eDA.copy(); + dest.windowSize = source.windowSize; + + // Copy implementations + dest.maxImpl = source.maxImpl.copy(); + dest.meanImpl = source.meanImpl.copy(); + dest.minImpl = source.minImpl.copy(); + dest.sumImpl = source.sumImpl.copy(); + dest.varianceImpl = source.varianceImpl.copy(); + dest.sumsqImpl = source.sumsqImpl.copy(); + dest.geometricMeanImpl = source.geometricMeanImpl.copy(); + dest.kurtosisImpl = source.kurtosisImpl; + dest.skewnessImpl = source.skewnessImpl; + dest.percentileImpl = source.percentileImpl; + } } diff --git a/src/java/org/apache/commons/math/stat/descriptive/DescriptiveStatisticsImpl.java b/src/java/org/apache/commons/math/stat/descriptive/DescriptiveStatisticsImpl.java deleted file mode 100644 index 659df61ce..000000000 --- a/src/java/org/apache/commons/math/stat/descriptive/DescriptiveStatisticsImpl.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.stat.descriptive; - -import java.io.Serializable; - -/** - * Default implementation of - * {@link org.apache.commons.math.stat.descriptive.DescriptiveStatistics}. - * - * @deprecated to be removed in commons-math 2.0. - * Use {@link DescriptiveStatistics} - * - * @version $Revision$ $Date$ - */ -public class DescriptiveStatisticsImpl extends DescriptiveStatistics implements Serializable { - - /** Serializable version identifier */ - private static final long serialVersionUID = -6467796944112488424L; - - /** - * Construct a DescriptiveStatisticsImpl with infinite window - */ - public DescriptiveStatisticsImpl() { - super(); - } - - /** - * Construct a DescriptiveStatisticsImpl with finite window - * @param window the finite window size. - */ - public DescriptiveStatisticsImpl(int window) { - super(window); - } - - /** - * Resets all statistics and storage - */ - public void clear() { - super.clear(); - } -} diff --git a/src/java/org/apache/commons/math/stat/descriptive/MultivariateSummaryStatistics.java b/src/java/org/apache/commons/math/stat/descriptive/MultivariateSummaryStatistics.java index 2d8da9e58..316196bfb 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/MultivariateSummaryStatistics.java +++ b/src/java/org/apache/commons/math/stat/descriptive/MultivariateSummaryStatistics.java @@ -20,6 +20,7 @@ import java.io.Serializable; import java.util.Arrays; import org.apache.commons.math.DimensionMismatchException; +import org.apache.commons.math.MathRuntimeException; import org.apache.commons.math.linear.RealMatrix; import org.apache.commons.math.stat.descriptive.moment.GeometricMean; import org.apache.commons.math.stat.descriptive.moment.Mean; @@ -62,7 +63,7 @@ import org.apache.commons.math.util.MathUtils; * threads is required.

* * @since 1.2 - * @version $Revision: 618097 $ $Date: 2008-02-03 22:39:08 +0100 (dim., 03 févr. 2008) $ + * @version $Revision$ $Date$ */ public class MultivariateSummaryStatistics implements StatisticalMultivariateSummary, Serializable { @@ -609,8 +610,8 @@ public class MultivariateSummaryStatistics */ private void checkEmpty() { if (n > 0) { - throw new IllegalStateException( - "Implementations must be configured before values are added."); + throw MathRuntimeException.createIllegalStateException("{0} values have been added before statistic is configured", + new Object[] { n }); } } diff --git a/src/java/org/apache/commons/math/stat/descriptive/StatisticalMultivariateSummary.java b/src/java/org/apache/commons/math/stat/descriptive/StatisticalMultivariateSummary.java index 48af35f8c..22b733040 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/StatisticalMultivariateSummary.java +++ b/src/java/org/apache/commons/math/stat/descriptive/StatisticalMultivariateSummary.java @@ -22,7 +22,7 @@ import org.apache.commons.math.linear.RealMatrix; * Reporting interface for basic multivariate statistics. * * @since 1.2 - * @version $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer., 29 nov. 2006) $ + * @version $Revision$ $Date$ */ public interface StatisticalMultivariateSummary { /** diff --git a/src/java/org/apache/commons/math/stat/descriptive/StorelessUnivariateStatistic.java b/src/java/org/apache/commons/math/stat/descriptive/StorelessUnivariateStatistic.java index 49899b661..d4ee0d376 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/StorelessUnivariateStatistic.java +++ b/src/java/org/apache/commons/math/stat/descriptive/StorelessUnivariateStatistic.java @@ -38,7 +38,7 @@ public interface StorelessUnivariateStatistic extends UnivariateStatistic { /** * Updates the internal state of the statistic to reflect addition of * all values in the values array. Does not clear the statistic first -- - * i.e., the values are added incrementally to the dataset. + * i.e., the values are added incrementally to the dataset. * * @param values array holding the new values to add * @throws IllegalArgumentException if the array is null @@ -49,7 +49,7 @@ public interface StorelessUnivariateStatistic extends UnivariateStatistic { * Updates the internal state of the statistic to reflect addition of * the values in the designated portion of the values array. Does not * clear the statistic first -- i.e., the values are added - * incrementally to the dataset. + * incrementally to the dataset. * * @param values array holding the new values to add * @param start the array index of the first value to add @@ -75,5 +75,12 @@ public interface StorelessUnivariateStatistic extends UnivariateStatistic { * Clears the internal state of the Statistic */ void clear(); + + /** + * Returns a copy of the statistic with the same internal state. + * + * @return a copy of the statistic + */ + StorelessUnivariateStatistic copy(); } \ No newline at end of file diff --git a/src/java/org/apache/commons/math/stat/descriptive/SummaryStatistics.java b/src/java/org/apache/commons/math/stat/descriptive/SummaryStatistics.java index fbbb1522b..edfc52e60 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/SummaryStatistics.java +++ b/src/java/org/apache/commons/math/stat/descriptive/SummaryStatistics.java @@ -1,21 +1,24 @@ /* * 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. + * 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.stat.descriptive; import java.io.Serializable; -import org.apache.commons.discovery.tools.DiscoverClass; +import org.apache.commons.math.MathRuntimeException; import org.apache.commons.math.stat.descriptive.moment.GeometricMean; import org.apache.commons.math.stat.descriptive.moment.Mean; import org.apache.commons.math.stat.descriptive.moment.SecondMoment; @@ -49,43 +52,12 @@ import org.apache.commons.math.util.MathUtils; * {@link SynchronizedSummaryStatistics} if concurrent access from multiple * threads is required. *

- * @version $Revision$ $Date: 2008-02-10 13:28:59 -0600 (Sun, 10 Feb - * 2008) $ + * @version $Revision$ $Date$ */ public class SummaryStatistics implements StatisticalSummary, Serializable { /** Serialization UID */ - private static final long serialVersionUID = -3346512372447011854L; - - /** - * Create an instance of a SummaryStatistics - * @param cls the type of SummaryStatistics object to create. - * @return a new instance. - * @deprecated to be removed in commons-math 2.0 - * @throws InstantiationException is thrown if the object can not be - * created. - * @throws IllegalAccessException is thrown if the type's default - * constructor is not accessible. - */ - public static SummaryStatistics newInstance(Class cls) throws InstantiationException, IllegalAccessException { - return (SummaryStatistics)cls.newInstance(); - } - - /** - * Create an instance of a SummaryStatistics - * @return a new SummaryStatistics instance. - * @deprecated to be removed in commons-math 2.0 - */ - public static SummaryStatistics newInstance() { - SummaryStatistics instance = null; - try { - DiscoverClass dc = new DiscoverClass(); - instance = (SummaryStatistics)dc.newInstance(SummaryStatistics.class, "org.apache.commons.math.stat.descriptive.SummaryStatisticsImpl"); - } catch (Throwable t) { - return new SummaryStatisticsImpl(); - } - return instance; - } + private static final long serialVersionUID = -2021321786743555871L; /** * Construct a SummaryStatistics instance @@ -93,6 +65,15 @@ public class SummaryStatistics implements StatisticalSummary, Serializable { public SummaryStatistics() { } + /** + * A copy constructor. Creates a deep-copy of the {@code original}. + * + * @param original the {@code SummaryStatistics} instance to copy + */ + public SummaryStatistics(SummaryStatistics original) { + copy(original, this); + } + /** count of values that have been added */ protected long n = 0; @@ -153,7 +134,8 @@ public class SummaryStatistics implements StatisticalSummary, Serializable { * @return Current values of statistics */ public StatisticalSummary getSummary() { - return new StatisticalSummaryValues(getMean(), getVariance(), getN(), getMax(), getMin(), getSum()); + return new StatisticalSummaryValues(getMean(), getVariance(), getN(), + getMax(), getMin(), getSum()); } /** @@ -361,9 +343,14 @@ public class SummaryStatistics implements StatisticalSummary, Serializable { return false; } SummaryStatistics stat = (SummaryStatistics)object; - return (MathUtils.equals(stat.getGeometricMean(), this.getGeometricMean()) && MathUtils.equals(stat.getMax(), this.getMax()) - && MathUtils.equals(stat.getMean(), this.getMean()) && MathUtils.equals(stat.getMin(), this.getMin()) && MathUtils.equals(stat.getN(), this.getN()) - && MathUtils.equals(stat.getSum(), this.getSum()) && MathUtils.equals(stat.getSumsq(), this.getSumsq()) && MathUtils.equals(stat.getVariance(), + return (MathUtils.equals(stat.getGeometricMean(), this.getGeometricMean()) && + MathUtils.equals(stat.getMax(), this.getMax()) && + MathUtils.equals(stat.getMean(), this.getMean()) && + MathUtils.equals(stat.getMin(), this.getMin()) && + MathUtils.equals(stat.getN(), this.getN()) && + MathUtils.equals(stat.getSum(), this.getSum()) && + MathUtils.equals(stat.getSumsq(), this.getSumsq()) && + MathUtils.equals(stat.getVariance(), this.getVariance())); } @@ -623,8 +610,88 @@ public class SummaryStatistics implements StatisticalSummary, Serializable { */ private void checkEmpty() { if (n > 0) { - throw new IllegalStateException("Implementations must be configured before values are added."); + throw MathRuntimeException.createIllegalStateException("{0} values have been added before statistic is configured", + new Object[] { n }); } } - + + /** + * Returns a copy of this SummaryStatistics instance with the same internal state. + * + * @return a copy of this + */ + public SummaryStatistics copy() { + SummaryStatistics result = new SummaryStatistics(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source SummaryStatistics to copy + * @param dest SummaryStatistics to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(SummaryStatistics source, SummaryStatistics dest) { + dest.maxImpl = source.maxImpl.copy(); + dest.meanImpl = source.meanImpl.copy(); + dest.minImpl = source.minImpl.copy(); + dest.sumImpl = source.sumImpl.copy(); + dest.varianceImpl = source.varianceImpl.copy(); + dest.sumLogImpl = source.sumLogImpl.copy(); + dest.sumsqImpl = source.sumsqImpl.copy(); + if (source.getGeoMeanImpl() instanceof GeometricMean) { + // Keep geoMeanImpl, sumLogImpl in synch + dest.geoMeanImpl = new GeometricMean((SumOfLogs) dest.sumLogImpl); + } else { + dest.geoMeanImpl = source.geoMeanImpl.copy(); + } + SecondMoment.copy(source.secondMoment, dest.secondMoment); + dest.n = source.n; + + // Make sure that if stat == statImpl in source, same + // holds in dest; otherwise copy stat + if (source.geoMean == source.geoMeanImpl) { + dest.geoMean = (GeometricMean) dest.geoMeanImpl; + } else { + GeometricMean.copy(source.geoMean, dest.geoMean); + } + if (source.max == source.maxImpl) { + dest.max = (Max) dest.maxImpl; + } else { + Max.copy(source.max, dest.max); + } + if (source.mean == source.meanImpl) { + dest.mean = (Mean) dest.meanImpl; + } else { + Mean.copy(source.mean, dest.mean); + } + if (source.min == source.minImpl) { + dest.min = (Min) dest.minImpl; + } else { + Min.copy(source.min, dest.min); + } + if (source.sum == source.sumImpl) { + dest.sum = (Sum) dest.sumImpl; + } else { + Sum.copy(source.sum, dest.sum); + } + if (source.variance == source.varianceImpl) { + dest.variance = (Variance) dest.varianceImpl; + } else { + Variance.copy(source.variance, dest.variance); + } + if (source.sumLog == source.sumLogImpl) { + dest.sumLog = (SumOfLogs) dest.sumLogImpl; + } else { + SumOfLogs.copy(source.sumLog, dest.sumLog); + } + if (source.sumsq == source.sumsqImpl) { + dest.sumsq = (SumOfSquares) dest.sumsqImpl; + } else { + SumOfSquares.copy(source.sumsq, dest.sumsq); + } + } } diff --git a/src/java/org/apache/commons/math/stat/descriptive/SynchronizedDescriptiveStatistics.java b/src/java/org/apache/commons/math/stat/descriptive/SynchronizedDescriptiveStatistics.java index 6817d63a0..f01b331ed 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/SynchronizedDescriptiveStatistics.java +++ b/src/java/org/apache/commons/math/stat/descriptive/SynchronizedDescriptiveStatistics.java @@ -48,9 +48,18 @@ public class SynchronizedDescriptiveStatistics extends DescriptiveStatistics { public SynchronizedDescriptiveStatistics(int window) { super(window); } + + /** + * A copy constructor. Creates a deep-copy of the {@code original}. + * + * @param original the {@code SynchronizedDescriptiveStatistics} instance to copy + */ + public SynchronizedDescriptiveStatistics(SynchronizedDescriptiveStatistics original) { + copy(original, this); + } /** - * @see org.apache.commons.math.stat.descriptive.DescriptiveStatistics#addValue(double) + * {@inheritDoc} */ public synchronized void addValue(double v) { super.addValue(v); @@ -66,21 +75,21 @@ public class SynchronizedDescriptiveStatistics extends DescriptiveStatistics { } /** - * @see org.apache.commons.math.stat.descriptive.DescriptiveStatistics#clear() + * {@inheritDoc} */ public synchronized void clear() { super.clear(); } /** - * @see org.apache.commons.math.stat.descriptive.DescriptiveStatistics#getElement(int) + * {@inheritDoc} */ public synchronized double getElement(int index) { return super.getElement(index); } /** - * @see org.apache.commons.math.stat.descriptive.DescriptiveStatistics#getN() + * {@inheritDoc} */ public synchronized long getN() { return super.getN(); @@ -96,7 +105,7 @@ public class SynchronizedDescriptiveStatistics extends DescriptiveStatistics { } /** - * @see org.apache.commons.math.stat.descriptive.DescriptiveStatistics#getValues() + * {@inheritDoc} */ public synchronized double[] getValues() { return super.getValues(); @@ -111,7 +120,7 @@ public class SynchronizedDescriptiveStatistics extends DescriptiveStatistics { } /** - * @see org.apache.commons.math.stat.descriptive.DescriptiveStatistics#setWindowSize(int) + * {@inheritDoc} */ public synchronized void setWindowSize(int windowSize) { super.setWindowSize(windowSize); @@ -127,4 +136,35 @@ public class SynchronizedDescriptiveStatistics extends DescriptiveStatistics { public synchronized String toString() { return super.toString(); } + + /** + * Returns a copy of this SynchronizedDescriptiveStatistics instance with the + * same internal state. + * + * @return a copy of this + */ + public synchronized SynchronizedDescriptiveStatistics copy() { + SynchronizedDescriptiveStatistics result = + new SynchronizedDescriptiveStatistics(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ *

Acquires synchronization lock on source, then dest before copying.

+ * + * @param source SynchronizedDescriptiveStatistics to copy + * @param dest SynchronizedDescriptiveStatistics to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(SynchronizedDescriptiveStatistics source, + SynchronizedDescriptiveStatistics dest) { + synchronized (source) { + synchronized (dest) { + DescriptiveStatistics.copy(source, dest); + } + } + } } diff --git a/src/java/org/apache/commons/math/stat/descriptive/SynchronizedMultivariateSummaryStatistics.java b/src/java/org/apache/commons/math/stat/descriptive/SynchronizedMultivariateSummaryStatistics.java index 2ede5bc90..deb267cf6 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/SynchronizedMultivariateSummaryStatistics.java +++ b/src/java/org/apache/commons/math/stat/descriptive/SynchronizedMultivariateSummaryStatistics.java @@ -29,7 +29,7 @@ import org.apache.commons.math.linear.RealMatrix; * thread is computing a statistic from the instance, no other thread can modify * the instance nor compute another statistic. * @since 1.2 - * @version $Revision: 618097 $ $Date: 2008-02-03 22:39:08 +0100 (dim., 03 févr. 2008) $ + * @version $Revision$ $Date$ */ public class SynchronizedMultivariateSummaryStatistics extends MultivariateSummaryStatistics { @@ -49,7 +49,7 @@ public class SynchronizedMultivariateSummaryStatistics } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#addValue(double[]) + * {@inheritDoc} */ public synchronized void addValue(double[] value) throws DimensionMismatchException { @@ -57,119 +57,119 @@ public class SynchronizedMultivariateSummaryStatistics } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getDimension() + * {@inheritDoc} */ public synchronized int getDimension() { return super.getDimension(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getN() + * {@inheritDoc} */ public synchronized long getN() { return super.getN(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getSum() + * {@inheritDoc} */ public synchronized double[] getSum() { return super.getSum(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getSumSq() + * {@inheritDoc} */ public synchronized double[] getSumSq() { return super.getSumSq(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getSumLog() + * {@inheritDoc} */ public synchronized double[] getSumLog() { return super.getSumLog(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getMean() + * {@inheritDoc} */ public synchronized double[] getMean() { return super.getMean(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getStandardDeviation() + * {@inheritDoc} */ public synchronized double[] getStandardDeviation() { return super.getStandardDeviation(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getCovariance() + * {@inheritDoc} */ public synchronized RealMatrix getCovariance() { return super.getCovariance(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getMax() + * {@inheritDoc} */ public synchronized double[] getMax() { return super.getMax(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getMin() + * {@inheritDoc} */ public synchronized double[] getMin() { return super.getMin(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getGeometricMean() + * {@inheritDoc} */ public synchronized double[] getGeometricMean() { return super.getGeometricMean(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#toString() + * {@inheritDoc} */ public synchronized String toString() { return super.toString(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#clear() + * {@inheritDoc} */ public synchronized void clear() { super.clear(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#equals(Object) + * {@inheritDoc} */ public synchronized boolean equals(Object object) { return super.equals(object); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#hashCode() + * {@inheritDoc} */ public synchronized int hashCode() { return super.hashCode(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getSumImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic[] getSumImpl() { return super.getSumImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#setSumImpl(StorelessUnivariateStatistic[]) + * {@inheritDoc} */ public synchronized void setSumImpl(StorelessUnivariateStatistic[] sumImpl) throws DimensionMismatchException { @@ -177,14 +177,14 @@ public class SynchronizedMultivariateSummaryStatistics } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getSumsqImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic[] getSumsqImpl() { return super.getSumsqImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#setSumsqImpl(StorelessUnivariateStatistic[]) + * {@inheritDoc} */ public synchronized void setSumsqImpl(StorelessUnivariateStatistic[] sumsqImpl) throws DimensionMismatchException { @@ -192,14 +192,14 @@ public class SynchronizedMultivariateSummaryStatistics } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getMinImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic[] getMinImpl() { return super.getMinImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#setMinImpl(StorelessUnivariateStatistic[]) + * {@inheritDoc} */ public synchronized void setMinImpl(StorelessUnivariateStatistic[] minImpl) throws DimensionMismatchException { @@ -207,14 +207,14 @@ public class SynchronizedMultivariateSummaryStatistics } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getMaxImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic[] getMaxImpl() { return super.getMaxImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#setMaxImpl(StorelessUnivariateStatistic[]) + * {@inheritDoc} */ public synchronized void setMaxImpl(StorelessUnivariateStatistic[] maxImpl) throws DimensionMismatchException { @@ -222,14 +222,14 @@ public class SynchronizedMultivariateSummaryStatistics } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getSumLogImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic[] getSumLogImpl() { return super.getSumLogImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#setSumLogImpl(StorelessUnivariateStatistic[]) + * {@inheritDoc} */ public synchronized void setSumLogImpl(StorelessUnivariateStatistic[] sumLogImpl) throws DimensionMismatchException { @@ -237,14 +237,14 @@ public class SynchronizedMultivariateSummaryStatistics } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getGeoMeanImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic[] getGeoMeanImpl() { return super.getGeoMeanImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#setGeoMeanImpl(StorelessUnivariateStatistic[]) + * {@inheritDoc} */ public synchronized void setGeoMeanImpl(StorelessUnivariateStatistic[] geoMeanImpl) throws DimensionMismatchException { @@ -252,14 +252,14 @@ public class SynchronizedMultivariateSummaryStatistics } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#getMeanImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic[] getMeanImpl() { return super.getMeanImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics#setMeanImpl(StorelessUnivariateStatistic[]) + * {@inheritDoc} */ public synchronized void setMeanImpl(StorelessUnivariateStatistic[] meanImpl) throws DimensionMismatchException { diff --git a/src/java/org/apache/commons/math/stat/descriptive/SynchronizedSummaryStatistics.java b/src/java/org/apache/commons/math/stat/descriptive/SynchronizedSummaryStatistics.java index 31ea3a9c8..3b5f540f0 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/SynchronizedSummaryStatistics.java +++ b/src/java/org/apache/commons/math/stat/descriptive/SynchronizedSummaryStatistics.java @@ -40,222 +40,262 @@ public class SynchronizedSummaryStatistics extends SummaryStatistics { public SynchronizedSummaryStatistics() { super(); } + + /** + * A copy constructor. Creates a deep-copy of the {@code original}. + * + * @param original the {@code SynchronizedSummaryStatistics} instance to copy + */ + public SynchronizedSummaryStatistics(SynchronizedSummaryStatistics original) { + copy(original, this); + } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getSummary() + * {@inheritDoc} */ public synchronized StatisticalSummary getSummary() { return super.getSummary(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#addValue(double) + * {@inheritDoc} */ public synchronized void addValue(double value) { super.addValue(value); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getN() + * {@inheritDoc} */ public synchronized long getN() { return super.getN(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getSum() + * {@inheritDoc} */ public synchronized double getSum() { return super.getSum(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getSumsq() + * {@inheritDoc} */ public synchronized double getSumsq() { return super.getSumsq(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getMean() + * {@inheritDoc} */ public synchronized double getMean() { return super.getMean(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getStandardDeviation() + * {@inheritDoc} */ public synchronized double getStandardDeviation() { return super.getStandardDeviation(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getVariance() + * {@inheritDoc} */ public synchronized double getVariance() { return super.getVariance(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getMax() + * {@inheritDoc} */ public synchronized double getMax() { return super.getMax(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getMin() + * {@inheritDoc} */ public synchronized double getMin() { return super.getMin(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getGeometricMean() + * {@inheritDoc} */ public synchronized double getGeometricMean() { return super.getGeometricMean(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#toString() + * {@inheritDoc} */ public synchronized String toString() { return super.toString(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#clear() + * {@inheritDoc} */ public synchronized void clear() { super.clear(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#equals(Object) + * {@inheritDoc} */ public synchronized boolean equals(Object object) { return super.equals(object); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#hashCode() + * {@inheritDoc} */ public synchronized int hashCode() { return super.hashCode(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getSumImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic getSumImpl() { return super.getSumImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#setSumImpl(StorelessUnivariateStatistic) + * {@inheritDoc} */ public synchronized void setSumImpl(StorelessUnivariateStatistic sumImpl) { super.setSumImpl(sumImpl); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getSumsqImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic getSumsqImpl() { return super.getSumsqImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#setSumsqImpl(StorelessUnivariateStatistic) + * {@inheritDoc} */ public synchronized void setSumsqImpl(StorelessUnivariateStatistic sumsqImpl) { super.setSumsqImpl(sumsqImpl); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getMinImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic getMinImpl() { return super.getMinImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#setMinImpl(StorelessUnivariateStatistic) + * {@inheritDoc} */ public synchronized void setMinImpl(StorelessUnivariateStatistic minImpl) { super.setMinImpl(minImpl); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getMaxImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic getMaxImpl() { return super.getMaxImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#setMaxImpl(StorelessUnivariateStatistic) + * {@inheritDoc} */ public synchronized void setMaxImpl(StorelessUnivariateStatistic maxImpl) { super.setMaxImpl(maxImpl); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getSumLogImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic getSumLogImpl() { return super.getSumLogImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#setSumLogImpl(StorelessUnivariateStatistic) + * {@inheritDoc} */ public synchronized void setSumLogImpl(StorelessUnivariateStatistic sumLogImpl) { super.setSumLogImpl(sumLogImpl); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getGeoMeanImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic getGeoMeanImpl() { return super.getGeoMeanImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#setGeoMeanImpl(StorelessUnivariateStatistic) + * {@inheritDoc} */ public synchronized void setGeoMeanImpl(StorelessUnivariateStatistic geoMeanImpl) { super.setGeoMeanImpl(geoMeanImpl); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getMeanImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic getMeanImpl() { return super.getMeanImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#setMeanImpl(StorelessUnivariateStatistic) + * {@inheritDoc} */ public synchronized void setMeanImpl(StorelessUnivariateStatistic meanImpl) { super.setMeanImpl(meanImpl); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#getVarianceImpl() + * {@inheritDoc} */ public synchronized StorelessUnivariateStatistic getVarianceImpl() { return super.getVarianceImpl(); } /** - * @see org.apache.commons.math.stat.descriptive.SummaryStatistics#setVarianceImpl(StorelessUnivariateStatistic) + * {@inheritDoc} */ public synchronized void setVarianceImpl(StorelessUnivariateStatistic varianceImpl) { super.setVarianceImpl(varianceImpl); } + /** + * Returns a copy of this SynchronizedSummaryStatistics instance with the + * same internal state. + * + * @return a copy of this + */ + public synchronized SynchronizedSummaryStatistics copy() { + SynchronizedSummaryStatistics result = + new SynchronizedSummaryStatistics(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ *

Acquires synchronization lock on source, then dest before copying.

+ * + * @param source SynchronizedSummaryStatistics to copy + * @param dest SynchronizedSummaryStatistics to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(SynchronizedSummaryStatistics source, + SynchronizedSummaryStatistics dest) { + synchronized (source) { + synchronized (dest) { + SummaryStatistics.copy(source, dest); + } + } + } + } diff --git a/src/java/org/apache/commons/math/stat/descriptive/UnivariateStatistic.java b/src/java/org/apache/commons/math/stat/descriptive/UnivariateStatistic.java index 817497097..fdb1ab8f1 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/UnivariateStatistic.java +++ b/src/java/org/apache/commons/math/stat/descriptive/UnivariateStatistic.java @@ -19,11 +19,7 @@ package org.apache.commons.math.stat.descriptive; import java.io.Serializable; /** - * Base evaluation interface implemented by all statistics. - *

- * Includes "stateless" evaluate methods that take - * double[] arrays as input and return the value of the statistic - * applied to the input values.

+ * Base interface implemented by all statistics. * * @version $Revision$ $Date$ */ @@ -47,5 +43,12 @@ public interface UnivariateStatistic extends Serializable { * @return the value of the statistic applied to the included array entries */ double evaluate(double[] values, int begin, int length); + + /** + * Returns a copy of the statistic with the same internal state. + * + * @return a copy of the statistic + */ + UnivariateStatistic copy(); -} \ No newline at end of file +} diff --git a/src/java/org/apache/commons/math/stat/descriptive/moment/FirstMoment.java b/src/java/org/apache/commons/math/stat/descriptive/moment/FirstMoment.java index 5b09ba515..e45fb6a03 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/moment/FirstMoment.java +++ b/src/java/org/apache/commons/math/stat/descriptive/moment/FirstMoment.java @@ -17,7 +17,6 @@ package org.apache.commons.math.stat.descriptive.moment; import java.io.Serializable; - import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic; /** @@ -81,7 +80,18 @@ public class FirstMoment extends AbstractStorelessUnivariateStatistic } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * Copy constructor, creates a new {@code FirstMoment} identical + * to the {@code original} + * + * @param original the {@code FirstMoment} instance to copy + */ + public FirstMoment(FirstMoment original) { + super(); + copy(original, this); + } + + /** + * {@inheritDoc} */ public void increment(final double d) { if (n == 0) { @@ -95,7 +105,7 @@ public class FirstMoment extends AbstractStorelessUnivariateStatistic } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { m1 = Double.NaN; @@ -105,16 +115,40 @@ public class FirstMoment extends AbstractStorelessUnivariateStatistic } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { return m1; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return n; } -} \ No newline at end of file + + /** + * {@inheritDoc} + */ + public FirstMoment copy() { + FirstMoment result = new FirstMoment(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source FirstMoment to copy + * @param dest FirstMoment to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(FirstMoment source, FirstMoment dest) { + dest.n = source.n; + dest.m1 = source.m1; + dest.dev = source.dev; + dest.nDev = dest.nDev; + } +} diff --git a/src/java/org/apache/commons/math/stat/descriptive/moment/FourthMoment.java b/src/java/org/apache/commons/math/stat/descriptive/moment/FourthMoment.java index a7c218414..985992a25 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/moment/FourthMoment.java +++ b/src/java/org/apache/commons/math/stat/descriptive/moment/FourthMoment.java @@ -67,7 +67,18 @@ public class FourthMoment extends ThirdMoment implements Serializable{ } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * Copy constructor, creates a new {@code FourthMoment} identical + * to the {@code original} + * + * @param original the {@code FourthMoment} instance to copy + */ + public FourthMoment(FourthMoment original) { + super(); + copy(original, this); + } + + /** + * {@inheritDoc} */ public void increment(final double d) { if (n < 1) { @@ -89,18 +100,39 @@ public class FourthMoment extends ThirdMoment implements Serializable{ } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { return m4; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { super.clear(); m4 = Double.NaN; } - + + /** + * {@inheritDoc} + */ + public FourthMoment copy() { + FourthMoment result = new FourthMoment(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source FourthMoment to copy + * @param dest FourthMoment to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(FourthMoment source, FourthMoment dest) { + ThirdMoment.copy(source, dest); + dest.m4 = source.m4; + } } diff --git a/src/java/org/apache/commons/math/stat/descriptive/moment/GeometricMean.java b/src/java/org/apache/commons/math/stat/descriptive/moment/GeometricMean.java index 4b5417ded..e352766fe 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/moment/GeometricMean.java +++ b/src/java/org/apache/commons/math/stat/descriptive/moment/GeometricMean.java @@ -16,6 +16,7 @@ */ package org.apache.commons.math.stat.descriptive.moment; +import org.apache.commons.math.MathRuntimeException; import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic; import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic; import org.apache.commons.math.stat.descriptive.summary.SumOfLogs; @@ -59,22 +60,43 @@ public class GeometricMean extends AbstractStorelessUnivariateStatistic { sumOfLogs = new SumOfLogs(); } + /** + * Copy constructor, creates a new {@code GeometricMean} identical + * to the {@code original} + * + * @param original the {@code GeometricMean} instance to copy + */ + public GeometricMean(GeometricMean original) { + super(); + copy(original, this); + } + + /** + * {@inheritDoc} + */ + public GeometricMean copy() { + GeometricMean result = new GeometricMean(); + copy(this, result); + return result; + } + /** * Create a GeometricMean instance using the given SumOfLogs instance + * @param sumOfLogs sum of logs instance to use for computation */ public GeometricMean(SumOfLogs sumOfLogs) { this.sumOfLogs = sumOfLogs; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * {@inheritDoc} */ public void increment(final double d) { sumOfLogs.increment(d); } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { if (sumOfLogs.getN() > 0) { @@ -85,7 +107,7 @@ public class GeometricMean extends AbstractStorelessUnivariateStatistic { } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { sumOfLogs.clear(); @@ -114,7 +136,7 @@ public class GeometricMean extends AbstractStorelessUnivariateStatistic { } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return sumOfLogs.getN(); @@ -146,13 +168,26 @@ public class GeometricMean extends AbstractStorelessUnivariateStatistic { return sumOfLogs; } + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source GeometricMean to copy + * @param dest GeometricMean to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(GeometricMean source, GeometricMean dest) { + dest.sumOfLogs = (SumOfLogs) source.sumOfLogs.copy(); + } + + /** * Throws IllegalStateException if n > 0. */ private void checkEmpty() { if (getN() > 0) { - throw new IllegalStateException( - "Implementation must be configured before values are added."); + throw MathRuntimeException.createIllegalStateException("{0} values have been added before statistic is configured", + new Object[] { getN() }); } } diff --git a/src/java/org/apache/commons/math/stat/descriptive/moment/Kurtosis.java b/src/java/org/apache/commons/math/stat/descriptive/moment/Kurtosis.java index c35c2f0af..0ac8488c1 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/moment/Kurtosis.java +++ b/src/java/org/apache/commons/math/stat/descriptive/moment/Kurtosis.java @@ -16,8 +16,10 @@ */ package org.apache.commons.math.stat.descriptive.moment; +import org.apache.commons.math.MathRuntimeException; import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic; + /** * Computes the Kurtosis of the available values. *

@@ -71,21 +73,32 @@ public class Kurtosis extends AbstractStorelessUnivariateStatistic { incMoment = false; this.moment = m4; } + + /** + * Copy constructor, creates a new {@code Kurtosis} identical + * to the {@code original} + * + * @param original the {@code Kurtosis} instance to copy + */ + public Kurtosis(Kurtosis original) { + copy(original, this); + } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * {@inheritDoc} */ public void increment(final double d) { if (incMoment) { moment.increment(d); } else { - throw new IllegalStateException - ("Statistics constructed from external moments cannot be incremented"); + throw MathRuntimeException.createIllegalStateException("statistics constructed from external " + + "moments cannot be incremented", + null); } } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { double kurtosis = Double.NaN; @@ -105,19 +118,20 @@ public class Kurtosis extends AbstractStorelessUnivariateStatistic { } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { if (incMoment) { moment.clear(); } else { - throw new IllegalStateException - ("Statistics constructed from external moments cannot be cleared"); + throw MathRuntimeException.createIllegalStateException("statistics constructed from external " + + "moments cannot be cleared", + null); } } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return moment.getN(); @@ -174,5 +188,27 @@ public class Kurtosis extends AbstractStorelessUnivariateStatistic { } return kurt; } + + /** + * {@inheritDoc} + */ + public Kurtosis copy() { + Kurtosis result = new Kurtosis(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source Kurtosis to copy + * @param dest Kurtosis to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(Kurtosis source, Kurtosis dest) { + dest.moment = (FourthMoment) source.moment.copy(); + dest.incMoment = source.incMoment; + } } diff --git a/src/java/org/apache/commons/math/stat/descriptive/moment/Mean.java b/src/java/org/apache/commons/math/stat/descriptive/moment/Mean.java index a06b381df..385e2615e 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/moment/Mean.java +++ b/src/java/org/apache/commons/math/stat/descriptive/moment/Mean.java @@ -87,9 +87,19 @@ public class Mean extends AbstractStorelessUnivariateStatistic this.moment = m1; incMoment = false; } + + /** + * Copy constructor, creates a new {@code Mean} identical + * to the {@code original} + * + * @param original the {@code Mean} instance to copy + */ + public Mean(Mean original) { + copy(original, this); + } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * {@inheritDoc} */ public void increment(final double d) { if (incMoment) { @@ -98,7 +108,7 @@ public class Mean extends AbstractStorelessUnivariateStatistic } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { if (incMoment) { @@ -107,14 +117,14 @@ public class Mean extends AbstractStorelessUnivariateStatistic } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { return moment.m1; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return moment.getN(); @@ -153,4 +163,27 @@ public class Mean extends AbstractStorelessUnivariateStatistic } return Double.NaN; } -} \ No newline at end of file + + /* + * {@inheritDoc} + */ + public Mean copy() { + Mean result = new Mean(); + copy(this, result); + return result; + } + + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source Mean to copy + * @param dest Mean to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(Mean source, Mean dest) { + dest.incMoment = source.incMoment; + dest.moment = (FirstMoment) source.moment.copy(); + } +} diff --git a/src/java/org/apache/commons/math/stat/descriptive/moment/SecondMoment.java b/src/java/org/apache/commons/math/stat/descriptive/moment/SecondMoment.java index 77b135a8e..61267cfca 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/moment/SecondMoment.java +++ b/src/java/org/apache/commons/math/stat/descriptive/moment/SecondMoment.java @@ -59,7 +59,18 @@ public class SecondMoment extends FirstMoment implements Serializable { } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * Copy constructor, creates a new {@code SecondMoment} identical + * to the {@code original} + * + * @param original the {@code SecondMoment} instance to copy + */ + public SecondMoment(SecondMoment original) { + super(original); + this.m2 = original.m2; + } + + /** + * {@inheritDoc} */ public void increment(final double d) { if (n < 1) { @@ -70,7 +81,7 @@ public class SecondMoment extends FirstMoment implements Serializable { } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { super.clear(); @@ -78,10 +89,32 @@ public class SecondMoment extends FirstMoment implements Serializable { } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { return m2; } + + /** + * {@inheritDoc} + */ + public SecondMoment copy() { + SecondMoment result = new SecondMoment(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source SecondMoment to copy + * @param dest SecondMoment to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(SecondMoment source, SecondMoment dest) { + FirstMoment.copy(source, dest); + dest.m2 = source.m2; + } } diff --git a/src/java/org/apache/commons/math/stat/descriptive/moment/Skewness.java b/src/java/org/apache/commons/math/stat/descriptive/moment/Skewness.java index 971d990af..935e70cf9 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/moment/Skewness.java +++ b/src/java/org/apache/commons/math/stat/descriptive/moment/Skewness.java @@ -69,9 +69,19 @@ public class Skewness extends AbstractStorelessUnivariateStatistic implements Se incMoment = false; this.moment = m3; } + + /** + * Copy constructor, creates a new {@code Skewness} identical + * to the {@code original} + * + * @param original the {@code Skewness} instance to copy + */ + public Skewness(Skewness original) { + copy(original, this); + } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * {@inheritDoc} */ public void increment(final double d) { if (incMoment) { @@ -102,14 +112,14 @@ public class Skewness extends AbstractStorelessUnivariateStatistic implements Se } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return moment.getN(); } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { if (incMoment) { @@ -170,4 +180,26 @@ public class Skewness extends AbstractStorelessUnivariateStatistic implements Se } return skew; } + + /** + * {@inheritDoc} + */ + public Skewness copy() { + Skewness result = new Skewness(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source Skewness to copy + * @param dest Skewness to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(Skewness source, Skewness dest) { + dest.moment = new ThirdMoment((ThirdMoment) source.moment.copy()); + dest.incMoment = source.incMoment; + } } diff --git a/src/java/org/apache/commons/math/stat/descriptive/moment/StandardDeviation.java b/src/java/org/apache/commons/math/stat/descriptive/moment/StandardDeviation.java index f3b828f35..d78b965c2 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/moment/StandardDeviation.java +++ b/src/java/org/apache/commons/math/stat/descriptive/moment/StandardDeviation.java @@ -63,6 +63,16 @@ public class StandardDeviation extends AbstractStorelessUnivariateStatistic variance = new Variance(m2); } + /** + * Copy constructor, creates a new {@code StandardDeviation} identical + * to the {@code original} + * + * @param original the {@code StandardDeviation} instance to copy + */ + public StandardDeviation(StandardDeviation original) { + copy(original, this); + } + /** * Contructs a StandardDeviation with the specified value for the * isBiasCorrected property. If this property is set to @@ -93,28 +103,28 @@ public class StandardDeviation extends AbstractStorelessUnivariateStatistic } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * {@inheritDoc} */ public void increment(final double d) { variance.increment(d); } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return variance.getN(); } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { return Math.sqrt(variance.getResult()); } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { variance.clear(); @@ -227,4 +237,27 @@ public class StandardDeviation extends AbstractStorelessUnivariateStatistic public void setBiasCorrected(boolean isBiasCorrected) { variance.setBiasCorrected(isBiasCorrected); } -} \ No newline at end of file + + /** + * {@inheritDoc} + */ + public StandardDeviation copy() { + StandardDeviation result = new StandardDeviation(); + copy(this, result); + return result; + } + + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source StandardDeviation to copy + * @param dest StandardDeviation to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(StandardDeviation source, StandardDeviation dest) { + dest.variance = (Variance) source.variance.copy(); + } + +} diff --git a/src/java/org/apache/commons/math/stat/descriptive/moment/ThirdMoment.java b/src/java/org/apache/commons/math/stat/descriptive/moment/ThirdMoment.java index ee268cf16..5c0b6108e 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/moment/ThirdMoment.java +++ b/src/java/org/apache/commons/math/stat/descriptive/moment/ThirdMoment.java @@ -18,6 +18,7 @@ package org.apache.commons.math.stat.descriptive.moment; import java.io.Serializable; + /** * Computes a statistic related to the Third Central Moment. Specifically, * what is computed is the sum of cubed deviations from the sample mean. @@ -66,9 +67,19 @@ public class ThirdMoment extends SecondMoment implements Serializable { m3 = Double.NaN; nDevSq = Double.NaN; } + + /** + * Copy constructor, creates a new {@code ThirdMoment} identical + * to the {@code original} + * + * @param original the {@code ThirdMoment} instance to copy + */ + public ThirdMoment(ThirdMoment original) { + copy(original, this); + } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * {@inheritDoc} */ public void increment(final double d) { if (n < 1) { @@ -83,19 +94,42 @@ public class ThirdMoment extends SecondMoment implements Serializable { } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { return m3; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { super.clear(); m3 = Double.NaN; nDevSq = Double.NaN; } + + /** + * {@inheritDoc} + */ + public ThirdMoment copy() { + ThirdMoment result = new ThirdMoment(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source ThirdMoment to copy + * @param dest ThirdMoment to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(ThirdMoment source, ThirdMoment dest) { + SecondMoment.copy(source, dest); + dest.m3 = source.m3; + dest.nDevSq = source.nDevSq; + } -} \ No newline at end of file +} diff --git a/src/java/org/apache/commons/math/stat/descriptive/moment/Variance.java b/src/java/org/apache/commons/math/stat/descriptive/moment/Variance.java index 7fea13faf..8c006f836 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/moment/Variance.java +++ b/src/java/org/apache/commons/math/stat/descriptive/moment/Variance.java @@ -131,6 +131,16 @@ public class Variance extends AbstractStorelessUnivariateStatistic implements Se this.isBiasCorrected = isBiasCorrected; } + /** + * Copy constructor, creates a new {@code Variance} identical + * to the {@code original} + * + * @param original the {@code Variance} instance to copy + */ + public Variance(Variance original) { + copy(original, this); + } + /** * {@inheritDoc} *

If all values are available, it is more accurate to use @@ -147,7 +157,7 @@ public class Variance extends AbstractStorelessUnivariateStatistic implements Se } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { if (moment.n == 0) { @@ -164,14 +174,14 @@ public class Variance extends AbstractStorelessUnivariateStatistic implements Se } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return moment.getN(); } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { if (incMoment) { @@ -335,5 +345,29 @@ public class Variance extends AbstractStorelessUnivariateStatistic implements Se public void setBiasCorrected(boolean isBiasCorrected) { this.isBiasCorrected = isBiasCorrected; } + + /** + * {@inheritDoc} + */ + public Variance copy() { + Variance result = new Variance(); + copy(this, result); + return result; + } + + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source Variance to copy + * @param dest Variance to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(Variance source, Variance dest) { + dest.moment = (SecondMoment) source.moment.copy(); + dest.isBiasCorrected = source.isBiasCorrected; + dest.incMoment = source.incMoment; + } } diff --git a/src/java/org/apache/commons/math/stat/descriptive/moment/VectorialCovariance.java b/src/java/org/apache/commons/math/stat/descriptive/moment/VectorialCovariance.java index 539ab58f2..bdcd89a1b 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/moment/VectorialCovariance.java +++ b/src/java/org/apache/commons/math/stat/descriptive/moment/VectorialCovariance.java @@ -45,7 +45,7 @@ public class VectorialCovariance implements Serializable { /** Number of vectors in the sample. */ private long n; - /** Constructs a VectorialMean. + /** Constructs a VectorialCovariance. * @param dimension vectors dimension * @param isBiasCorrected if true, computed the unbiased sample covariance, * otherwise computes the biased population covariance diff --git a/src/java/org/apache/commons/math/stat/descriptive/rank/Max.java b/src/java/org/apache/commons/math/stat/descriptive/rank/Max.java index fabb73102..bfcf9317c 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/rank/Max.java +++ b/src/java/org/apache/commons/math/stat/descriptive/rank/Max.java @@ -55,7 +55,17 @@ public class Max extends AbstractStorelessUnivariateStatistic { } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * Copy constructor, creates a new {@code Max} identical + * to the {@code original} + * + * @param original the {@code Max} instance to copy + */ + public Max(Max original) { + copy(original, this); + } + + /** + * {@inheritDoc} */ public void increment(final double d) { if (d > value || Double.isNaN(value)) { @@ -65,7 +75,7 @@ public class Max extends AbstractStorelessUnivariateStatistic { } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { value = Double.NaN; @@ -73,14 +83,14 @@ public class Max extends AbstractStorelessUnivariateStatistic { } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { return value; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return n; @@ -120,4 +130,26 @@ public class Max extends AbstractStorelessUnivariateStatistic { } return max; } -} \ No newline at end of file + + /* + * {@inheritDoc} + */ + public Max copy() { + Max result = new Max(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source Max to copy + * @param dest Max to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(Max source, Max dest) { + dest.n = source.n; + dest.value = source.value; + } +} diff --git a/src/java/org/apache/commons/math/stat/descriptive/rank/Median.java b/src/java/org/apache/commons/math/stat/descriptive/rank/Median.java index e16d3ab9b..49f8046f8 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/rank/Median.java +++ b/src/java/org/apache/commons/math/stat/descriptive/rank/Median.java @@ -41,5 +41,15 @@ public class Median extends Percentile implements Serializable { public Median() { super(50.0); } + + /** + * Copy constructor, creates a new {@code Median} identical + * to the {@code original} + * + * @param original the {@code Median} instance to copy + */ + public Median(Median original) { + super(original); + } } \ No newline at end of file diff --git a/src/java/org/apache/commons/math/stat/descriptive/rank/Min.java b/src/java/org/apache/commons/math/stat/descriptive/rank/Min.java index 278510574..c7c777536 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/rank/Min.java +++ b/src/java/org/apache/commons/math/stat/descriptive/rank/Min.java @@ -57,7 +57,17 @@ public class Min extends AbstractStorelessUnivariateStatistic implements Seriali } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * Copy constructor, creates a new {@code Min} identical + * to the {@code original} + * + * @param original the {@code Min} instance to copy + */ + public Min(Min original) { + copy(original, this); + } + + /** + * {@inheritDoc} */ public void increment(final double d) { if (d < value || Double.isNaN(value)) { @@ -67,7 +77,7 @@ public class Min extends AbstractStorelessUnivariateStatistic implements Seriali } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { value = Double.NaN; @@ -75,14 +85,14 @@ public class Min extends AbstractStorelessUnivariateStatistic implements Seriali } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { return value; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return n; @@ -122,4 +132,26 @@ public class Min extends AbstractStorelessUnivariateStatistic implements Seriali } return min; } -} \ No newline at end of file + + /* + * {@inheritDoc} + */ + public Min copy() { + Min result = new Min(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source Min to copy + * @param dest Min to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(Min source, Min dest) { + dest.n = source.n; + dest.value = source.value; + } +} diff --git a/src/java/org/apache/commons/math/stat/descriptive/rank/Percentile.java b/src/java/org/apache/commons/math/stat/descriptive/rank/Percentile.java index 25ec3ee2f..2eed79214 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/rank/Percentile.java +++ b/src/java/org/apache/commons/math/stat/descriptive/rank/Percentile.java @@ -90,6 +90,16 @@ public class Percentile extends AbstractUnivariateStatistic implements Serializa setQuantile(p); } + /** + * Copy constructor, creates a new {@code Percentile} identical + * to the {@code original} + * + * @param original the {@code Percentile} instance to copy + */ + public Percentile(Percentile original) { + copy(original, this); + } + /** * Returns an estimate of the pth percentile of the values * in the values array. @@ -237,5 +247,26 @@ public class Percentile extends AbstractUnivariateStatistic implements Serializa } quantile = p; } + + /* + * {@inheritDoc} + */ + public Percentile copy() { + Percentile result = new Percentile(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source Percentile to copy + * @param dest Percentile to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(Percentile source, Percentile dest) { + dest.quantile = source.quantile; + } -} \ No newline at end of file +} diff --git a/src/java/org/apache/commons/math/stat/descriptive/summary/Product.java b/src/java/org/apache/commons/math/stat/descriptive/summary/Product.java index 62afd27d7..1af3033c4 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/summary/Product.java +++ b/src/java/org/apache/commons/math/stat/descriptive/summary/Product.java @@ -55,7 +55,17 @@ public class Product extends AbstractStorelessUnivariateStatistic implements Ser } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * Copy constructor, creates a new {@code Product} identical + * to the {@code original} + * + * @param original the {@code Product} instance to copy + */ + public Product(Product original) { + copy(original, this); + } + + /** + * {@inheritDoc} */ public void increment(final double d) { if (n == 0) { @@ -67,21 +77,21 @@ public class Product extends AbstractStorelessUnivariateStatistic implements Ser } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { return value; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return n; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { value = Double.NaN; @@ -112,5 +122,27 @@ public class Product extends AbstractStorelessUnivariateStatistic implements Ser } return product; } + + /* + * {@inheritDoc} + */ + public Product copy() { + Product result = new Product(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source Product to copy + * @param dest Product to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(Product source, Product dest) { + dest.n = source.n; + dest.value = source.value; + } -} \ No newline at end of file +} diff --git a/src/java/org/apache/commons/math/stat/descriptive/summary/Sum.java b/src/java/org/apache/commons/math/stat/descriptive/summary/Sum.java index b4ee8726f..48c589bb0 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/summary/Sum.java +++ b/src/java/org/apache/commons/math/stat/descriptive/summary/Sum.java @@ -55,7 +55,17 @@ public class Sum extends AbstractStorelessUnivariateStatistic implements Seriali } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * Copy constructor, creates a new {@code Sum} identical + * to the {@code original} + * + * @param original the {@code Sum} instance to copy + */ + public Sum(Sum original) { + copy(original, this); + } + + /** + * {@inheritDoc} */ public void increment(final double d) { if (n == 0) { @@ -67,21 +77,21 @@ public class Sum extends AbstractStorelessUnivariateStatistic implements Seriali } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { return value; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return n; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { value = Double.NaN; @@ -112,5 +122,27 @@ public class Sum extends AbstractStorelessUnivariateStatistic implements Seriali } return sum; } + + /* + * {@inheritDoc} + */ + public Sum copy() { + Sum result = new Sum(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source Sum to copy + * @param dest Sum to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(Sum source, Sum dest) { + dest.n = source.n; + dest.value = source.value; + } } diff --git a/src/java/org/apache/commons/math/stat/descriptive/summary/SumOfLogs.java b/src/java/org/apache/commons/math/stat/descriptive/summary/SumOfLogs.java index 971f2aae8..4417bd0be 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/summary/SumOfLogs.java +++ b/src/java/org/apache/commons/math/stat/descriptive/summary/SumOfLogs.java @@ -61,9 +61,19 @@ public class SumOfLogs extends AbstractStorelessUnivariateStatistic implements S value = 0d; n = 0; } + + /** + * Copy constructor, creates a new {@code SumOfLogs} identical + * to the {@code original} + * + * @param original the {@code SumOfLogs} instance to copy + */ + public SumOfLogs(SumOfLogs original) { + copy(original, this); + } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * {@inheritDoc} */ public void increment(final double d) { value += Math.log(d); @@ -71,7 +81,7 @@ public class SumOfLogs extends AbstractStorelessUnivariateStatistic implements S } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { if (n > 0) { @@ -82,14 +92,14 @@ public class SumOfLogs extends AbstractStorelessUnivariateStatistic implements S } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return n; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { value = 0d; @@ -123,4 +133,26 @@ public class SumOfLogs extends AbstractStorelessUnivariateStatistic implements S } return sumLog; } -} \ No newline at end of file + + /* + * {@inheritDoc} + */ + public SumOfLogs copy() { + SumOfLogs result = new SumOfLogs(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source SumOfLogs to copy + * @param dest SumOfLogs to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(SumOfLogs source, SumOfLogs dest) { + dest.n = source.n; + dest.value = source.value; + } +} diff --git a/src/java/org/apache/commons/math/stat/descriptive/summary/SumOfSquares.java b/src/java/org/apache/commons/math/stat/descriptive/summary/SumOfSquares.java index d76f4e1a8..f94bf7c8a 100644 --- a/src/java/org/apache/commons/math/stat/descriptive/summary/SumOfSquares.java +++ b/src/java/org/apache/commons/math/stat/descriptive/summary/SumOfSquares.java @@ -55,7 +55,17 @@ public class SumOfSquares extends AbstractStorelessUnivariateStatistic implement } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#increment(double) + * Copy constructor, creates a new {@code SumOfSquares} identical + * to the {@code original} + * + * @param original the {@code SumOfSquares} instance to copy + */ + public SumOfSquares(SumOfSquares original) { + copy(original, this); + } + + /** + * {@inheritDoc} */ public void increment(final double d) { if (n == 0) { @@ -67,21 +77,21 @@ public class SumOfSquares extends AbstractStorelessUnivariateStatistic implement } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getResult() + * {@inheritDoc} */ public double getResult() { return value; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#getN() + * {@inheritDoc} */ public long getN() { return n; } /** - * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#clear() + * {@inheritDoc} */ public void clear() { value = Double.NaN; @@ -112,5 +122,27 @@ public class SumOfSquares extends AbstractStorelessUnivariateStatistic implement } return sumSq; } + + /* + * {@inheritDoc} + */ + public SumOfSquares copy() { + SumOfSquares result = new SumOfSquares(); + copy(this, result); + return result; + } + + /** + * Copies source to dest. + *

Neither source nor dest can be null.

+ * + * @param source SumOfSquares to copy + * @param dest SumOfSquares to copy to + * @throws NullPointerException if either source or dest is null + */ + public static void copy(SumOfSquares source, SumOfSquares dest) { + dest.n = source.n; + dest.value = source.value; + } -} \ No newline at end of file +} diff --git a/src/java/org/apache/commons/math/stat/inference/ChiSquareTestImpl.java b/src/java/org/apache/commons/math/stat/inference/ChiSquareTestImpl.java index d9a4efe85..c1e8501de 100644 --- a/src/java/org/apache/commons/math/stat/inference/ChiSquareTestImpl.java +++ b/src/java/org/apache/commons/math/stat/inference/ChiSquareTestImpl.java @@ -19,7 +19,6 @@ package org.apache.commons.math.stat.inference; import org.apache.commons.math.MathException; import org.apache.commons.math.distribution.ChiSquaredDistribution; import org.apache.commons.math.distribution.ChiSquaredDistributionImpl; -import org.apache.commons.math.distribution.DistributionFactory; /** * Implements Chi-Square test statistics defined in the @@ -329,16 +328,6 @@ public class ChiSquareTestImpl implements UnknownDistributionChiSquareTest { } - //--------------------- Protected methods --------------------------------- - /** - * Gets a DistributionFactory to use in creating ChiSquaredDistribution instances. - * @deprecated inject ChiSquaredDistribution instances directly instead of - * using a factory. - */ - protected DistributionFactory getDistributionFactory() { - return DistributionFactory.newInstance(); - } - //--------------------- Private array methods -- should find a utility home for these /** diff --git a/src/java/org/apache/commons/math/stat/inference/OneWayAnova.java b/src/java/org/apache/commons/math/stat/inference/OneWayAnova.java index bce9a1603..becaf21ef 100644 --- a/src/java/org/apache/commons/math/stat/inference/OneWayAnova.java +++ b/src/java/org/apache/commons/math/stat/inference/OneWayAnova.java @@ -50,7 +50,7 @@ public interface OneWayAnova { * @throws MathException if the statistic can not be computed do to a * convergence or other numerical error. */ - public double anovaFValue(Collection categoryData) + public double anovaFValue(Collection categoryData) throws IllegalArgumentException, MathException; /** @@ -71,7 +71,7 @@ public interface OneWayAnova { * @throws MathException if the statistic can not be computed do to a * convergence or other numerical error. */ - public double anovaPValue(Collection categoryData) + public double anovaPValue(Collection categoryData) throws IllegalArgumentException, MathException; /** @@ -96,7 +96,7 @@ public interface OneWayAnova { * @throws MathException if the statistic can not be computed do to a * convergence or other numerical error. */ - public boolean anovaTest(Collection categoryData, double alpha) + public boolean anovaTest(Collection categoryData, double alpha) throws IllegalArgumentException, MathException; } \ No newline at end of file diff --git a/src/java/org/apache/commons/math/stat/inference/OneWayAnovaImpl.java b/src/java/org/apache/commons/math/stat/inference/OneWayAnovaImpl.java index 10849fb68..41f55595a 100644 --- a/src/java/org/apache/commons/math/stat/inference/OneWayAnovaImpl.java +++ b/src/java/org/apache/commons/math/stat/inference/OneWayAnovaImpl.java @@ -16,15 +16,13 @@ */ package org.apache.commons.math.stat.inference; -import org.apache.commons.math.MathException; -import org.apache.commons.math.stat.descriptive.summary.Sum; -import org.apache.commons.math.stat.descriptive.summary.SumOfSquares; +import java.util.Collection; +import org.apache.commons.math.MathException; import org.apache.commons.math.distribution.FDistribution; import org.apache.commons.math.distribution.FDistributionImpl; - -import java.util.Collection; -import java.util.Iterator; +import org.apache.commons.math.stat.descriptive.summary.Sum; +import org.apache.commons.math.stat.descriptive.summary.SumOfSquares; /** @@ -65,7 +63,7 @@ public class OneWayAnovaImpl implements OneWayAnova { * are as defined * here

*/ - public double anovaFValue(Collection categoryData) + public double anovaFValue(Collection categoryData) throws IllegalArgumentException, MathException { AnovaStats a = anovaStats(categoryData); return a.F; @@ -81,7 +79,7 @@ public class OneWayAnovaImpl implements OneWayAnova { * where F is the F value and cumulativeProbability * is the commons-math implementation of the F distribution.

*/ - public double anovaPValue(Collection categoryData) + public double anovaPValue(Collection categoryData) throws IllegalArgumentException, MathException { AnovaStats a = anovaStats(categoryData); FDistribution fdist = new FDistributionImpl(a.dfbg, a.dfwg); @@ -99,7 +97,7 @@ public class OneWayAnovaImpl implements OneWayAnova { * is the commons-math implementation of the F distribution.

*

True is returned iff the estimated p-value is less than alpha.

*/ - public boolean anovaTest(Collection categoryData, double alpha) + public boolean anovaTest(Collection categoryData, double alpha) throws IllegalArgumentException, MathException { if ((alpha <= 0) || (alpha > 0.5)) { throw new IllegalArgumentException("bad significance level: " + alpha); @@ -118,7 +116,7 @@ public class OneWayAnovaImpl implements OneWayAnova { * preconditions specified in the interface definition * @throws MathException if an error occurs computing the Anova stats */ - private AnovaStats anovaStats(Collection categoryData) + private AnovaStats anovaStats(Collection categoryData) throws IllegalArgumentException, MathException { // check if we have enough categories @@ -128,14 +126,7 @@ public class OneWayAnovaImpl implements OneWayAnova { } // check if each category has enough data and all is double[] - for (Iterator iterator = categoryData.iterator(); iterator.hasNext();) { - double[] array; - try { - array = (double[])iterator.next(); - } catch (ClassCastException ex) { - throw new IllegalArgumentException( - "ANOVA: categoryData contains non-double[] elements."); - } + for (double[] array : categoryData) { if (array.length <= 1) { throw new IllegalArgumentException( "ANOVA: one element of categoryData has fewer than 2 values."); @@ -148,8 +139,7 @@ public class OneWayAnovaImpl implements OneWayAnova { SumOfSquares totsumsq = new SumOfSquares(); int totnum = 0; - for (Iterator iterator = categoryData.iterator(); iterator.hasNext();) { - double[] data = (double[])iterator.next(); + for (double[] data : categoryData) { Sum sum = new Sum(); SumOfSquares sumsq = new SumOfSquares(); @@ -188,8 +178,14 @@ public class OneWayAnovaImpl implements OneWayAnova { No get/set methods provided. */ private static class AnovaStats { + + /** Degrees of freedom in numerator (between groups). */ private int dfbg; + + /** Degrees of freedom in denominator (within groups). */ private int dfwg; + + /** Statistic. */ private double F; /** diff --git a/src/java/org/apache/commons/math/stat/inference/TTestImpl.java b/src/java/org/apache/commons/math/stat/inference/TTestImpl.java index 0a0d0d053..d69d60a49 100644 --- a/src/java/org/apache/commons/math/stat/inference/TTestImpl.java +++ b/src/java/org/apache/commons/math/stat/inference/TTestImpl.java @@ -17,7 +17,6 @@ package org.apache.commons.math.stat.inference; import org.apache.commons.math.MathException; -import org.apache.commons.math.distribution.DistributionFactory; import org.apache.commons.math.distribution.TDistribution; import org.apache.commons.math.distribution.TDistributionImpl; import org.apache.commons.math.stat.StatUtils; @@ -919,15 +918,6 @@ public class TTestImpl implements TTest { //----------------------------------------------- Protected methods - /** - * Gets a DistributionFactory to use in creating TDistribution instances. - * @return a distribution factory. - * @deprecated inject TDistribution directly instead of using a factory. - */ - protected DistributionFactory getDistributionFactory() { - return DistributionFactory.newInstance(); - } - /** * Computes approximate degrees of freedom for 2-sample t-test. * diff --git a/src/java/org/apache/commons/math/stat/inference/TestFactory.java b/src/java/org/apache/commons/math/stat/inference/TestFactory.java deleted file mode 100644 index e34f06bfc..000000000 --- a/src/java/org/apache/commons/math/stat/inference/TestFactory.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.stat.inference; -import org.apache.commons.discovery.tools.DiscoverClass; - -/** - * Abstract factory to create inference test instances. - * - * @since 1.1 - * @version $Revision$ $Date$ - * @deprecated as of 1.2, pluggability of test instances is now provided through - * constructors and setters. - */ -public abstract class TestFactory { - /** - * Default constructor. - */ - protected TestFactory() { - super(); - } - - /** - * Create an instance of a TestFactory - * - * @return a new factory. - */ - public static TestFactory newInstance() { - TestFactory factory = null; - try { - DiscoverClass dc = new DiscoverClass(); - factory = (TestFactory) dc.newInstance( - TestFactory.class, - "org.apache.commons.math.stat.inference.TestFactoryImpl"); - } catch(Throwable t) { - return new TestFactoryImpl(); - } - return factory; - } - - /** - * Create a TTest instance. - * - * @return a new TTest instance - */ - public abstract TTest createTTest(); - - /** - * Create a ChiSquareTest instance. - * - * @return a new ChiSquareTest instance - */ - public abstract ChiSquareTest createChiSquareTest(); -} diff --git a/src/java/org/apache/commons/math/stat/inference/TestUtils.java b/src/java/org/apache/commons/math/stat/inference/TestUtils.java index 18a900156..d311efb10 100644 --- a/src/java/org/apache/commons/math/stat/inference/TestUtils.java +++ b/src/java/org/apache/commons/math/stat/inference/TestUtils.java @@ -128,6 +128,8 @@ public class TestUtils { } + // CHECKSTYLE: stop JavadocMethodCheck + /** * @see org.apache.commons.math.stat.inference.TTest#homoscedasticT(double[], double[]) */ @@ -382,7 +384,7 @@ public class TestUtils { * * @since 1.2 */ - public static double oneWayAnovaFValue(Collection categoryData) + public static double oneWayAnovaFValue(Collection categoryData) throws IllegalArgumentException, MathException { return oneWayAnova.anovaFValue(categoryData); } @@ -392,7 +394,7 @@ public class TestUtils { * * @since 1.2 */ - public static double oneWayAnovaPValue(Collection categoryData) + public static double oneWayAnovaPValue(Collection categoryData) throws IllegalArgumentException, MathException { return oneWayAnova.anovaPValue(categoryData); } @@ -402,9 +404,11 @@ public class TestUtils { * * @since 1.2 */ - public static boolean oneWayAnovaTest(Collection categoryData, double alpha) + public static boolean oneWayAnovaTest(Collection categoryData, double alpha) throws IllegalArgumentException, MathException { return oneWayAnova.anovaTest(categoryData, alpha); } + // CHECKSTYLE: resume JavadocMethodCheck + } diff --git a/src/java/org/apache/commons/math/stat/regression/AbstractMultipleLinearRegression.java b/src/java/org/apache/commons/math/stat/regression/AbstractMultipleLinearRegression.java new file mode 100644 index 000000000..49057c13d --- /dev/null +++ b/src/java/org/apache/commons/math/stat/regression/AbstractMultipleLinearRegression.java @@ -0,0 +1,187 @@ +/* + * 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.stat.regression; + +import org.apache.commons.math.linear.RealMatrix; +import org.apache.commons.math.linear.RealMatrixImpl; + +/** + * Abstract base class for implementations of MultipleLinearRegression. + * @version $Revision$ $Date$ + * @since 2.0 + */ +public abstract class AbstractMultipleLinearRegression implements + MultipleLinearRegression { + + /** X sample data. */ + protected RealMatrix X; + + /** Y sample data. */ + protected RealMatrix Y; + + /** + * Loads model x and y sample data from a flat array of data, overriding any previous sample. + * Assumes that rows are concatenated with y values first in each row. + * + * @param data input data array + * @param nobs number of observations (rows) + * @param nvars number of independent variables (columnns, not counting y) + */ + public void newSampleData(double[] data, int nobs, int nvars) { + double[] y = new double[nobs]; + double[][] x = new double[nobs][nvars + 1]; + int pointer = 0; + for (int i = 0; i < nobs; i++) { + y[i] = data[pointer++]; + x[i][0] = 1.0d; + for (int j = 1; j < nvars + 1; j++) { + x[i][j] = data[pointer++]; + } + } + this.X = new RealMatrixImpl(x); + this.Y = new RealMatrixImpl(y); + } + + /** + * Loads new y sample data, overriding any previous sample + * + * @param y the [n,1] array representing the y sample + */ + protected void newYSampleData(double[] y) { + this.Y = new RealMatrixImpl(y); + } + + /** + * Loads new x sample data, overriding any previous sample + * + * @param x the [n,k] array representing the x sample + */ + protected void newXSampleData(double[][] x) { + this.X = new RealMatrixImpl(x); + } + + /** + * Validates sample data. + * + * @param x the [n,k] array representing the x sample + * @param y the [n,1] array representing the y sample + * @throws IllegalArgumentException if the x and y array data are not + * compatible for the regression + */ + protected void validateSampleData(double[][] x, double[] y) { + if (x == null) { + throw new IllegalArgumentException("The regressors matrix x cannot be null."); + } + if (y == null) { + throw new IllegalArgumentException("The regressand vector y cannot be null."); + } + if (x.length != y.length) { + throw new IllegalArgumentException( + "The regressors matrix x columns must have the same length of the regressand vector y"); + } + } + + /** + * Validates sample data. + * + * @param x the [n,k] array representing the x sample + * @param covariance the [n,n] array representing the covariance matrix + * @throws IllegalArgumentException if the x sample data or covariance + * matrix are not compatible for the regression + */ + protected void validateCovarianceData(double[][] x, double[][] covariance) { + if (covariance == null) { + throw new IllegalArgumentException("Covariance matrix cannot be null."); + } + if (x.length != covariance.length) { + throw new IllegalArgumentException( + "The regressors matrix x columns must have the same length of the covariance matrix columns"); + } + if (covariance.length > 0 && covariance.length != covariance[0].length) { + throw new IllegalArgumentException("The covariance matrix must be square"); + } + } + + /** + * {@inheritDoc} + */ + public double[] estimateRegressionParameters() { + RealMatrix b = calculateBeta(); + return b.getColumn(0); + } + + /** + * {@inheritDoc} + */ + public double[] estimateResiduals() { + RealMatrix b = calculateBeta(); + RealMatrix e = Y.subtract(X.multiply(b)); + return e.getColumn(0); + } + + /** + * {@inheritDoc} + */ + public double[][] estimateRegressionParametersVariance() { + return calculateBetaVariance().getData(); + } + + /** + * {@inheritDoc} + */ + public double estimateRegressandVariance() { + return calculateYVariance(); + } + + /** + * Calculates the beta of multiple linear regression in matrix notation. + * + * @return beta + */ + protected abstract RealMatrix calculateBeta(); + + /** + * Calculates the beta variance of multiple linear regression in matrix + * notation. + * + * @return beta variance + */ + protected abstract RealMatrix calculateBetaVariance(); + + /** + * Calculates the Y variance of multiple linear regression. + * + * @return Y variance + */ + protected abstract double calculateYVariance(); + + /** + * Calculates the residuals of multiple linear regression in matrix + * notation. + * + *
+     * u = y - X * b
+     * 
+ * + * @return The residuals [n,1] matrix + */ + protected RealMatrix calculateResiduals() { + RealMatrix b = calculateBeta(); + return Y.subtract(X.multiply(b)); + } + +} diff --git a/src/java/org/apache/commons/math/stat/regression/GLSMultipleLinearRegression.java b/src/java/org/apache/commons/math/stat/regression/GLSMultipleLinearRegression.java new file mode 100644 index 000000000..55375c16b --- /dev/null +++ b/src/java/org/apache/commons/math/stat/regression/GLSMultipleLinearRegression.java @@ -0,0 +1,126 @@ +/* + * 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.stat.regression; + +import org.apache.commons.math.linear.LUDecompositionImpl; +import org.apache.commons.math.linear.RealMatrix; +import org.apache.commons.math.linear.RealMatrixImpl; + + +/** + * The GLS implementation of the multiple linear regression. + * + * GLS assumes a general covariance matrix Omega of the error + *
+ * u ~ N(0, Omega)
+ * 
+ * + * Estimated by GLS, + *
+ * b=(X' Omega^-1 X)^-1X'Omega^-1 y
+ * 
+ * whose variance is + *
+ * Var(b)=(X' Omega^-1 X)^-1
+ * 
+ * @version $Revision$ $Date$ + * @since 2.0 + */ +public class GLSMultipleLinearRegression extends AbstractMultipleLinearRegression { + + /** Covariance matrix. */ + private RealMatrix Omega; + + /** Inverse of covariance matrix. */ + private RealMatrix OmegaInverse; + + /** Replace sample data, overriding any previous sample. + * @param y y values of the sample + * @param x x values of the sample + * @param covariance array representing the covariance matrix + */ + public void newSampleData(double[] y, double[][] x, double[][] covariance) { + validateSampleData(x, y); + newYSampleData(y); + newXSampleData(x); + validateCovarianceData(x, covariance); + newCovarianceData(covariance); + } + + /** + * Add the covariance data. + * + * @param omega the [n,n] array representing the covariance + */ + protected void newCovarianceData(double[][] omega){ + this.Omega = new RealMatrixImpl(omega); + this.OmegaInverse = null; + } + + /** + * Get the inverse of the covariance. + *

The inverse of the covariance matrix is lazily evaluated and cached.

+ * @return inverse of the covariance + */ + protected RealMatrix getOmegaInverse() { + if (OmegaInverse == null) { + OmegaInverse = new LUDecompositionImpl(Omega).getInverse(); + } + return OmegaInverse; + } + + /** + * Calculates beta by GLS. + *
+     *  b=(X' Omega^-1 X)^-1X'Omega^-1 y
+     * 
+ * @return beta + */ + protected RealMatrix calculateBeta() { + RealMatrix OI = getOmegaInverse(); + RealMatrix XT = X.transpose(); + RealMatrix XTOIX = XT.multiply(OI).multiply(X); + return new LUDecompositionImpl(XTOIX).getInverse().multiply(XT).multiply(OI).multiply(Y); + } + + /** + * Calculates the variance on the beta by GLS. + *
+     *  Var(b)=(X' Omega^-1 X)^-1
+     * 
+ * @return The beta variance matrix + */ + protected RealMatrix calculateBetaVariance() { + RealMatrix OI = getOmegaInverse(); + RealMatrix XTOIX = X.transpose().multiply(OI).multiply(X); + return new LUDecompositionImpl(XTOIX).getInverse(); + } + + /** + * Calculates the variance on the y by GLS. + *
+     *  Var(y)=Tr(u' Omega^-1 u)/(n-k)
+     * 
+ * @return The Y variance + */ + protected double calculateYVariance() { + RealMatrix u = calculateResiduals(); + RealMatrix sse = u.transpose().multiply(getOmegaInverse()).multiply(u); + return sse.getTrace()/(X.getRowDimension()-X.getColumnDimension()); + } + +} diff --git a/src/java/org/apache/commons/math/stat/regression/MultipleLinearRegression.java b/src/java/org/apache/commons/math/stat/regression/MultipleLinearRegression.java new file mode 100644 index 000000000..eb0795b50 --- /dev/null +++ b/src/java/org/apache/commons/math/stat/regression/MultipleLinearRegression.java @@ -0,0 +1,63 @@ +/* + * 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.stat.regression; + +/** + * The multiple linear regression can be represented in matrix-notation. + *
+ *  y=X*b+u
+ * 
+ * where y is an n-vector regressand, X is a [n,k] matrix whose k columns are called + * regressors, b is k-vector of regression parameters and u is an n-vector + * of error terms or residuals. + * + * The notation is quite standard in literature, + * cf eg Davidson and MacKinnon, Econometrics Theory and Methods, 2004. + * @version $Revision$ $Date$ + * @since 2.0 + */ +public interface MultipleLinearRegression { + + /** + * Estimates the regression parameters b. + * + * @return The [k,1] array representing b + */ + double[] estimateRegressionParameters(); + + /** + * Estimates the variance of the regression parameters, ie Var(b). + * + * @return The [k,k] array representing the variance of b + */ + double[][] estimateRegressionParametersVariance(); + + /** + * Estimates the residuals, ie u = y - X*b. + * + * @return The [n,1] array representing the residuals + */ + double[] estimateResiduals(); + + /** + * Returns the variance of the regressand, ie Var(y). + * + * @return The double representing the variance of y + */ + double estimateRegressandVariance(); + +} diff --git a/src/java/org/apache/commons/math/stat/regression/OLSMultipleLinearRegression.java b/src/java/org/apache/commons/math/stat/regression/OLSMultipleLinearRegression.java new file mode 100644 index 000000000..06ac1595a --- /dev/null +++ b/src/java/org/apache/commons/math/stat/regression/OLSMultipleLinearRegression.java @@ -0,0 +1,199 @@ +/* + * 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.stat.regression; + +import org.apache.commons.math.linear.LUDecompositionImpl; +import org.apache.commons.math.linear.QRDecomposition; +import org.apache.commons.math.linear.QRDecompositionImpl; +import org.apache.commons.math.linear.RealMatrix; +import org.apache.commons.math.linear.RealMatrixImpl; + +/** + *

Implements ordinary least squares (OLS) to estimate the parameters of a + * multiple linear regression model.

+ * + *

OLS assumes the covariance matrix of the error to be diagonal and with + * equal variance. + *

+ * u ~ N(0, sigma^2*I)
+ * 

+ * + *

The regression coefficients, b, satisfy the normal equations: + *

+ * X^T X b = X^T y
+ * 

+ * + *

To solve the normal equations, this implementation uses QR decomposition + * of the X matrix. (See {@link QRDecompositionImpl} for details on the + * decomposition algorithm.) + *

+ * X^T X b = X^T y
+ * (QR)^T (QR) b = (QR)^T y
+ * R^T (Q^T Q) R b = R^T Q^T y
+ * R^T R b = R^T Q^T y
+ * (R^T)^{-1} R^T R b = (R^T)^{-1} R^T Q^T y
+ * R b = Q^T y
+ * 
+ * Given Q and R, the last equation is solved by back-subsitution.

+ * + * @version $Revision$ $Date$ + * @since 2.0 + */ +public class OLSMultipleLinearRegression extends AbstractMultipleLinearRegression { + + /** Cached QR decomposition of X matrix */ + private QRDecomposition qr = null; + + /** + * {@inheritDoc} + * + * Computes and caches QR decomposition of the X matrix. + */ + public void newSampleData(double[] y, double[][] x) { + validateSampleData(x, y); + newYSampleData(y); + newXSampleData(x); + } + + /** + * {@inheritDoc} + * + * Computes and caches QR decomposition of the X matrix + */ + public void newSampleData(double[] data, int nobs, int nvars) { + super.newSampleData(data, nobs, nvars); + qr = new QRDecompositionImpl(X); + } + + /** + * Loads new x sample data, overriding any previous sample + * + * @param x the [n,k] array representing the x sample + */ + protected void newXSampleData(double[][] x) { + this.X = new RealMatrixImpl(x); + qr = new QRDecompositionImpl(X); + } + + /** + * Calculates regression coefficients using OLS. + * + * @return beta + */ + protected RealMatrix calculateBeta() { + return solveUpperTriangular((RealMatrixImpl) qr.getR(), + (RealMatrixImpl) qr.getQ().transpose().multiply(Y)); + } + + /** + * Calculates the variance on the beta by OLS. + *
+     *  Var(b)=(X'X)^-1
+     * 
+ * @return The beta variance + */ + protected RealMatrix calculateBetaVariance() { + RealMatrix XTX = X.transpose().multiply(X); + return new LUDecompositionImpl(XTX).getInverse(); + } + + + /** + * Calculates the variance on the Y by OLS. + *
+     *  Var(y)=Tr(u'u)/(n-k)
+     * 
+ * @return The Y variance + */ + protected double calculateYVariance() { + RealMatrix u = calculateResiduals(); + RealMatrix sse = u.transpose().multiply(u); + return sse.getTrace()/(X.getRowDimension()-X.getColumnDimension()); + } + + /** TODO: Find a home for the following methods in the linear package */ + + /** + *

Uses back substitution to solve the system

+ * + *

coefficients X = constants

+ * + *

coefficients must upper-triangular and constants must be a column + * matrix. The solution is returned as a column matrix.

+ * + *

The number of columns in coefficients determines the length + * of the returned solution vector (column matrix). If constants + * has more rows than coefficients has columns, excess rows are ignored. + * Similarly, extra (zero) rows in coefficients are ignored

+ * + * @param coefficients upper-triangular coefficients matrix + * @param constants column RHS constants matrix + * @return solution matrix as a column matrix + * + */ + private static RealMatrix solveUpperTriangular(RealMatrixImpl coefficients, + RealMatrixImpl constants) { + if (!isUpperTriangular(coefficients, 1E-12)) { + throw new IllegalArgumentException( + "Coefficients is not upper-triangular"); + } + if (constants.getColumnDimension() != 1) { + throw new IllegalArgumentException( + "Constants not a column matrix."); + } + int length = coefficients.getColumnDimension(); + double[][] cons = constants.getDataRef(); + double[][] coef = coefficients.getDataRef(); + double x[] = new double[length]; + for (int i = 0; i < length; i++) { + int index = length - 1 - i; + double sum = 0; + for (int j = index + 1; j < length; j++) { + sum += coef[index][j] * x[j]; + } + x[index] = (cons[index][0] - sum) / coef[index][index]; + } + return new RealMatrixImpl(x); + } + + /** + *

Returns true iff m is an upper-triangular matrix.

+ * + *

Makes sure all below-diagonal elements are within epsilon of 0.

+ * + * @param m matrix to check + * @param epsilon maximum allowable absolute value for elements below + * the main diagonal + * + * @return true if m is upper-triangular; false otherwise + * @throws NullPointerException if m is null + */ + private static boolean isUpperTriangular(RealMatrixImpl m, double epsilon) { + double[][] data = m.getDataRef(); + int nCols = m.getColumnDimension(); + int nRows = m.getRowDimension(); + for (int r = 0; r < nRows; r++) { + int bound = Math.min(r, nCols); + for (int c = 0; c < bound; c++) { + if (Math.abs(data[r][c]) > epsilon) { + return false; + } + } + } + return true; + } +} diff --git a/src/java/org/apache/commons/math/stat/regression/SimpleRegression.java b/src/java/org/apache/commons/math/stat/regression/SimpleRegression.java index 289e5488f..0ba416842 100644 --- a/src/java/org/apache/commons/math/stat/regression/SimpleRegression.java +++ b/src/java/org/apache/commons/math/stat/regression/SimpleRegression.java @@ -139,6 +139,39 @@ public class SimpleRegression implements Serializable { } } + + /** + * Removes the observation (x,y) from the regression data set. + *

+ * Mirrors the addData method. This method permits the use of + * SimpleRegression instances in streaming mode where the regression + * is applied to a sliding "window" of observations, however the caller is + * responsible for maintaining the set of observations in the window.

+ * + * The method has no effect if there are no points of data (i.e. n=0) + * + * @param x independent variable value + * @param y dependent variable value + */ + public void removeData(double x, double y) { + if (n > 0) { + double dx = x - xbar; + double dy = y - ybar; + sumXX -= dx * dx * (double) n / (double) (n - 1.0); + sumYY -= dy * dy * (double) n / (double) (n - 1.0); + sumXY -= dx * dy * (double) n / (double) (n - 1.0); + xbar -= dx / (double) (n - 1.0); + ybar -= dy / (double) (n - 1.0); + sumX -= x; + sumY -= y; + n--; + + if (n > 2) { + distribution.setDegreesOfFreedom(n - 2); + } + } + } + /** * Adds the observations represented by the elements in * data. @@ -161,6 +194,26 @@ public class SimpleRegression implements Serializable { } } + + /** + * Removes observations represented by the elements in data. + *

+ * If the array is larger than the current n, only the first n elements are + * processed. This method permits the use of SimpleRegression instances in + * streaming mode where the regression is applied to a sliding "window" of + * observations, however the caller is responsible for maintaining the set + * of observations in the window.

+ *

+ * To remove all data, use clear().

+ * + * @param data array of observations to be removed + */ + public void removeData(double[][] data) { + for (int i = 0; i < data.length && n > 0; i++) { + removeData(data[i][0], data[i][1]); + } + } + /** * Clears all data from the model. */ diff --git a/src/java/org/apache/commons/math/transform/FastCosineTransformer.java b/src/java/org/apache/commons/math/transform/FastCosineTransformer.java index 38b27be1c..b14afd099 100644 --- a/src/java/org/apache/commons/math/transform/FastCosineTransformer.java +++ b/src/java/org/apache/commons/math/transform/FastCosineTransformer.java @@ -34,7 +34,7 @@ import org.apache.commons.math.MathException; * power of 2 plus one. Users should especially pay attention to the * function transformation on how this affects the sampling.

* - * @version $Revision$ $Date$ + * @version $Revision:670469 $ $Date:2008-06-23 10:01:38 +0200 (lun., 23 juin 2008) $ * @since 1.2 */ public class FastCosineTransformer implements Serializable { diff --git a/src/java/org/apache/commons/math/util/CompositeFormat.java b/src/java/org/apache/commons/math/util/CompositeFormat.java new file mode 100644 index 000000000..998a5574f --- /dev/null +++ b/src/java/org/apache/commons/math/util/CompositeFormat.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.util; + +import java.text.FieldPosition; +import java.text.Format; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Locale; + +/** + * Base class for formatters of composite objects (complex numbers, vectors ...). + * + * @author Apache Software Foundation + * @version $Revision$ $Date$ + */ +public abstract class CompositeFormat extends Format { + + /** Serializable version identifier. */ + private static final long serialVersionUID = 5358685519349262494L; + + /** + * Create a default number format. The default number format is based on + * {@link NumberFormat#getInstance()} with the only customizing that the + * maximum number of fraction digits is set to 2. + * @return the default number format. + */ + protected static NumberFormat getDefaultNumberFormat() { + return getDefaultNumberFormat(Locale.getDefault()); + } + + /** + * Create a default number format. The default number format is based on + * {@link NumberFormat#getInstance(java.util.Locale)} with the only + * customizing that the maximum number of fraction digits is set to 2. + * @param locale the specific locale used by the format. + * @return the default number format specific to the given locale. + */ + protected static NumberFormat getDefaultNumberFormat(final Locale locale) { + final NumberFormat nf = NumberFormat.getInstance(locale); + nf.setMaximumFractionDigits(2); + return nf; + } + + /** + * Parses source until a non-whitespace character is found. + * + * @param source the string to parse + * @param pos input/ouput parsing parameter. On output, pos + * holds the index of the next non-whitespace character. + */ + protected void parseAndIgnoreWhitespace(final String source, + final ParsePosition pos) { + parseNextCharacter(source, pos); + pos.setIndex(pos.getIndex() - 1); + } + + /** + * Parses source until a non-whitespace character is found. + * + * @param source the string to parse + * @param pos input/ouput parsing parameter. + * @return the first non-whitespace character. + */ + protected char parseNextCharacter(final String source, + final ParsePosition pos) { + int index = pos.getIndex(); + final int n = source.length(); + char ret = 0; + + if (index < n) { + char c; + do { + c = source.charAt(index++); + } while (Character.isWhitespace(c) && index < n); + pos.setIndex(index); + + if (index < n) { + ret = c; + } + } + + return ret; + } + + /** + * Parses source for special double values. These values + * include Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY. + * + * @param source the string to parse + * @param value the special value to parse. + * @param pos input/ouput parsing parameter. + * @return the special number. + */ + private Number parseNumber(final String source, final double value, + final ParsePosition pos) { + Number ret = null; + + StringBuffer sb = new StringBuffer(); + sb.append('('); + sb.append(value); + sb.append(')'); + + final int n = sb.length(); + final int startIndex = pos.getIndex(); + final int endIndex = startIndex + n; + if (endIndex < source.length()) { + if (source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) { + ret = Double.valueOf(value); + pos.setIndex(endIndex); + } + } + + return ret; + } + + /** + * Parses source for a number. This method can parse normal, + * numeric values as well as special values. These special values include + * Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY. + * + * @param source the string to parse + * @param format the number format used to parse normal, numeric values. + * @param pos input/ouput parsing parameter. + * @return the parsed number. + */ + protected Number parseNumber(final String source, final NumberFormat format, + final ParsePosition pos) { + final int startIndex = pos.getIndex(); + Number number = format.parse(source, pos); + final int endIndex = pos.getIndex(); + + // check for error parsing number + if (startIndex == endIndex) { + // try parsing special numbers + final double[] special = { + Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY + }; + for (int i = 0; i < special.length; ++i) { + number = parseNumber(source, special[i], pos); + if (number != null) { + break; + } + } + } + + return number; + } + + /** + * Parse source for an expected fixed string. + * @param source the string to parse + * @param expected expected string + * @param pos input/ouput parsing parameter. + * @return true if the expected string was there + */ + protected boolean parseFixedstring(final String source, final String expected, + final ParsePosition pos) { + + final int startIndex = pos.getIndex(); + final int endIndex = startIndex + expected.length(); + if ((startIndex >= source.length()) || + (endIndex > source.length()) || + (source.substring(startIndex, endIndex).compareTo(expected) != 0)) { + // set index back to start, error index should be the start index + pos.setIndex(startIndex); + pos.setErrorIndex(startIndex); + return false; + } + + // the string was here + pos.setIndex(endIndex); + return true; + + } + + /** + * Formats a double value to produce a string. In general, the value is + * formatted using the formatting rules of format. There are + * three exceptions to this: + *
    + *
  1. NaN is formatted as '(NaN)'
  2. + *
  3. Positive infinity is formatted as '(Infinity)'
  4. + *
  5. Negative infinity is formatted as '(-Infinity)'
  6. + *
+ * + * @param value the double to format. + * @param format the format used. + * @param toAppendTo where the text is to be appended + * @param pos On input: an alignment field, if desired. On output: the + * offsets of the alignment field + * @return the value passed in as toAppendTo. + */ + protected StringBuffer formatDouble(final double value, final NumberFormat format, + final StringBuffer toAppendTo, + final FieldPosition pos) { + if( Double.isNaN(value) || Double.isInfinite(value) ) { + toAppendTo.append('('); + toAppendTo.append(value); + toAppendTo.append(')'); + } else { + format.format(value, toAppendTo, pos); + } + return toAppendTo; + } + +} \ No newline at end of file diff --git a/src/java/org/apache/commons/math/util/ContinuedFraction.java b/src/java/org/apache/commons/math/util/ContinuedFraction.java index 6e8599794..8436bc59a 100644 --- a/src/java/org/apache/commons/math/util/ContinuedFraction.java +++ b/src/java/org/apache/commons/math/util/ContinuedFraction.java @@ -155,7 +155,7 @@ public abstract class ContinuedFraction implements Serializable { // can not scale an convergent is unbounded. throw new ConvergenceException( "Continued fraction convergents diverged to +/- infinity for value {0}", - new Object[] { new Double(x) }); + new Object[] { Double.valueOf(x) }); } } double r = p2 / q2; @@ -172,7 +172,7 @@ public abstract class ContinuedFraction implements Serializable { if (n >= maxIterations) { throw new MaxIterationsExceededException(maxIterations, "Continued fraction convergents failed to converge for value {0}", - new Object[] { new Double(x) }); + new Object[] { Double.valueOf(x) }); } return c; diff --git a/src/java/org/apache/commons/math/util/DefaultTransformer.java b/src/java/org/apache/commons/math/util/DefaultTransformer.java index bb610e8ec..216b6cf06 100644 --- a/src/java/org/apache/commons/math/util/DefaultTransformer.java +++ b/src/java/org/apache/commons/math/util/DefaultTransformer.java @@ -44,7 +44,7 @@ public class DefaultTransformer implements NumberTransformer, Serializable { public double transform(Object o) throws MathException{ if (o == null) { - throw new MathException("Conversion Exception in Transformation, Object is null", new Object[0]); + throw new MathException("Conversion Exception in Transformation, Object is null", null); } if (o instanceof Number) { @@ -52,7 +52,7 @@ public class DefaultTransformer implements NumberTransformer, Serializable { } try { - return new Double(o.toString()).doubleValue(); + return Double.valueOf(o.toString()).doubleValue(); } catch (Exception e) { throw new MathException("Conversion Exception in Transformation: {0}", new Object[] { e.getMessage() }, e); diff --git a/src/java/org/apache/commons/math/util/MathUtils.java b/src/java/org/apache/commons/math/util/MathUtils.java index 0b87cae40..c5342aeb3 100644 --- a/src/java/org/apache/commons/math/util/MathUtils.java +++ b/src/java/org/apache/commons/math/util/MathUtils.java @@ -18,6 +18,7 @@ package org.apache.commons.math.util; import java.math.BigDecimal; +import java.util.Arrays; /** * Some useful additions to the built-in functions in {@link Math}. @@ -25,6 +26,15 @@ import java.math.BigDecimal; */ public final class MathUtils { + /** Smallest positive number such that 1 - EPSILON is not numerically equal to 1. */ + public static final double EPSILON = 0x1.0p-53; + + /** Safe minimum, such that 1 / SAFE_MIN does not overflow. + *

In IEEE 754 arithmetic, this is also the smallest normalized + * number 2-1022.

+ */ + public static final double SAFE_MIN = 0x1.0p-1022; + /** -1.0 cast as a byte. */ private static final byte NB = (byte)-1; @@ -456,26 +466,18 @@ public final class MathUtils { * @return the hash code */ public static int hash(double value) { - long bits = Double.doubleToLongBits(value); - return (int)(bits ^ (bits >>> 32)); + return new Double(value).hashCode(); } /** - * Returns an integer hash code representing the given double array value. + * Returns an integer hash code representing the given double array. * * @param value the value to be hashed (may be null) * @return the hash code * @since 1.2 */ public static int hash(double[] value) { - if (value == null) { - return 0; - } - int result = value.length; - for (int i = 0; i < value.length; ++i) { - result = result * 31 + hash(value[i]); - } - return result; + return Arrays.hashCode(value); } /** @@ -712,6 +714,33 @@ public final class MathUtils { } + /** + * Scale a number by 2scaleFactor. + *

If d is 0 or NaN or Infinite, it is returned unchanged.

+ * + * @param d base number + * @param scaleFactor power of two by which d sould be multiplied + * @return d × 2scaleFactor + * @since 2.0 + */ + public static double scalb(final double d, final int scaleFactor) { + + // handling of some important special cases + if ((d == 0) || Double.isNaN(d) || Double.isInfinite(d)) { + return d; + } + + // split the double in raw components + final long bits = Double.doubleToLongBits(d); + final long exponent = bits & 0x7ff0000000000000L; + final long rest = bits & 0x800fffffffffffffL; + + // shift the exponent + final long newBits = rest | (exponent + (((long) scaleFactor) << 52)); + return Double.longBitsToDouble(newBits); + + } + /** * Normalize an angle in a 2&pi wide interval around a center value. *

This method has three main uses:

diff --git a/src/java/org/apache/commons/math/util/ResizableDoubleArray.java b/src/java/org/apache/commons/math/util/ResizableDoubleArray.java index 71abc326e..333be6f86 100644 --- a/src/java/org/apache/commons/math/util/ResizableDoubleArray.java +++ b/src/java/org/apache/commons/math/util/ResizableDoubleArray.java @@ -17,6 +17,9 @@ package org.apache.commons.math.util; import java.io.Serializable; +import java.util.Arrays; + +import org.apache.commons.math.MathRuntimeException; /** *

@@ -241,6 +244,19 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { setExpansionMode(expansionMode); internalArray = new double[initialCapacity]; } + + /** + * Copy constructor. Creates a new ResizableDoubleArray that is a deep, + * fresh copy of the original. Needs to acquire synchronization lock + * on original. Original may not be null; otherwise a NullPointerException + * is thrown. + * + * @param original + * @since 2.0 + */ + public ResizableDoubleArray(ResizableDoubleArray original) { + copy(original, this); + } /** * Adds an element to the end of this expandable array. @@ -292,14 +308,37 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { } return discarded; } + + /** + * Substitutes value for the most recently added value. + * Returns the value that has been replaced. If the array is empty (i.e. + * if {@link #numElements} is zero), a MathRuntimeException is thrown. + * + * @param value new value to substitute for the most recently added value + * @return value that has been replaced in the array + * @since 2.0 + */ + public synchronized double substituteMostRecentElement(double value) { + if (numElements < 1) { + throw MathRuntimeException.createArrayIndexOutOfBoundsException( + "cannot substitute an element from an empty array", null); + } + double discarded = internalArray[startIndex + (numElements - 1)]; + + internalArray[startIndex + (numElements - 1)] = value; + + return discarded; + } + + /** * Checks the expansion factor and the contraction criteria and throws an * IllegalArgumentException if the contractionCriteria is less than the * expansionCriteria * * @param expansionFactor factor to be checked - * @param contractionCritera critera to be checked + * @param contractionCritera criteria to be checked * @throws IllegalArgumentException if the contractionCriteria is less than * the expansionCriteria. */ @@ -368,8 +407,51 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { * * @param i the number of elements to discard from the front of the array * @throws IllegalArgumentException if i is greater than numElements. + * @since 2.0 */ public synchronized void discardFrontElements(int i) { + + discardExtremeElements(i,true); + + } + + /** + * Discards the i last elements of the array. For example, + * if the array contains the elements 1,2,3,4, invoking + * discardMostRecentElements(2) will cause the last two elements + * to be discarded, leaving 1,2 in the array. Throws illegalArgumentException + * if i exceeds numElements. + * + * @param i the number of elements to discard from the end of the array + * @throws IllegalArgumentException if i is greater than numElements. + * @since 2.0 + */ + public synchronized void discardMostRecentElements(int i) { + + discardExtremeElements(i,false); + + } + + /** + * Discards the i first or last elements of the array, + * depending on the value of front. + * For example, if the array contains the elements 1,2,3,4, invoking + * discardExtremeElements(2,false) will cause the last two elements + * to be discarded, leaving 1,2 in the array. + * For example, if the array contains the elements 1,2,3,4, invoking + * discardExtremeElements(2,true) will cause the first two elements + * to be discarded, leaving 3,4 in the array. + * Throws illegalArgumentException + * if i exceeds numElements. + * + * @param i the number of elements to discard from the front/end of the array + * @param front true if elements are to be discarded from the front + * of the array, false if elements are to be discarded from the end + * of the array + * @throws IllegalArgumentException if i is greater than numElements. + * @since 2.0 + */ + private synchronized void discardExtremeElements(int i,boolean front) { if (i > numElements) { String msg = "Cannot discard more elements than are" + "contained in this array."; @@ -380,7 +462,7 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { } else { // "Subtract" this number of discarded from numElements numElements -= i; - startIndex += i; + if (front) startIndex += i; } if (shouldContract()) { contract(); @@ -398,7 +480,7 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { */ protected synchronized void expand() { - // notice the use of Math.ceil(), this gaurantees that we will always + // notice the use of Math.ceil(), this guarantees that we will always // have an array of at least currentSize + 1. Assume that the // current initial capacity is 1 and the expansion factor // is 1.000000000000000001. The newly calculated size will be @@ -454,16 +536,13 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { */ public synchronized double getElement(int index) { if (index >= numElements) { - String msg = - "The index specified: " + index + - " is larger than the current number of elements"; - throw new ArrayIndexOutOfBoundsException(msg); + throw MathRuntimeException.createArrayIndexOutOfBoundsException("the index specified: {0} is larger than the current maximal index {1}", + new Object[] { index, numElements - 1 }); } else if (index >= 0) { return internalArray[startIndex + index]; } else { - String msg = - "Elements cannot be retrieved from a negative array index"; - throw new ArrayIndexOutOfBoundsException(msg); + throw MathRuntimeException.createArrayIndexOutOfBoundsException("elements cannot be retrieved from a negative array index {0}", + new Object[] { index }); } } @@ -482,7 +561,7 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { } /** - * The expansion factor controls the size of a new aray when an array + * The expansion factor controls the size of a new array when an array * needs to be expanded. The expansionMode * determines whether the size of the array is multiplied by the * expansionFactor (MULTIPLICATIVE_MODE) or if @@ -570,8 +649,8 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { */ public synchronized void setElement(int index, double value) { if (index < 0) { - String msg = "Cannot set an element at a negative index"; - throw new ArrayIndexOutOfBoundsException(msg); + throw MathRuntimeException.createArrayIndexOutOfBoundsException("cannot set an element at a negative index {0}", + new Object[] { index }); } if (index + 1 > numElements) { numElements = index + 1; @@ -652,7 +731,7 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { } // Test the new num elements, check to see if the array needs to be - // expanded to accomodate this new number of elements + // expanded to accommodate this new number of elements if ((startIndex + i) > internalArray.length) { expandTo(startIndex + i); } @@ -687,5 +766,100 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { public synchronized int start() { return startIndex; } - + + /** + *

Copies source to dest, copying the underlying data, so dest is + * a new, independent copy of source. Does not contract before + * the copy.

+ * + *

Obtains synchronization locks on both source and dest + * (in that order) before performing the copy.

+ * + *

Neither source nor dest may be null; otherwise a NullPointerException + * is thrown

+ * + * @param source ResizableDoubleArray to copy + * @param dest ResizableArray to replace with a copy of the source array + * @since 2.0 + * + */ + public static void copy(ResizableDoubleArray source, ResizableDoubleArray dest) { + synchronized(source) { + synchronized(dest) { + dest.initialCapacity = source.initialCapacity; + dest.contractionCriteria = source.contractionCriteria; + dest.expansionFactor = source.expansionFactor; + dest.expansionMode = source.expansionMode; + dest.internalArray = new double[source.internalArray.length]; + System.arraycopy(source.internalArray, 0, dest.internalArray, + 0, dest.internalArray.length); + dest.numElements = source.numElements; + dest.startIndex = source.startIndex; + } + } + } + + /** + * Returns a copy of the ResizableDoubleArray. Does not contract before + * the copy, so the returned object is an exact copy of this. + * + * @return a new ResizableDoubleArray with the same data and configuration + * properties as this + * @since 2.0 + */ + public synchronized ResizableDoubleArray copy() { + ResizableDoubleArray result = new ResizableDoubleArray(); + copy(this, result); + return result; + } + + /** + * Returns true iff object is a ResizableDoubleArray with the same properties + * as this and an identical internal storage array. + * + * @param object object to be compared for equality with this + * @return true iff object is a ResizableDoubleArray with the same data and + * properties as this + * @since 2.0 + */ + public boolean equals(Object object) { + if (object == this ) { + return true; + } + if (object instanceof ResizableDoubleArray == false) { + return false; + } + boolean result = true; + ResizableDoubleArray other = (ResizableDoubleArray) object; + result = result && (other.initialCapacity == initialCapacity); + result = result && (other.contractionCriteria == contractionCriteria); + result = result && (other.expansionFactor == expansionFactor); + result = result && (other.expansionMode == expansionMode); + result = result && (other.numElements == numElements); + result = result && (other.startIndex == startIndex); + if (!result) { + return false; + } else { + return Arrays.equals(internalArray, other.internalArray); + } + } + + /** + * Returns a hash code consistent with equals. + * + * @return hash code representing this ResizableDoubleArray + * @since 2.0 + */ + public int hashCode() { + int[] hashData = new int[7]; + hashData[0] = Arrays.hashCode(internalArray); + hashData[1] = new Float(expansionFactor).hashCode(); + hashData[2] = new Float(contractionCriteria).hashCode(); + hashData[3] = initialCapacity; + hashData[4] = expansionMode; + hashData[5] = numElements; + hashData[6] = startIndex; + return Arrays.hashCode(hashData); + } + } diff --git a/src/java/org/apache/commons/math/util/TransformerMap.java b/src/java/org/apache/commons/math/util/TransformerMap.java index c28fee3f3..2c8ea5371 100644 --- a/src/java/org/apache/commons/math/util/TransformerMap.java +++ b/src/java/org/apache/commons/math/util/TransformerMap.java @@ -34,8 +34,8 @@ import org.apache.commons.math.MathException; public class TransformerMap implements NumberTransformer, Serializable { /** Serializable version identifier */ - private static final long serialVersionUID = -942772950698439883L; - + private static final long serialVersionUID = 4605318041528645258L; + /** * A default Number Transformer for Numbers and numeric Strings. */ @@ -44,13 +44,13 @@ public class TransformerMap implements NumberTransformer, Serializable { /** * The internal Map. */ - private Map map = null; + private Map, NumberTransformer> map = null; /** - * + * Build a map containing only the default transformer. */ public TransformerMap() { - map = new HashMap(); + map = new HashMap, NumberTransformer>(); defaultTransformer = new DefaultTransformer(); } @@ -59,7 +59,7 @@ public class TransformerMap implements NumberTransformer, Serializable { * @param key Class to check * @return true|false */ - public boolean containsClass(Class key) { + public boolean containsClass(Class key) { return map.containsKey(key); } @@ -78,7 +78,7 @@ public class TransformerMap implements NumberTransformer, Serializable { * @param key The Class of the object * @return the mapped NumberTransformer or null. */ - public NumberTransformer getTransformer(Class key) { + public NumberTransformer getTransformer(Class key) { return (NumberTransformer) map.get(key); } @@ -90,7 +90,7 @@ public class TransformerMap implements NumberTransformer, Serializable { * @param transformer The NumberTransformer * @return the replaced transformer if one is present */ - public Object putTransformer(Class key, NumberTransformer transformer) { + public NumberTransformer putTransformer(Class key, NumberTransformer transformer) { return map.put(key, transformer); } @@ -100,7 +100,7 @@ public class TransformerMap implements NumberTransformer, Serializable { * @return the removed transformer if one is present or * null if none was present. */ - public Object removeTransformer(Class key) { + public NumberTransformer removeTransformer(Class key) { return map.remove(key); } @@ -115,7 +115,7 @@ public class TransformerMap implements NumberTransformer, Serializable { * Returns the Set of Classes used as keys in the map. * @return Set of Classes */ - public Set classes() { + public Set> classes() { return map.keySet(); } @@ -124,7 +124,7 @@ public class TransformerMap implements NumberTransformer, Serializable { * in the map. * @return Set of NumberTransformers */ - public Collection transformers() { + public Collection transformers() { return map.values(); } diff --git a/src/mantissa/src/org/spaceroots/mantissa/MantissaException.java b/src/mantissa/src/org/spaceroots/mantissa/MantissaException.java index 59c853d72..4cf4286bb 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/MantissaException.java +++ b/src/mantissa/src/org/spaceroots/mantissa/MantissaException.java @@ -32,7 +32,7 @@ import java.util.MissingResourceException; * standard exceptions are thrown rather than the mantissa specific * ones.

- * @version $Id: MantissaException.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/MessagesResources.java b/src/mantissa/src/org/spaceroots/mantissa/MessagesResources.java index b11c79907..900650a82 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/MessagesResources.java +++ b/src/mantissa/src/org/spaceroots/mantissa/MessagesResources.java @@ -20,7 +20,7 @@ package org.spaceroots.mantissa; import java.util.ListResourceBundle; /** This class gather the message resources for the mantissa library. - * @version $Id: MessagesResources.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/MessagesResources_fr.java b/src/mantissa/src/org/spaceroots/mantissa/MessagesResources_fr.java index 8aa7aa0e5..c170be157 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/MessagesResources_fr.java +++ b/src/mantissa/src/org/spaceroots/mantissa/MessagesResources_fr.java @@ -20,7 +20,7 @@ package org.spaceroots.mantissa; import java.util.ListResourceBundle; /** This class gather the message resources for the mantissa library. - * @version $Id: MessagesResources_fr.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ public class MessagesResources_fr diff --git a/src/mantissa/src/org/spaceroots/mantissa/algebra/Chebyshev.java b/src/mantissa/src/org/spaceroots/mantissa/algebra/Chebyshev.java index d444bc893..089b89925 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/algebra/Chebyshev.java +++ b/src/mantissa/src/org/spaceroots/mantissa/algebra/Chebyshev.java @@ -28,7 +28,7 @@ package org.spaceroots.mantissa.algebra; * Tk+1(X) = 2X Tk(X) - Tk-1(X) *

- * @version $Id: Chebyshev.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/algebra/Hermite.java b/src/mantissa/src/org/spaceroots/mantissa/algebra/Hermite.java index 218f679d6..37d56a35e 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/algebra/Hermite.java +++ b/src/mantissa/src/org/spaceroots/mantissa/algebra/Hermite.java @@ -28,7 +28,7 @@ package org.spaceroots.mantissa.algebra; * Hk+1(X) = 2X Hk(X) - 2k Hk-1(X) *

- * @version $Id: Hermite.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/algebra/Laguerre.java b/src/mantissa/src/org/spaceroots/mantissa/algebra/Laguerre.java index 3e4442f8d..d3c5f41f1 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/algebra/Laguerre.java +++ b/src/mantissa/src/org/spaceroots/mantissa/algebra/Laguerre.java @@ -28,7 +28,7 @@ package org.spaceroots.mantissa.algebra; * (k+1) Lk+1(X) = (2k + 1 - X) Lk(X) - k Lk-1(X) *

- * @version $Id: Laguerre.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/algebra/Legendre.java b/src/mantissa/src/org/spaceroots/mantissa/algebra/Legendre.java index 7d2e0ceff..8288d68f8 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/algebra/Legendre.java +++ b/src/mantissa/src/org/spaceroots/mantissa/algebra/Legendre.java @@ -28,7 +28,7 @@ package org.spaceroots.mantissa.algebra; * (k+1) Pk+1(X) = (2k+1) X Pk(X) - k Pk-1(X) *

- * @version $Id: Legendre.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/algebra/OrthogonalPolynomial.java b/src/mantissa/src/org/spaceroots/mantissa/algebra/OrthogonalPolynomial.java index 149069a7e..32264ab04 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/algebra/OrthogonalPolynomial.java +++ b/src/mantissa/src/org/spaceroots/mantissa/algebra/OrthogonalPolynomial.java @@ -30,7 +30,7 @@ package org.spaceroots.mantissa.algebra; * a4,k are simple expressions which either are * constants or depend on k.

- * @version $Id: OrthogonalPolynomial.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/algebra/Polynomial.java b/src/mantissa/src/org/spaceroots/mantissa/algebra/Polynomial.java index b3d7f84d3..9f81ca186 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/algebra/Polynomial.java +++ b/src/mantissa/src/org/spaceroots/mantissa/algebra/Polynomial.java @@ -36,7 +36,7 @@ import java.util.Arrays; *

Instances of this class are immutable.

- * @version $Id: Polynomial.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ @@ -818,7 +818,7 @@ public abstract class Polynomial implements Serializable { System.arraycopy((a.length < p.a.length) ? p.a : a, lowLength, newA, lowLength, highLength - lowLength); - return new Double(newA); + return Double.valueOf(newA); } @@ -845,7 +845,7 @@ public abstract class Polynomial implements Serializable { System.arraycopy(a, lowLength, newA, lowLength, highLength - lowLength); } - return new Double(newA); + return Double.valueOf(newA); } @@ -857,7 +857,7 @@ public abstract class Polynomial implements Serializable { for (int i = 0; i < a.length; ++i) { newA[i] = -a[i]; } - return new Double(newA); + return Double.valueOf(newA); } /** Multiply the instance by a polynomial. @@ -877,7 +877,7 @@ public abstract class Polynomial implements Serializable { } } - return new Double(newA); + return Double.valueOf(newA); } @@ -904,14 +904,14 @@ public abstract class Polynomial implements Serializable { public Polynomial multiply(double r) { if (r == 0) { - return new Double(new double[] { 0.0 }); + return Double.valueOf(new double[] { 0.0 }); } double[] newA = new double[a.length]; for (int i = 0; i < a.length; ++i) { newA[i] = a[i] * r; } - return new Double(newA); + return Double.valueOf(newA); } @@ -934,13 +934,13 @@ public abstract class Polynomial implements Serializable { */ public Polynomial getDerivative() { if (a.length == 1) { - return new Double(); + return Double.valueOf(); } double[] newA = new double[a.length - 1]; for (int i = 1; i < a.length; ++i) { newA[i - 1] = a[i] * i; } - return new Double(newA); + return Double.valueOf(newA); } /** Returns a string representation of the polynomial. diff --git a/src/mantissa/src/org/spaceroots/mantissa/algebra/PolynomialFraction.java b/src/mantissa/src/org/spaceroots/mantissa/algebra/PolynomialFraction.java index 074da9a0f..739907420 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/algebra/PolynomialFraction.java +++ b/src/mantissa/src/org/spaceroots/mantissa/algebra/PolynomialFraction.java @@ -25,7 +25,7 @@ import java.math.BigInteger; * rational coefficients. *

Instances of this class are immutable.

- * @version $Id: PolynomialFraction.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/algebra/RationalNumber.java b/src/mantissa/src/org/spaceroots/mantissa/algebra/RationalNumber.java index fe787f16c..c39848fe8 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/algebra/RationalNumber.java +++ b/src/mantissa/src/org/spaceroots/mantissa/algebra/RationalNumber.java @@ -24,7 +24,7 @@ import java.math.BigInteger; * This class implements reduced rational numbers. *

Instances of this class are immutable.

- * @version $Id: RationalNumber.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/fitting/AbstractCurveFitter.java b/src/mantissa/src/org/spaceroots/mantissa/fitting/AbstractCurveFitter.java index 05920f01f..049a0c109 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/fitting/AbstractCurveFitter.java +++ b/src/mantissa/src/org/spaceroots/mantissa/fitting/AbstractCurveFitter.java @@ -31,7 +31,7 @@ import org.spaceroots.mantissa.estimation.*; * sub-classes to define the precise shape of the curve they * represent.

- * @version $Id: AbstractCurveFitter.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/fitting/F2FP2Iterator.java b/src/mantissa/src/org/spaceroots/mantissa/fitting/F2FP2Iterator.java index 6fe6082d4..2ee76238d 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/fitting/F2FP2Iterator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/fitting/F2FP2Iterator.java @@ -33,7 +33,7 @@ import org.spaceroots.mantissa.functions.vectorial.VectorialValuedPair; * @see FFPIterator * @see HarmonicCoefficientsGuesser - * @version $Id: F2FP2Iterator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/fitting/FFPIterator.java b/src/mantissa/src/org/spaceroots/mantissa/fitting/FFPIterator.java index b483ad1eb..f70dca326 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/fitting/FFPIterator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/fitting/FFPIterator.java @@ -33,7 +33,7 @@ import org.spaceroots.mantissa.functions.vectorial.VectorialValuedPair; * @see F2FP2Iterator * @see HarmonicCoefficientsGuesser - * @version $Id: FFPIterator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/fitting/HarmonicCoefficientsGuesser.java b/src/mantissa/src/org/spaceroots/mantissa/fitting/HarmonicCoefficientsGuesser.java index 4116241e4..567915416 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/fitting/HarmonicCoefficientsGuesser.java +++ b/src/mantissa/src/org/spaceroots/mantissa/fitting/HarmonicCoefficientsGuesser.java @@ -127,7 +127,7 @@ import org.spaceroots.mantissa.estimation.EstimationException; * estimations, these operations run in O(n) time, where n is the * number of measurements.

- * @version $Id: HarmonicCoefficientsGuesser.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/fitting/HarmonicFitter.java b/src/mantissa/src/org/spaceroots/mantissa/fitting/HarmonicFitter.java index 9c98392ad..5c6790eb7 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/fitting/HarmonicFitter.java +++ b/src/mantissa/src/org/spaceroots/mantissa/fitting/HarmonicFitter.java @@ -35,7 +35,7 @@ import org.spaceroots.mantissa.functions.FunctionException; *

This class is by no means optimized, neither versus * space nor versus time performance.

- * @version $Id: HarmonicFitter.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ @@ -73,65 +73,6 @@ public class HarmonicFitter firstGuessNeeded = false; } - /** - * Simple constructor. - * @param maxIterations maximum number of iterations allowed - * @param convergence criterion threshold below which we do not need - * to improve the criterion anymore - * @param steadyStateThreshold steady state detection threshold, the - * problem has reached a steady state (read converged) if - * Math.abs (Jn - Jn-1) < Jn * convergence, where - * Jn and Jn-1 are the current and - * preceding criterion value (square sum of the weighted residuals - * of considered measurements). - * @param epsilon threshold under which the matrix of the linearized - * problem is considered singular (see {@link - * org.spaceroots.mantissa.linalg.SquareMatrix#solve( - * org.spaceroots.mantissa.linalg.Matrix,double) SquareMatrix.solve}). - * @deprecated replaced by {@link #HarmonicFitter(Estimator)} - * as of version 7.0 - */ - public HarmonicFitter(int maxIterations, double convergence, - double steadyStateThreshold, double epsilon) { - this(new GaussNewtonEstimator(maxIterations, convergence, - steadyStateThreshold, epsilon)); - } - - /** - * Simple constructor. - - *

This constructor can be used when a first estimate of the - * coefficients is already known.

- - * @param coefficients first estimate of the coefficients. - * A reference to this array is hold by the newly created - * object. Its elements will be adjusted during the fitting process - * and they will be set to the adjusted coefficients at the end. - * @param maxIterations maximum number of iterations allowed - * @param convergence criterion threshold below which we do not need - * to improve the criterion anymore - * @param steadyStateThreshold steady state detection threshold, the - * problem has reached a steady state (read converged) if - * Math.abs (Jn - Jn-1) < Jn * convergence, where - * Jn and Jn-1 are the current and - * preceding criterion value (square sum of the weighted residuals - * of considered measurements). - * @param epsilon threshold under which the matrix of the linearized - * problem is considered singular (see {@link - * org.spaceroots.mantissa.linalg.SquareMatrix#solve( - * org.spaceroots.mantissa.linalg.Matrix,double) SquareMatrix.solve}). - - * @deprecated replaced by {@link #HarmonicFitter(EstimatedParameter[], - * Estimator)} as of version 7.0 - */ - public HarmonicFitter(EstimatedParameter[] coefficients, - int maxIterations, double convergence, - double steadyStateThreshold, double epsilon) { - this(coefficients, - new GaussNewtonEstimator(maxIterations, convergence, - steadyStateThreshold, epsilon)); - } - public double[] fit() throws EstimationException { if (firstGuessNeeded) { diff --git a/src/mantissa/src/org/spaceroots/mantissa/fitting/PolynomialCoefficient.java b/src/mantissa/src/org/spaceroots/mantissa/fitting/PolynomialCoefficient.java index 20ce5fd94..17de92e81 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/fitting/PolynomialCoefficient.java +++ b/src/mantissa/src/org/spaceroots/mantissa/fitting/PolynomialCoefficient.java @@ -25,7 +25,7 @@ import org.spaceroots.mantissa.estimation.EstimatedParameter; * @see PolynomialFitter - * @version $Id: PolynomialCoefficient.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/fitting/PolynomialFitter.java b/src/mantissa/src/org/spaceroots/mantissa/fitting/PolynomialFitter.java index 4178a6af9..33c40afe1 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/fitting/PolynomialFitter.java +++ b/src/mantissa/src/org/spaceroots/mantissa/fitting/PolynomialFitter.java @@ -32,7 +32,7 @@ import org.spaceroots.mantissa.estimation.GaussNewtonEstimator; * @see PolynomialCoefficient - * @version $Id: PolynomialFitter.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ @@ -78,78 +78,6 @@ public class PolynomialFitter super(coefficients, estimator); } - /** Simple constructor. - - *

The polynomial fitter built this way are complete polynoms, - * ie. a n-degree polynom has n+1 coefficients. In order to build - * fitter for sparse polynoms (for example a x^20 - b - * x^30, on should first build the coefficients array and - * provide it to {@link - * #PolynomialFitter(PolynomialCoefficient[], int, double, double, - * double)}.

- * @param degree maximal degree of the polynom - * @param maxIterations maximum number of iterations allowed - * @param convergence criterion threshold below which we do not need - * to improve the criterion anymore - * @param steadyStateThreshold steady state detection threshold, the - * problem has reached a steady state (read converged) if - * Math.abs (Jn - Jn-1) < Jn * convergence, where - * Jn and Jn-1 are the current and - * preceding criterion value (square sum of the weighted residuals - * of considered measurements). - * @param epsilon threshold under which the matrix of the linearized - * problem is considered singular (see {@link - * org.spaceroots.mantissa.linalg.SquareMatrix#solve( - * org.spaceroots.mantissa.linalg.Matrix,double) SquareMatrix.solve}). - - * @deprecated replaced by {@link #PolynomialFitter(int,Estimator)} - * as of version 7.0 - */ - public PolynomialFitter(int degree, - int maxIterations, double convergence, - double steadyStateThreshold, double epsilon) { - this(degree, - new GaussNewtonEstimator(maxIterations, steadyStateThreshold, - convergence, epsilon)); - } - - /** Simple constructor. - - *

This constructor can be used either when a first estimate of - * the coefficients is already known (which is of little interest - * because the fit cost is the same whether a first guess is known or - * not) or when one needs to handle sparse polynoms like a - * x^20 - b x^30.

- - * @param coefficients first estimate of the coefficients. - * A reference to this array is hold by the newly created - * object. Its elements will be adjusted during the fitting process - * and they will be set to the adjusted coefficients at the end. - * @param maxIterations maximum number of iterations allowed - * @param convergence criterion threshold below which we do not need - * to improve the criterion anymore - * @param steadyStateThreshold steady state detection threshold, the - * problem has reached a steady state (read converged) if - * Math.abs (Jn - Jn-1) < Jn * convergence, where - * Jn and Jn-1 are the current and - * preceding criterion value (square sum of the weighted residuals - * of considered measurements). - * @param epsilon threshold under which the matrix of the linearized - * problem is considered singular (see {@link - * org.spaceroots.mantissa.linalg.SquareMatrix#solve( - * org.spaceroots.mantissa.linalg.Matrix,double) SquareMatrix.solve}). - - * @deprecated replaced by {@link #PolynomialFitter(PolynomialCoefficient[], - * Estimator)} as of version 7.0 - */ - public PolynomialFitter(PolynomialCoefficient[] coefficients, - int maxIterations, double convergence, - double steadyStateThreshold, double epsilon) { - this(coefficients, - new GaussNewtonEstimator(maxIterations, steadyStateThreshold, - convergence, epsilon)); - } - /** Get the value of the function at x according to the current parameters value. * @param x abscissa at which the theoretical value is requested * @return theoretical value at x diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/ExhaustedSampleException.java b/src/mantissa/src/org/spaceroots/mantissa/functions/ExhaustedSampleException.java index bcc09eb15..0993fed59 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/ExhaustedSampleException.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/ExhaustedSampleException.java @@ -21,7 +21,7 @@ import org.spaceroots.mantissa.MantissaException; /** This class represents exceptions thrown by sample iterators. - * @version $Id: ExhaustedSampleException.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/FunctionException.java b/src/mantissa/src/org/spaceroots/mantissa/functions/FunctionException.java index 6422b97a2..88daabffe 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/FunctionException.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/FunctionException.java @@ -21,7 +21,7 @@ import org.spaceroots.mantissa.MantissaException; /** This class represents exceptions thrown by scalar functions. - * @version $Id: FunctionException.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/BasicSampledFunctionIterator.java b/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/BasicSampledFunctionIterator.java index 7da7d2de8..54efe3bd1 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/BasicSampledFunctionIterator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/BasicSampledFunctionIterator.java @@ -31,7 +31,7 @@ import org.spaceroots.mantissa.functions.ExhaustedSampleException; * @see SampledFunction - * @version $Id: BasicSampledFunctionIterator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/ComputableFunction.java b/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/ComputableFunction.java index f03fd53ab..2ef3108d7 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/ComputableFunction.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/ComputableFunction.java @@ -40,7 +40,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * @see org.spaceroots.mantissa.quadrature.scalar.ComputableFunctionIntegrator * @see SampledFunction - * @version $Id: ComputableFunction.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/ComputableFunctionSampler.java b/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/ComputableFunctionSampler.java index 0f06cb6de..e834d5d51 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/ComputableFunctionSampler.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/ComputableFunctionSampler.java @@ -43,7 +43,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * @see ComputableFunction - * @version $Id: ComputableFunctionSampler.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/SampledFunction.java b/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/SampledFunction.java index 32d083560..682181afb 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/SampledFunction.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/SampledFunction.java @@ -43,7 +43,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * @see ComputableFunctionSampler * @see ComputableFunction - * @version $Id: SampledFunction.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/SampledFunctionIterator.java b/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/SampledFunctionIterator.java index d35a7b49b..549a91810 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/SampledFunctionIterator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/SampledFunctionIterator.java @@ -25,7 +25,7 @@ import org.spaceroots.mantissa.functions.ExhaustedSampleException; * @see SampledFunction - * @version $Id: SampledFunctionIterator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/ScalarValuedPair.java b/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/ScalarValuedPair.java index 9a29d8c38..e3c6e2f3c 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/ScalarValuedPair.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/scalar/ScalarValuedPair.java @@ -29,7 +29,7 @@ import java.io.Serializable; * @see SampledFunction * @see org.spaceroots.mantissa.functions.vectorial.VectorialValuedPair - * @version $Id: ScalarValuedPair.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/BasicSampledFunctionIterator.java b/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/BasicSampledFunctionIterator.java index 781116a08..82d832ff3 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/BasicSampledFunctionIterator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/BasicSampledFunctionIterator.java @@ -31,7 +31,7 @@ import org.spaceroots.mantissa.functions.ExhaustedSampleException; * @see SampledFunction - * @version $Id: BasicSampledFunctionIterator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/ComputableFunction.java b/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/ComputableFunction.java index 64028c486..65456a3bd 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/ComputableFunction.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/ComputableFunction.java @@ -40,7 +40,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * @see org.spaceroots.mantissa.quadrature.vectorial.ComputableFunctionIntegrator * @see SampledFunction - * @version $Id: ComputableFunction.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/ComputableFunctionSampler.java b/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/ComputableFunctionSampler.java index 64670d7e5..858622c18 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/ComputableFunctionSampler.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/ComputableFunctionSampler.java @@ -43,7 +43,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * @see ComputableFunction - * @version $Id: ComputableFunctionSampler.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/SampledFunction.java b/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/SampledFunction.java index 74d75c5a6..f9e21311c 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/SampledFunction.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/SampledFunction.java @@ -43,7 +43,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * @see ComputableFunctionSampler * @see ComputableFunction - * @version $Id: SampledFunction.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/SampledFunctionIterator.java b/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/SampledFunctionIterator.java index a580a7eed..2f5fd5c04 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/SampledFunctionIterator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/SampledFunctionIterator.java @@ -25,7 +25,7 @@ import org.spaceroots.mantissa.functions.ExhaustedSampleException; * @see SampledFunction - * @version $Id: SampledFunctionIterator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/VectorialValuedPair.java b/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/VectorialValuedPair.java index eec26f26c..ed12e472a 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/VectorialValuedPair.java +++ b/src/mantissa/src/org/spaceroots/mantissa/functions/vectorial/VectorialValuedPair.java @@ -29,7 +29,7 @@ import java.io.Serializable; * @see SampledFunction * @see org.spaceroots.mantissa.functions.vectorial.VectorialValuedPair - * @version $Id: VectorialValuedPair.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/linalg/DiagonalMatrix.java b/src/mantissa/src/org/spaceroots/mantissa/linalg/DiagonalMatrix.java index efb81d76a..803ac6fcc 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/linalg/DiagonalMatrix.java +++ b/src/mantissa/src/org/spaceroots/mantissa/linalg/DiagonalMatrix.java @@ -19,7 +19,7 @@ package org.spaceroots.mantissa.linalg; /** This class implements diagonal matrices of linear algebra. - * @version $Id: DiagonalMatrix.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/linalg/GeneralMatrix.java b/src/mantissa/src/org/spaceroots/mantissa/linalg/GeneralMatrix.java index 61f6c720a..58779bc96 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/linalg/GeneralMatrix.java +++ b/src/mantissa/src/org/spaceroots/mantissa/linalg/GeneralMatrix.java @@ -22,7 +22,7 @@ package org.spaceroots.mantissa.linalg; *

This class is the basic implementation of matrices to use when * nothing special is known about the structure of the matrix.

- * @version $Id: GeneralMatrix.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/linalg/GeneralSquareMatrix.java b/src/mantissa/src/org/spaceroots/mantissa/linalg/GeneralSquareMatrix.java index 11a4e2e66..f60c68c2f 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/linalg/GeneralSquareMatrix.java +++ b/src/mantissa/src/org/spaceroots/mantissa/linalg/GeneralSquareMatrix.java @@ -19,7 +19,7 @@ package org.spaceroots.mantissa.linalg; /** This class implements general square matrices of linear algebra. - * @version $Id: GeneralSquareMatrix.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/linalg/LowerTriangularMatrix.java b/src/mantissa/src/org/spaceroots/mantissa/linalg/LowerTriangularMatrix.java index f6712cfbe..7b1dee89b 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/linalg/LowerTriangularMatrix.java +++ b/src/mantissa/src/org/spaceroots/mantissa/linalg/LowerTriangularMatrix.java @@ -19,7 +19,7 @@ package org.spaceroots.mantissa.linalg; /** This class implements lower triangular matrices of linear algebra. - * @version $Id: LowerTriangularMatrix.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/linalg/Matrix.java b/src/mantissa/src/org/spaceroots/mantissa/linalg/Matrix.java index 1aa3c847c..8fcefdb14 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/linalg/Matrix.java +++ b/src/mantissa/src/org/spaceroots/mantissa/linalg/Matrix.java @@ -35,7 +35,7 @@ import java.io.Serializable; * specific shape to the general algorithms implemented by this * abstract class.

- * @version $Id: Matrix.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/linalg/MatrixFactory.java b/src/mantissa/src/org/spaceroots/mantissa/linalg/MatrixFactory.java index ba40ea02a..4648deae5 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/linalg/MatrixFactory.java +++ b/src/mantissa/src/org/spaceroots/mantissa/linalg/MatrixFactory.java @@ -25,7 +25,7 @@ package org.spaceroots.mantissa.linalg; *

This is a utility class, no instance of this class should be * built, so the constructor is explicitly made private.

- * @version $Id: MatrixFactory.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/linalg/NonNullRange.java b/src/mantissa/src/org/spaceroots/mantissa/linalg/NonNullRange.java index 51a95efcf..521e0e0f9 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/linalg/NonNullRange.java +++ b/src/mantissa/src/org/spaceroots/mantissa/linalg/NonNullRange.java @@ -47,7 +47,7 @@ import java.io.Serializable; * coincidence. Therefore, the range (in row/columns count) * corresponding to third row will span from 0 to 2, not from 1 to 2.

- * @version $Id: NonNullRange.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/linalg/SingularMatrixException.java b/src/mantissa/src/org/spaceroots/mantissa/linalg/SingularMatrixException.java index 302a9d2d7..308288926 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/linalg/SingularMatrixException.java +++ b/src/mantissa/src/org/spaceroots/mantissa/linalg/SingularMatrixException.java @@ -21,7 +21,7 @@ import org.spaceroots.mantissa.MantissaException; /** This class represent exceptions thrown by some matrix operations. - * @version $Id: SingularMatrixException.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/linalg/SquareMatrix.java b/src/mantissa/src/org/spaceroots/mantissa/linalg/SquareMatrix.java index d1edae766..617a3ab2b 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/linalg/SquareMatrix.java +++ b/src/mantissa/src/org/spaceroots/mantissa/linalg/SquareMatrix.java @@ -23,7 +23,7 @@ package org.spaceroots.mantissa.linalg; * implementations. It extends the {@link Matrix} class with methods * specific to square matrices.

- * @version $Id: SquareMatrix.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/linalg/SymetricalMatrix.java b/src/mantissa/src/org/spaceroots/mantissa/linalg/SymetricalMatrix.java index 974ee4258..a8d9fcbe6 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/linalg/SymetricalMatrix.java +++ b/src/mantissa/src/org/spaceroots/mantissa/linalg/SymetricalMatrix.java @@ -19,7 +19,7 @@ package org.spaceroots.mantissa.linalg; /** This class implements symetrical matrices of linear algebra. - * @version $Id: SymetricalMatrix.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/linalg/UpperTriangularMatrix.java b/src/mantissa/src/org/spaceroots/mantissa/linalg/UpperTriangularMatrix.java index 93cc4bbea..f01cdb397 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/linalg/UpperTriangularMatrix.java +++ b/src/mantissa/src/org/spaceroots/mantissa/linalg/UpperTriangularMatrix.java @@ -19,7 +19,7 @@ package org.spaceroots.mantissa.linalg; /** This class implements upper triangular matrices of linear algebra. - * @version $Id: UpperTriangularMatrix.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/ComputableFunctionIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/ComputableFunctionIntegrator.java index 7716cc5e9..51fa6d510 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/ComputableFunctionIntegrator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/ComputableFunctionIntegrator.java @@ -30,7 +30,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * @see org.spaceroots.mantissa.functions.scalar.ComputableFunction - * @version $Id: ComputableFunctionIntegrator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/EnhancedSimpsonIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/EnhancedSimpsonIntegrator.java index a3d8f035f..cdfd505e0 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/EnhancedSimpsonIntegrator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/EnhancedSimpsonIntegrator.java @@ -29,7 +29,7 @@ import org.spaceroots.mantissa.functions.scalar.SampledFunctionIterator; * points. If it is used on a regular sample, it behaves exactly as a * traditional Simpson integrator.

- * @version $Id: EnhancedSimpsonIntegrator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/EnhancedSimpsonIntegratorSampler.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/EnhancedSimpsonIntegratorSampler.java index 586f93bf3..0715b8401 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/EnhancedSimpsonIntegratorSampler.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/EnhancedSimpsonIntegratorSampler.java @@ -31,7 +31,7 @@ import org.spaceroots.mantissa.functions.ExhaustedSampleException; * @see EnhancedSimpsonIntegrator - * @version $Id: EnhancedSimpsonIntegratorSampler.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/GaussLegendreIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/GaussLegendreIntegrator.java index 281121c70..2852d5c26 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/GaussLegendreIntegrator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/GaussLegendreIntegrator.java @@ -33,7 +33,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * boundary points, which means it can be undefined at these * points.

- * @version $Id: GaussLegendreIntegrator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/RiemannIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/RiemannIntegrator.java index ae61f3271..10489f4ab 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/RiemannIntegrator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/RiemannIntegrator.java @@ -35,7 +35,7 @@ import org.spaceroots.mantissa.functions.scalar.SampledFunctionIterator; * @see TrapezoidIntegrator - * @version $Id: RiemannIntegrator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/RiemannIntegratorSampler.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/RiemannIntegratorSampler.java index dd360be87..9aa3aa9e9 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/RiemannIntegratorSampler.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/RiemannIntegratorSampler.java @@ -35,7 +35,7 @@ import org.spaceroots.mantissa.functions.ExhaustedSampleException; * @see RiemannIntegrator - * @version $Id: RiemannIntegratorSampler.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/SampledFunctionIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/SampledFunctionIntegrator.java index 56a4eafa5..ab7b90d8c 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/SampledFunctionIntegrator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/SampledFunctionIntegrator.java @@ -29,7 +29,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * @see org.spaceroots.mantissa.functions.scalar.SampledFunctionIterator * @see ComputableFunctionIntegrator - * @version $Id: SampledFunctionIntegrator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/TrapezoidIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/TrapezoidIntegrator.java index ce2f3b908..3c0c0d3db 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/TrapezoidIntegrator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/TrapezoidIntegrator.java @@ -26,7 +26,7 @@ import org.spaceroots.mantissa.functions.scalar.SampledFunctionIterator; *

A trapezoid integrator is a very simple one that assumes the * function is linear over the integration step.

- * @version $Id: TrapezoidIntegrator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/TrapezoidIntegratorSampler.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/TrapezoidIntegratorSampler.java index aee1588e7..59572f1a6 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/TrapezoidIntegratorSampler.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/scalar/TrapezoidIntegratorSampler.java @@ -35,7 +35,7 @@ import org.spaceroots.mantissa.functions.ExhaustedSampleException; * @see TrapezoidIntegrator - * @version $Id: TrapezoidIntegratorSampler.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/ComputableFunctionIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/ComputableFunctionIntegrator.java index fb482cc43..863253163 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/ComputableFunctionIntegrator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/ComputableFunctionIntegrator.java @@ -30,7 +30,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * @see org.spaceroots.mantissa.functions.vectorial.ComputableFunction - * @version $Id: ComputableFunctionIntegrator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/EnhancedSimpsonIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/EnhancedSimpsonIntegrator.java index 86d2a6bfe..e228c929f 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/EnhancedSimpsonIntegrator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/EnhancedSimpsonIntegrator.java @@ -29,7 +29,7 @@ import org.spaceroots.mantissa.functions.vectorial.SampledFunctionIterator; * points. If it is used on a regular sample, it behaves exactly as a * traditional Simpson integrator.

- * @version $Id: EnhancedSimpsonIntegrator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/EnhancedSimpsonIntegratorSampler.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/EnhancedSimpsonIntegratorSampler.java index 9b2667bc3..3a6f469b7 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/EnhancedSimpsonIntegratorSampler.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/EnhancedSimpsonIntegratorSampler.java @@ -31,7 +31,7 @@ import org.spaceroots.mantissa.functions.ExhaustedSampleException; * @see EnhancedSimpsonIntegrator - * @version $Id: EnhancedSimpsonIntegratorSampler.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/GaussLegendreIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/GaussLegendreIntegrator.java index 667574e71..6d949acb5 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/GaussLegendreIntegrator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/GaussLegendreIntegrator.java @@ -33,7 +33,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * boundary points, which means it can be undefined at these * points.

- * @version $Id: GaussLegendreIntegrator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/RiemannIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/RiemannIntegrator.java index 6b37c4a88..96207ee14 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/RiemannIntegrator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/RiemannIntegrator.java @@ -35,7 +35,7 @@ import org.spaceroots.mantissa.functions.vectorial.SampledFunctionIterator; * @see TrapezoidIntegrator - * @version $Id: RiemannIntegrator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/RiemannIntegratorSampler.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/RiemannIntegratorSampler.java index 0b19cdf96..f0cd6b436 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/RiemannIntegratorSampler.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/RiemannIntegratorSampler.java @@ -35,7 +35,7 @@ import org.spaceroots.mantissa.functions.ExhaustedSampleException; * @see RiemannIntegrator - * @version $Id: RiemannIntegratorSampler.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/SampledFunctionIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/SampledFunctionIntegrator.java index 4eca2a116..a14724072 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/SampledFunctionIntegrator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/SampledFunctionIntegrator.java @@ -29,7 +29,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * @see org.spaceroots.mantissa.functions.vectorial.SampledFunctionIterator * @see ComputableFunctionIntegrator - * @version $Id: SampledFunctionIntegrator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/TrapezoidIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/TrapezoidIntegrator.java index e40a39afd..83a4cb361 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/TrapezoidIntegrator.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/TrapezoidIntegrator.java @@ -26,7 +26,7 @@ import org.spaceroots.mantissa.functions.vectorial.SampledFunctionIterator; *

A trapezoid integrator is a very simple one that assumes the * function is linear over the integration step.

- * @version $Id: TrapezoidIntegrator.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/TrapezoidIntegratorSampler.java b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/TrapezoidIntegratorSampler.java index 8d7982a45..8526763aa 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/TrapezoidIntegratorSampler.java +++ b/src/mantissa/src/org/spaceroots/mantissa/quadrature/vectorial/TrapezoidIntegratorSampler.java @@ -35,7 +35,7 @@ import org.spaceroots.mantissa.functions.ExhaustedSampleException; * @see TrapezoidIntegrator - * @version $Id: TrapezoidIntegratorSampler.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/random/ScalarSampleStatistics.java b/src/mantissa/src/org/spaceroots/mantissa/random/ScalarSampleStatistics.java index 0a2902dd1..30760abd9 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/random/ScalarSampleStatistics.java +++ b/src/mantissa/src/org/spaceroots/mantissa/random/ScalarSampleStatistics.java @@ -18,7 +18,7 @@ package org.spaceroots.mantissa.random; /** This class compute basic statistics on a scalar sample. - * @version $Id: ScalarSampleStatistics.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ public class ScalarSampleStatistics { diff --git a/src/mantissa/src/org/spaceroots/mantissa/random/VectorialSampleStatistics.java b/src/mantissa/src/org/spaceroots/mantissa/random/VectorialSampleStatistics.java index 5f473e273..0e017dfe4 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/random/VectorialSampleStatistics.java +++ b/src/mantissa/src/org/spaceroots/mantissa/random/VectorialSampleStatistics.java @@ -20,7 +20,7 @@ package org.spaceroots.mantissa.random; import org.spaceroots.mantissa.linalg.SymetricalMatrix; /** This class compute basic statistics on a scalar sample. - * @version $Id: VectorialSampleStatistics.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ public class VectorialSampleStatistics { diff --git a/src/mantissa/src/org/spaceroots/mantissa/roots/BrentSolver.java b/src/mantissa/src/org/spaceroots/mantissa/roots/BrentSolver.java index e5a660c36..ba84a391a 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/roots/BrentSolver.java +++ b/src/mantissa/src/org/spaceroots/mantissa/roots/BrentSolver.java @@ -27,7 +27,7 @@ import org.spaceroots.mantissa.functions.FunctionException; * implementation found at netlib (zeroin.f). - * @version $Id: BrentSolver.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/roots/ConvergenceChecker.java b/src/mantissa/src/org/spaceroots/mantissa/roots/ConvergenceChecker.java index 72c2e98ca..30acc07cd 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/roots/ConvergenceChecker.java +++ b/src/mantissa/src/org/spaceroots/mantissa/roots/ConvergenceChecker.java @@ -25,7 +25,7 @@ package org.spaceroots.mantissa.roots; * to allow the root-finding algorithm to stop its search according to * the problem at hand. - * @version $Id: ConvergenceChecker.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/roots/RootsFinder.java b/src/mantissa/src/org/spaceroots/mantissa/roots/RootsFinder.java index 7c1401a64..c8adfca26 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/roots/RootsFinder.java +++ b/src/mantissa/src/org/spaceroots/mantissa/roots/RootsFinder.java @@ -23,7 +23,7 @@ import org.spaceroots.mantissa.functions.FunctionException; /** This interface specifies root-finding methods for scalar * functions. - * @version $Id: RootsFinder.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/utilities/ArrayMapper.java b/src/mantissa/src/org/spaceroots/mantissa/utilities/ArrayMapper.java index c09e4a4f7..c4fffe7fa 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/utilities/ArrayMapper.java +++ b/src/mantissa/src/org/spaceroots/mantissa/utilities/ArrayMapper.java @@ -28,7 +28,7 @@ import java.util.Iterator; * @see ArraySliceMappable - * @version $Id: ArrayMapper.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/utilities/ArrayMapperEntry.java b/src/mantissa/src/org/spaceroots/mantissa/utilities/ArrayMapperEntry.java index 98ef8cfb1..db21e9527 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/utilities/ArrayMapperEntry.java +++ b/src/mantissa/src/org/spaceroots/mantissa/utilities/ArrayMapperEntry.java @@ -21,7 +21,7 @@ package org.spaceroots.mantissa.utilities; * This class is a simple container for an offset and an * {@link ArraySliceMappable} object. - * @version $Id: ArrayMapperEntry.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/utilities/ArraySliceMappable.java b/src/mantissa/src/org/spaceroots/mantissa/utilities/ArraySliceMappable.java index 4db280c9a..9a3959f96 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/utilities/ArraySliceMappable.java +++ b/src/mantissa/src/org/spaceroots/mantissa/utilities/ArraySliceMappable.java @@ -54,7 +54,7 @@ package org.spaceroots.mantissa.utilities; * @see ArrayMapper * - * @version $Id: ArraySliceMappable.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe * */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/utilities/Interval.java b/src/mantissa/src/org/spaceroots/mantissa/utilities/Interval.java index c49e4bdfc..f8f7d4820 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/utilities/Interval.java +++ b/src/mantissa/src/org/spaceroots/mantissa/utilities/Interval.java @@ -27,7 +27,7 @@ package org.spaceroots.mantissa.utilities; * @see IntervalsList * @author Luc Maisonobe - * @version $Id: Interval.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ */ public class Interval { diff --git a/src/mantissa/src/org/spaceroots/mantissa/utilities/IntervalsList.java b/src/mantissa/src/org/spaceroots/mantissa/utilities/IntervalsList.java index 72199d4a0..14759cc1a 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/utilities/IntervalsList.java +++ b/src/mantissa/src/org/spaceroots/mantissa/utilities/IntervalsList.java @@ -29,7 +29,7 @@ import java.util.Iterator; * intersection.

* @see Interval * @author Luc Maisonobe - * @version $Id: IntervalsList.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ */ public class IntervalsList { diff --git a/src/mantissa/src/org/spaceroots/mantissa/utilities/MappableArray.java b/src/mantissa/src/org/spaceroots/mantissa/utilities/MappableArray.java index 8147b5303..88cc7d188 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/utilities/MappableArray.java +++ b/src/mantissa/src/org/spaceroots/mantissa/utilities/MappableArray.java @@ -21,7 +21,7 @@ package org.spaceroots.mantissa.utilities; * Wrapper class around an array in order to have it implement the * {@link ArraySliceMappable} interface. - * @version $Id: MappableArray.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/mantissa/src/org/spaceroots/mantissa/utilities/MappableScalar.java b/src/mantissa/src/org/spaceroots/mantissa/utilities/MappableScalar.java index 9b5ac4f24..1ecdb92c9 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/utilities/MappableScalar.java +++ b/src/mantissa/src/org/spaceroots/mantissa/utilities/MappableScalar.java @@ -21,7 +21,7 @@ package org.spaceroots.mantissa.utilities; * Wrapper class around a scalar in order to have it implement the * {@link ArraySliceMappable} interface. - * @version $Id: MappableScalar.java 1705 2006-09-17 19:57:39Z luc $ + * @version $Id$ * @author L. Maisonobe */ diff --git a/src/site/site.xml b/src/site/site.xml index 44ffc7cd4..bea25d574 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -27,7 +27,9 @@ - + + type attribute can be add,update,fix,remove. - -Commons Math Release Notes + Commons Math Release Notes - + + + Added support for copying statistics. Changes to stats classes + include copy constructor, static copy(-,-) and instance copy() + methods. Added copy() to UnivariateStatistic and StorelessUnivariateStatistic + interfaces. + + + Added a removal feature for observations in descriptive statistics. + + + Added a scalb method in MathUtils. This method is similar to the method + with same name added in java.lang.Math as of Java 6. + + + Fixed F distribution inverse CDF computation for small denominator degrees of freedom. + + + Fixed an error in CorrelatedRandomVectorGenerator leading to a component of + the generated vector being constant. + + + Greatly improved QR-decomposition speed using transposed matrices internally. + + + Fixed an infinite loop encountered in some backward integration cases for ODE solvers. + + + Added beta distribution. + + + Added probability density functions computation for distributions for which + it is feasible. + + + Changed the Complex.equals() method so that it considers +0 and -0 are equal, + as required by IEEE-754 standard. + + + Added JAMA-like interfaces for eigen/singular problems. The implementation + of eigen-decomposition is based on the very quick dqd/dqds algorithms and some + parts of the MRRR algorithm. This leads to very fast and accurate solutions. + + + Added JAMA-like interfaces for decomposition algorithms. These interfaces + decompose a matrix as a product of several other matrices with predefined + properties and shapes depending on the algorithm. These algorithms allow to + solve the equation A * X = B, either for an exact linear solution + (LU-decomposition) or an exact or least-squares solution (QR-decomposition). + + + Added removeData methods for the SimpleRegression class. This allows + to support regression calculations across a sliding window of (time-based) + observations without having to recalculate for the entire window every time. + + + Support for one dimensional vectors has been added to the linear algebra + package with a RealVector interface, a RealVectorImpl default implementation + using a single double array to store elements and a RealVectorFormat for + input/output. + + + Changed OLS regression implementation added in MATH-203 to use + QR decomposition to solve the normal equations. + + + New ODE integrators have been added: the explicit Adams-Bashforth and implicit + Adams-Moulton multistep methods. These methods support customizable starter + integrators and support discrete events even during the start phase. + All these methods provide the same rich features has the existing ones: + continuous output, step handlers, discrete events, G-stop ... + + + Replaced size adjustment of all steps of fixed steps Runge-Kutta integrators by + a truncation of the last step only. + + + The ODE integrators now support several step handlers at once, instead of just one. + This is more consistent with event handlers management. + The setStepHandler method has therefore been replaced by addStephandler, the + getStepHandler method has been replaced by getStepHandlers which returns a Collection + and a clearStepHandlers method has been added. + + + All step interpolators for ODE integrators now provide interpolation for + both the state and its time derivatives. The interpolated derivatives are + the exact derivatives of the interpolated state, thus preserving consistency. + The general step handlers hence do not need to call the derivation function + anymore. The fixed step handlers also get the time derivative of the state + as an additional argument along with the state when they are called. + + + Changed return type for FirstOrderIntegrator.integrate() to double + in order to retrieve exact stop time. This allows to handle properly + integration interruption due to an EventHandler instance asking to + stop the integration when its associated event is triggered. The state + was already set to the current state at interruption time, but it was + difficult to get the corresponding time (it involved setting a step + handler monitoring the last step specially). + + + Clarified the ODE package by breaking in into several sub-packages and renaming + classes (SwitchingFunctions/EventHandler, SwitchingFunctionsHandler/CombinedEventsManager) + + + Changed return type for FirstOrderIntegrator.getSwitchingFunctions() + to a collection of SwitchingFunction instances. This better suits the + intended use of the method and fixes a visibility error since the + previous return type referenced the package private SwitchState class. + + + Fixed dimension error on output vector for the operate method + in RealMatrixImpl and BigMatrixImpl classes. + + + The FirstOrderDifferentialEquations, FirstOrderIntegrator and FixedStepHandler + interfaces now extends Serializable, allowing integrators, problems and + handlers to be embedded into users Serializable classes. + + + Added several convenience methods and constants for Vector3D and Rotation. + + + Replaced public no argument constructors with IDENTITY or ZERO + static instances for immutable classes Vector3D and Rotation. + + + Fixed inconsistencies in the naming scheme for static fields in + Vector3D and Rotation with respect to the overall library. + + + Greatly improved RealMatrixImpl and BigMatrixImpl performances, + both in terms of speed and in terms of temporary memory footprint. + + + Added Mauro's patch to support multiple regression. + + + Starting with version 2.0 of the library, the minimal version of the Java + platform required to compile and use commons-math is Java 5. This version + is widely deployed now on many systems. It brings new important features + for specific mathematical developments, for example new functions (log10, + cbrt, ulp, signum, cosh, sinh, tanh, hypot, expm1, log1p), autoboxing, + MathContext or RoundingMode. It also brings important features for general + development, for example enums, generics or annotations. + + + Switching functions can now throw dedicated SwitchException from all their + method. At upper call level, the various ODE integrators handle these new + exceptions and wrap them into IntegratorException instances, hence the + integrators methods signature did not change. + + + Added the getSwitchingFunctions and clearSwitchingFunctions to the + FirstOrderIntegrator interface and all its implementations + + + Removed deprecated features. This includes the following changes. Factory-based + instantiation replaced by setter injection in 1.2 in several classes have been + removed. Protected fields in matrices implementations have been declared final + and private. Identity factory methods moved to MatrixUtils class have been + removed. Complex utilities methods that have been superseded by Complex instance + methods have been removed. + + + Fixed formula in fast cosine transformer javadoc comments. @@ -65,9 +228,6 @@ Commons Math Release Notes Added root checks for the endpoints. - - Fixed F distribution inverse CDF computation for small denominator degrees of freedom. - - + +
  • More inference methods
  • -
  • Multiple regression
Linear Algebra
diff --git a/src/site/xdoc/userguide/distribution.xml b/src/site/xdoc/userguide/distribution.xml index ff3fbb928..7015d4758 100644 --- a/src/site/xdoc/userguide/distribution.xml +++ b/src/site/xdoc/userguide/distribution.xml @@ -39,35 +39,6 @@ computation of PDF and CDF probabilities, the framework also allows for the computation of inverse PDF and inverse CDF values.

-

- In order to use the distribution framework, first a distribution object must - be created. It is encouraged that all distribution object creation occurs via - the org.apache.commons.math.distribution.DistributionFactory - class. DistributionFactory is a simple factory used to create all - of the distribution objects supported by Commons-Math. The typical usage of - DistributionFactory to create a distribution object would be: -

- DistributionFactory factory = DistributionFactory.newInstance(); -BinomialDistribution binomial = factory.createBinomialDistribution(10, .75); -

- The distributions that can be instantiated via the DistributionFactory - are detailed below: - - - - - - - - - - - - - - -
DistributionFactory MethodParameters
BinomialcreateBinomialDistribution
Number of trials
Probability of success
CauchycreateCauchyDistribution
Median
Scale
Chi-SquaredcreateChiSquaredDistribution
Degrees of freedom
ExponentialcreateExponentialDistribution
Mean
FcreateFDistribution
Numerator degrees of freedom
Denominator degrees of freedom
GammacreateGammaDistribution
Alpha
Beta
HypergeometriccreateHypergeometricDistribution
Population size
Number of successes in population
Sample size
Normal (Gaussian)createNormalDistribution
Mean
Standard Deviation
PoissoncreatePoissonDistribution
Mean
tcreateTDistribution
Degrees of freedom
WeibullcreateWeibullDistribution
Shape
Scale
Location
PascalcreatePascalDistribution
numberOfSuccesses
probabilityOfSuccess
-

Using a distribution object, PDF and CDF probabilities are easily computed using the cumulativeProbability methods. For a distribution X, diff --git a/src/site/xdoc/userguide/geometry.xml b/src/site/xdoc/userguide/geometry.xml index 9b32f1774..b54f29507 100644 --- a/src/site/xdoc/userguide/geometry.xml +++ b/src/site/xdoc/userguide/geometry.xml @@ -45,10 +45,10 @@

Numerous constructors are available to create vectors. In addition to the straightforward cartesian coordinates constructor, a constructor using - azimuthal coodinates can build normalized vectors and linear constructors + azimuthal coordinates can build normalized vectors and linear constructors from one, two, three or four base vectors are also available. Constants have - been defined for the most commons vectors (plus and minus canonical axes and - null vector). + been defined for the most commons vectors (plus and minus canonical axes, + null vector, and special vectors with infinite or NaN coordinates).

The generic vectorial space operations are available including dot product, @@ -56,6 +56,11 @@ which have a specific meaning in 3D. The 3D geometry specific cross product is of course also implemented.

+

+ + org.apache.commons.math.geometry.Vector3DFormat is a specialized format + for formatting output or parsing input with text representation of 3D vectors. +

@@ -91,7 +96,7 @@ these vectors may vary as well as the semantics of the rotation.

- For example in an spacecraft attitude simulation tool, users will + For example in a spacecraft attitude simulation tool, users will often consider the vectors are fixed (say the Earth direction for example) and the rotation transforms the coordinates coordinates of this vector in inertial frame into the coordinates of the same vector diff --git a/src/site/xdoc/userguide/index.xml b/src/site/xdoc/userguide/index.xml index b39eb0d86..605a5a3ab 100644 --- a/src/site/xdoc/userguide/index.xml +++ b/src/site/xdoc/userguide/index.xml @@ -58,7 +58,8 @@

  • 4. Numerical Analysis diff --git a/src/site/xdoc/userguide/linear.xml b/src/site/xdoc/userguide/linear.xml index 11892b51c..7319885cd 100644 --- a/src/site/xdoc/userguide/linear.xml +++ b/src/site/xdoc/userguide/linear.xml @@ -30,7 +30,8 @@

    Currently, numerical linear algebra support in commons-math is - limited to basic operations on real matrices and solving linear systems. + limited to basic operations on real matrices and vectors and + solving linear systems.

    @@ -39,7 +40,7 @@ RealMatrix interface represents a matrix with real numbers as entries. The following basic matrix operations are supported: