Author: celestin
Date: Fri Nov  9 08:13:18 2012
New Revision: 1407376

URL: http://svn.apache.org/viewvc?rev=1407376&view=rev
Log:
Small java application for the accuracy assessment of implementations of real
functions in Commons Math. The accuracy is assessed through comparison with
reference values computed with multi-precision softwares.

Added:
    commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/
    
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/MANIFEST.txt
   (with props)
    
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/README.txt
   (with props)
    
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.java
   (with props)
    
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.sh
   (with props)

Added: 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/MANIFEST.txt
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/MANIFEST.txt?rev=1407376&view=auto
==============================================================================
--- 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/MANIFEST.txt
 (added)
+++ 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/MANIFEST.txt
 Fri Nov  9 08:13:18 2012
@@ -0,0 +1 @@
+Main-Class: RealFunctionValidation

Propchange: 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/MANIFEST.txt
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/README.txt
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/README.txt?rev=1407376&view=auto
==============================================================================
--- 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/README.txt
 (added)
+++ 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/README.txt
 Fri Nov  9 08:13:18 2012
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+Validation of real functions
+============================
+
+This document details the procedure used in Commons-Math 3 to assess the
+accuracy of the implementations of special functions. It is a two-step process
+
+1. reference values are computed with a multi-precision software (for example,
+   the Maxima Computer Algebra System) [1],
+2. these reference values are compared with the Commons-Math3 implementation.
+   The accuracy is computed in ulps.
+
+This process relies on a small Java application, called RealFunctionValidation,
+which can be found in $CM3_SRC/src/test/maxima/special, where $CM3_SRC is the
+root directory to the source of Commons-Math 3
+
+
+Compilation of RealFunctionValidation
+-------------------------------------
+
+Change to the relevant directory
+
+  cd $CM3_SRC/src/test/maxima/special/RealFunctionValidation
+
+Compile the source file. The jar file of Commons-Math3 should be included in
+your classpath. If it is installed in your local maven repository, the
+following command should work
+
+  javac -classpath 
$HOME/.m2/repository/org/apache/commons/commons-math3/3.1-SNAPSHOT/commons-math3-3.1-SNAPSHOT.jar
 RealFunctionValidation.java
+
+Create a jar file
+
+  jar cfm RealFunctionValidation.jar Manifest.txt RealFunctionValidation*.class
+
+Remove the unused *.class files
+
+  rm *.class
+
+
+Invocation of the application RealFunctionValidation
+----------------------------------------------------
+
+The java application comes with a shell script, RealFunctionValidaton.sh. You
+should edit this file, and change the variables
+- CM3_JAR: full path to the Commons-Math 3 jar file,
+- APP_JAR: full path to the RealFunctionValidation application jar file.
+
+Invoking this application is then very simple. For example, to validate the
+implementation of Gamma.logGamma, change to directory reference
+
+  cd $CM3_SRC/src/test/maxima/special/reference
+
+and run the application
+
+  ../RealFunctionValidation/RealFunctionValidation.sh logGamma.properties
+
+
+Syntax of the *.properties files
+--------------------------------
+
+Parameters of the RealFunctionValidation application are specified through a
+standard Java properties file. The following keys must be specified in this
+file
+
+- method: the fully qualified name to the function to be validated. This
+  function should be static, take only primitive arguments, and return double.
+- signature: this key is necessary to discriminate functions with same name.
+  The signature should be specified as in a plain java file. For example
+  signature = double, int, float
+- inputFileMask: the name of the binary input file(s) containing the
+  high-accuracy reference values. The format of this file is described in
+  the next section. It is possible to specify multiple input files, which are
+  indexed by an integer. Then this key should really be understood as a format
+  string. In other words, the name of the file with index i is given by
+  String.format(inputFileMask, i)
+- outputFileMask: the name of the binary output file(s) containing the
+  reference values, the values computed through the specified method, and
+  the error (in ulps). The format of this file is described in the next 
section.  As for the input files, it is possible to specify multiple output 
files.
+- from: the first index
+- to: the last index (exclusive)
+- by: the increment
+
+As an example, here is the properties file for evaluation of
+double Gamma.logGamma(double)
+
+method=org.apache.commons.math3.special.Gamma.logGamma
+signature=double
+inputFileMask=logGamma-%02d.dat
+outputFileMask=logGamma-out-%02d.dat
+from=1
+to=5
+by=1
+
+Format of the input and output binary files
+-------------------------------------------
+
+The reference values are saved in a binary file
+- for a unary function f(x), the data is stored as follows
+  x[0], f(x[0]), x[1], f(x[1]), ...
+- for a binary function f(x, y), the data is stored as follows
+  x[0], y[0], f(x[0], y[0]), x[1], y[1], f(x[1], y[1]), ...
+- and similar storage pattern for a n-ary function.
+
+The parameters x[i], y[i], ... can be of arbitrary (primitive) type. The return
+value f(x[i], y[i], ...) must be of type double.
+
+The output files are also saved in a binary file
+- for a unary function f(x), the data is stored as follows
+  x[0], reference value of f(x[0]), actual value of f(x[0], y[0]),
+  error in ulps, x[1], y[1], reference value of f(x[1], y[1]), actual value of
+  f(x[1], y[1]), error in ulps, ...
+- for a binary function f(x, y), the data is stored as follows
+  x[0], y[0], reference value of f(x[0], y[0]), actual value of f(x[0], y[0]),
+  error in ulps, x[1], y[1], reference value of f(x[1], y[1]), actual value of
+  f(x[1], y[1]), error in ulps, ...
+
+The application also prints on the standard output some statistics about the
+error.
+
+References
+----------
+
+[1] http://maxima.sourceforge.net/

Propchange: 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/README.txt
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.java?rev=1407376&view=auto
==============================================================================
--- 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.java
 (added)
+++ 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.java
 Fri Nov  9 08:13:18 2012
@@ -0,0 +1,361 @@
+/*
+ * 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.
+ */
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
+import org.apache.commons.math3.util.FastMath;
+
+/*
+ * plot 'logGamma.dat' binary format="%double%double" endian=big u 1:2 w l
+ */
+public class RealFunctionValidation {
+
+    public static class MissingRequiredPropertyException
+        extends IllegalArgumentException {
+
+        private static final long serialVersionUID = 20121017L;
+
+        public MissingRequiredPropertyException(final String key) {
+
+            super("missing required property " + key);
+        }
+    }
+
+    public static class ApplicationProperties {
+
+        private static final int DOT = '.';
+
+        private static final String METHOD_KEY = "method";
+
+        private static final String SIGNATURE_KEY = "signature";
+
+        private static final String INPUT_FILE_MASK = "inputFileMask";
+
+        private static final String OUTPUT_FILE_MASK = "outputFileMask";
+
+        private static final String FROM_KEY = "from";
+
+        private static final String TO_KEY = "to";
+
+        private static final String BY_KEY = "by";
+
+        final Method method;
+
+        final String inputFileMask;
+
+        final String outputFileMask;
+
+        final int from;
+
+        final int to;
+
+        final int by;
+
+        /**
+         * Returns a {@link Method} with specified signature.
+         *
+         * @param className The fully qualified name of the class to which the
+         * method belongs.
+         * @param methodName The name of the method.
+         * @param signature The signature of the method, as a list of parameter
+         * types.
+         * @return the method
+         * @throws SecurityException
+         * @throws ClassNotFoundException
+         */
+        public static Method findStaticMethod(final String className,
+                                              final String methodName,
+                                              final List<Class<?>> signature)
+            throws SecurityException, ClassNotFoundException {
+
+            final int n = signature.size();
+            final Method[] methods = Class.forName(className).getMethods();
+            for (Method method : methods) {
+                if (method.getName().equals(methodName)) {
+                    final Class<?>[] parameters = method.getParameterTypes();
+                    boolean sameSignature = true;
+                    if (parameters.length == n) {
+                        for (int i = 0; i < n; i++) {
+                            sameSignature &= signature.get(i)
+                                .equals(parameters[i]);
+                        }
+                        if (sameSignature) {
+                            final int modifiers = method.getModifiers();
+                            if ((modifiers & Modifier.STATIC) != 0) {
+                                return method;
+                            } else {
+                                final String msg = "method must be static";
+                                throw new IllegalArgumentException(msg);
+                            }
+                        }
+                    }
+                }
+            }
+            throw new IllegalArgumentException("method not found");
+        }
+
+        public static Class<?> parsePrimitiveType(final String type) {
+
+            if (type.equals("boolean")) {
+                return Boolean.TYPE;
+            } else if (type.equals("byte")) {
+                return Byte.TYPE;
+            } else if (type.equals("char")) {
+                return Character.TYPE;
+            } else if (type.equals("double")) {
+                return Double.TYPE;
+            } else if (type.equals("float")) {
+                return Float.TYPE;
+            } else if (type.equals("int")) {
+                return Integer.TYPE;
+            } else if (type.equals("long")) {
+                return Long.TYPE;
+            } else if (type.equals("short")) {
+                return Short.TYPE;
+            } else {
+                final StringBuilder builder = new StringBuilder();
+                builder.append(type).append(" is not a primitive type");
+                throw new IllegalArgumentException(builder.toString());
+            }
+        }
+
+        private static String getPropertyAsString(final Properties properties,
+                                                  final String key) {
+
+            final String value = properties.getProperty(key);
+            if (value == null) {
+                throw new MissingRequiredPropertyException(key);
+            } else {
+                return value;
+            }
+        }
+
+        private static int getPropertyAsInteger(final Properties properties,
+                                                final String key) {
+
+            final String value = properties.getProperty(key);
+            if (value == null) {
+                throw new MissingRequiredPropertyException(key);
+            } else {
+                return Integer.parseInt(value);
+            }
+        }
+
+        private ApplicationProperties(final String fullyQualifiedName,
+                                      final String signature,
+                                      final String inputFileMask,
+                                      final String outputFileMask,
+                                      final int from, final int to, final int 
by) {
+
+            this.inputFileMask = inputFileMask;
+            this.outputFileMask = outputFileMask;
+            this.from = from;
+            this.to = to;
+            this.by = by;
+
+            final String[] types = signature.split(",");
+            final List<Class<?>> parameterTypes = new ArrayList<Class<?>>();
+            for (String type : types) {
+                parameterTypes.add(parsePrimitiveType(type.trim()));
+            }
+            final int index = fullyQualifiedName.lastIndexOf(DOT);
+            try {
+                final String className, methodName;
+                className = fullyQualifiedName.substring(0, index);
+                methodName = fullyQualifiedName.substring(index + 1);
+                this.method = findStaticMethod(className, methodName,
+                                               parameterTypes);
+            } catch (ClassNotFoundException e) {
+                throw new IllegalArgumentException(e);
+            }
+
+        }
+
+        public static final ApplicationProperties create(final Properties 
properties) {
+
+            final String methodFullyQualifiedName;
+            methodFullyQualifiedName = getPropertyAsString(properties,
+                                                           METHOD_KEY);
+
+            final String signature;
+            signature = getPropertyAsString(properties, SIGNATURE_KEY);
+
+            final String inputFileMask;
+            inputFileMask = getPropertyAsString(properties, INPUT_FILE_MASK);
+
+            final String outputFileMask;
+            outputFileMask = getPropertyAsString(properties, OUTPUT_FILE_MASK);
+
+            final int from = getPropertyAsInteger(properties, FROM_KEY);
+            final int to = getPropertyAsInteger(properties, TO_KEY);
+            final int by = getPropertyAsInteger(properties, BY_KEY);
+
+            return new ApplicationProperties(methodFullyQualifiedName,
+                                             signature, inputFileMask,
+                                             outputFileMask, from, to, by);
+        }
+    };
+
+    public static Object readAndWritePrimitiveValue(final DataInputStream in,
+                                                    final DataOutputStream out,
+                                                    final Class<?> type)
+        throws IOException {
+
+        if (!type.isPrimitive()) {
+            throw new IllegalArgumentException("type must be primitive");
+        }
+        if (type.equals(Boolean.TYPE)) {
+            final boolean x = in.readBoolean();
+            out.writeBoolean(x);
+            return Boolean.valueOf(x);
+        } else if (type.equals(Byte.TYPE)) {
+            final byte x = in.readByte();
+            out.writeByte(x);
+            return Byte.valueOf(x);
+        } else if (type.equals(Character.TYPE)) {
+            final char x = in.readChar();
+            out.writeChar(x);
+            return Character.valueOf(x);
+        } else if (type.equals(Double.TYPE)) {
+            final double x = in.readDouble();
+            out.writeDouble(x);
+            return Double.valueOf(x);
+        } else if (type.equals(Float.TYPE)) {
+            final float x = in.readFloat();
+            out.writeFloat(x);
+            return Float.valueOf(x);
+        } else if (type.equals(Integer.TYPE)) {
+            final int x = in.readInt();
+            out.writeInt(x);
+            return Integer.valueOf(x);
+        } else if (type.equals(Long.TYPE)) {
+            final long x = in.readLong();
+            out.writeLong(x);
+            return Long.valueOf(x);
+        } else if (type.equals(Short.TYPE)) {
+            final short x = in.readShort();
+            out.writeShort(x);
+            return Short.valueOf(x);
+        } else {
+            // This should never occur.
+            throw new IllegalStateException();
+        }
+    }
+
+    public static SummaryStatistics assessAccuracy(final Method method,
+                                                   final DataInputStream in,
+                                                   final DataOutputStream out)
+        throws IOException, IllegalAccessException, IllegalArgumentException,
+        InvocationTargetException {
+
+        if (method.getReturnType() != Double.TYPE) {
+            throw new IllegalArgumentException("method must return a double");
+        }
+
+        final Class<?>[] types = method.getParameterTypes();
+        for (int i = 0; i < types.length; i++) {
+            if (!types[i].isPrimitive()) {
+                final StringBuilder builder = new StringBuilder();
+                builder.append("argument #").append(i + 1)
+                    .append(" of method ").append(method.getName())
+                    .append("must be of primitive of type");
+                throw new IllegalArgumentException(builder.toString());
+            }
+        }
+
+        final SummaryStatistics stat = new SummaryStatistics();
+        final Object[] parameters = new Object[types.length];
+        while (true) {
+            try {
+                for (int i = 0; i < parameters.length; i++) {
+                    parameters[i] = readAndWritePrimitiveValue(in, out,
+                                                               types[i]);
+                }
+                final double expected = in.readDouble();
+                if (FastMath.abs(expected) > 1E-16) {
+                    final Object value = method.invoke(null, parameters);
+                    final double actual = ((Double) value).doubleValue();
+                    final double err = FastMath.abs(actual - expected);
+                    final double ulps = err / FastMath.ulp(expected);
+                    out.writeDouble(expected);
+                    out.writeDouble(actual);
+                    out.writeDouble(ulps);
+                    stat.addValue(ulps);
+                }
+            } catch (EOFException e) {
+                break;
+            }
+        }
+        return stat;
+    }
+
+    public static void run(final ApplicationProperties properties)
+        throws IllegalAccessException, IllegalArgumentException,
+        InvocationTargetException, IOException {
+
+        for (int i = properties.from; i < properties.to; i += properties.by) {
+            final String inputFileName;
+            inputFileName = String.format(properties.inputFileMask, i);
+            final String outputFileName;
+            outputFileName = String.format(properties.outputFileMask, i);
+
+            final DataInputStream in;
+            in = new DataInputStream(new FileInputStream(inputFileName));
+            final DataOutputStream out;
+            out = new DataOutputStream(new FileOutputStream(outputFileName));
+
+            final SummaryStatistics stats;
+            stats = assessAccuracy(properties.method, in, out);
+
+            System.out.println("input file name = " + inputFileName);
+            System.out.println("output file name = " + outputFileName);
+            System.out.println(stats);
+        }
+    }
+
+    public static void main(final String[] args)
+        throws IOException, IllegalAccessException, IllegalArgumentException,
+        InvocationTargetException {
+
+        if (args.length == 0) {
+            final String msg = "missing required properties file";
+            throw new IllegalArgumentException(msg);
+        }
+
+        final FileInputStream in = new FileInputStream(args[0]);
+        final Properties properties = new Properties();
+        properties.load(in);
+        in.close();
+
+        final ApplicationProperties p;
+        p = ApplicationProperties.create(properties);
+
+        run(p);
+    }
+}

Propchange: 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.sh
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.sh?rev=1407376&view=auto
==============================================================================
--- 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.sh
 (added)
+++ 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.sh
 Fri Nov  9 08:13:18 2012
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# Location of the Commons-Math3 jar file
+CM3_JAR=$HOME/.m2/repository/org/apache/commons/commons-math3/3.1-SNAPSHOT/commons-math3-3.1-SNAPSHOT.jar
+
+# Location of file RealFunctionValidation.jar
+APP_JAR=$HOME/Documents/workspace/commons-math3/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.jar
+
+java -cp $CM3_JAR:$APP_JAR RealFunctionValidation logGamma.properties

Propchange: 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.sh
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
commons/proper/math/trunk/src/test/maxima/special/RealFunctionValidation/RealFunctionValidation.sh
------------------------------------------------------------------------------
    svn:executable = *


Reply via email to