From e89db38df8a93104796dc13ce5327988984fefac Mon Sep 17 00:00:00 2001 From: Phil Steitz Date: Sun, 30 Aug 2009 18:10:16 +0000 Subject: [PATCH] Added normalizeArray method to MathUtils. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@809394 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/commons/math/util/MathUtils.java | 56 +++++++++++++++++++ src/site/xdoc/changes.xml | 3 + .../commons/math/util/MathUtilsTest.java | 48 ++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/src/main/java/org/apache/commons/math/util/MathUtils.java b/src/main/java/org/apache/commons/math/util/MathUtils.java index 28ee4a937..d5c331de0 100644 --- a/src/main/java/org/apache/commons/math/util/MathUtils.java +++ b/src/main/java/org/apache/commons/math/util/MathUtils.java @@ -987,6 +987,62 @@ public final class MathUtils { public static double normalizeAngle(double a, double center) { return a - TWO_PI * Math.floor((a + Math.PI - center) / TWO_PI); } + + /** + *

Normalizes an array to make it sum to a specified value. + * Returns the result of the transformation

+      *    x |-> x * normalizedSum / sum
+      * 
+ * applied to each non-NaN element x of the input array, where sum is the + * sum of the non-NaN entries in the input array.

+ * + *

Throws IllegalArgumentException if normalizedSum is infinite + * or NaN and ArithmeticException if the input array contains any infinite elements + * or sums to 0

+ * + *

Ignores (i.e., copies unchanged to the output array) NaNs in the input array.

+ * + * @param values input array to be normalized + * @param normalizedSum target sum for the normalized array + * @return normalized array + * @throws ArithmeticException if the input array contains infinite elements or sums to zero + * @throws IllegalArgumentException if the target sum is infinite or NaN + */ + public static double[] normalizeArray(double[] values, double normalizedSum) + throws ArithmeticException, IllegalArgumentException { + if (Double.isInfinite(normalizedSum)) { + throw MathRuntimeException.createIllegalArgumentException( + "Cannot normalize to an infinite value"); + } + if (Double.isNaN(normalizedSum)) { + throw MathRuntimeException.createIllegalArgumentException( + "Cannot normalize to NaN"); + } + double sum = 0d; + final int len = values.length; + double[] out = new double[len]; + for (int i = 0; i < len; i++) { + if (Double.isInfinite(values[i])) { + throw MathRuntimeException.createArithmeticException( + "Array contains an infinite element", values[i], i); + } + if (!Double.isNaN(values[i])) { + sum += values[i]; + } + } + if (sum == 0) { + throw MathRuntimeException.createArithmeticException( + "Array sums to zero"); + } + for (int i = 0; i < len; i++) { + if (Double.isNaN(values[i])) { + out[i] = Double.NaN; + } else { + out[i] = values[i] * normalizedSum / sum; + } + } + return out; + } /** * Round the given value to the specified number of decimal places. The diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml index d303f9acd..933cfa4db 100644 --- a/src/site/xdoc/changes.xml +++ b/src/site/xdoc/changes.xml @@ -39,6 +39,9 @@ The type attribute can be add,update,fix,remove. + + Added normalizeArray method to MathUtils. + Fixed a NullPointerException in simplex solver when no solution is possible and some constraints are negative. diff --git a/src/test/java/org/apache/commons/math/util/MathUtilsTest.java b/src/test/java/org/apache/commons/math/util/MathUtilsTest.java index d048f5f71..1486c9d1d 100644 --- a/src/test/java/org/apache/commons/math/util/MathUtilsTest.java +++ b/src/test/java/org/apache/commons/math/util/MathUtilsTest.java @@ -796,6 +796,54 @@ public final class MathUtilsTest extends TestCase { } } } + + public void testNormalizeArray() { + double[] testValues1 = new double[] {1, 1, 2}; + TestUtils.assertEquals( + new double[] {.25, .25, .5}, + MathUtils.normalizeArray(testValues1, 1), + Double.MIN_VALUE); + + double[] testValues2 = new double[] {-1, -1, 1}; + TestUtils.assertEquals( + new double[] {1, 1, -1}, + MathUtils.normalizeArray(testValues2, 1), + Double.MIN_VALUE); + + // Ignore NaNs + double[] testValues3 = new double[] {-1, -1, Double.NaN, 1, Double.NaN}; + TestUtils.assertEquals( + new double[] {1, 1,Double.NaN, -1, Double.NaN}, + MathUtils.normalizeArray(testValues3, 1), + Double.MIN_VALUE); + + // Zero sum -> ArithmeticException + double[] zeroSum = new double[] {-1, 1}; + try { + MathUtils.normalizeArray(zeroSum, 1); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + // Infinite elements -> ArithmeticException + double[] hasInf = new double[] {1, 2, 1, Double.NEGATIVE_INFINITY}; + try { + MathUtils.normalizeArray(hasInf, 1); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + // Infinite target -> IllegalArgumentException + try { + MathUtils.normalizeArray(testValues1, Double.POSITIVE_INFINITY); + fail("expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) {} + + // NaN target -> IllegalArgumentException + try { + MathUtils.normalizeArray(testValues1, Double.NaN); + fail("expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) {} + + } public void testRoundDouble() { double x = 1.234567890;