Author: mikl
Date: Mon Mar 21 08:14:17 2011
New Revision: 1083698

URL: http://svn.apache.org/viewvc?rev=1083698&view=rev
Log:
Fixes MATH-437

Modified:
    
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/AbstractRealMatrix.java
    
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/RealMatrix.java
    
commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/Array2DRowRealMatrixTest.java

Modified: 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/AbstractRealMatrix.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/AbstractRealMatrix.java?rev=1083698&r1=1083697&r2=1083698&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/AbstractRealMatrix.java
 (original)
+++ 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/AbstractRealMatrix.java
 Mon Mar 21 08:14:17 2011
@@ -17,6 +17,8 @@
 
 package org.apache.commons.math.linear;
 
+import java.util.ArrayList;
+
 import org.apache.commons.math.exception.NoDataException;
 import org.apache.commons.math.exception.NotStrictlyPositiveException;
 import org.apache.commons.math.exception.DimensionMismatchException;
@@ -147,6 +149,66 @@ public abstract class AbstractRealMatrix
     /** {@inheritDoc} */
     public RealMatrix preMultiply(final RealMatrix m) {
         return m.multiply(this);
+    }    
+
+    /** {@inheritDoc} */
+    @Override
+    public RealMatrix power(final int p) {
+        if (p < 0) {
+            throw new IllegalArgumentException("p must be >= 0");
+        }
+        
+        if (!isSquare()) {
+            throw new NonSquareMatrixException(getRowDimension(), 
getColumnDimension());
+        }
+        
+        if (p == 0) {
+            return 
MatrixUtils.createRealIdentityMatrix(this.getRowDimension());
+        }
+        
+        if (p == 1) {
+            return this.copy();
+        }
+
+        final int power = p - 1;
+        
+        /*
+         * Only log_2(p) operations is used by doing as follows:
+         * 5^214 = 5^128 * 5^64 * 5^16 * 5^4 * 5^2
+         * 
+         * In general, the same approach is used for A^p.
+         */        
+        
+        final char[] binaryRepresentation = 
Integer.toBinaryString(power).toCharArray();       
+        final ArrayList<Integer> nonZeroPositions = new ArrayList<Integer>();
+        int maxI = -1;
+        
+        for (int i = 0; i < binaryRepresentation.length; ++i) {            
+            if (binaryRepresentation[i] == '1') {
+                final int pos = binaryRepresentation.length - i - 1;
+                nonZeroPositions.add(pos);
+                
+                // The positions are taken in turn, so maxI is only changed 
once
+                if (maxI == -1) {
+                    maxI = pos;
+                }
+            }
+        }
+        
+        RealMatrix[] results = new RealMatrix[maxI + 1];
+        results[0] = this.copy();
+        
+        for (int i = 1; i <= maxI; ++i) {
+            results[i] = results[i-1].multiply(results[i-1]);
+        }
+        
+        RealMatrix result = this.copy();
+        
+        for (Integer i : nonZeroPositions) {
+            result = result.multiply(results[i]);
+        }        
+        
+        return result;
     }
 
     /** {@inheritDoc} */

Modified: 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/RealMatrix.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/RealMatrix.java?rev=1083698&r1=1083697&r2=1083698&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/RealMatrix.java
 (original)
+++ 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/RealMatrix.java
 Mon Mar 21 08:14:17 2011
@@ -98,6 +98,16 @@ public interface RealMatrix extends AnyM
      *             if rowDimension(this) != columnDimension(m)
      */
     RealMatrix preMultiply(RealMatrix m);
+    
+    /**
+     * Returns the result multiplying this with itself <code>p</code> times. 
+     * Depending on the underlying storage, instability for high powers might 
occur. 
+     * @param      p raise this to power p
+     * @return     this^p
+     * @throws     IllegalArgumentException if p < 0
+     *             NonSquareMatrixException if the matrix is not square
+     */
+    RealMatrix power(final int p);
 
     /**
      * Returns matrix entries as a two-dimensional array.

Modified: 
commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/Array2DRowRealMatrixTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/Array2DRowRealMatrixTest.java?rev=1083698&r1=1083697&r2=1083698&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/Array2DRowRealMatrixTest.java
 (original)
+++ 
commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/Array2DRowRealMatrixTest.java
 Mon Mar 21 08:14:17 2011
@@ -96,6 +96,7 @@ public final class Array2DRowRealMatrixT
     // tolerances
     protected double entryTolerance = 10E-16;
     protected double normTolerance = 10E-14;
+    protected double powerTolerance = 10E-16;
 
     /** test dimensions */
     @Test
@@ -222,6 +223,59 @@ public final class Array2DRowRealMatrixT
        TestUtils.assertEquals("m3*m4=m5", m3.multiply(m4), m5, entryTolerance);
    }
 
+    public void testPower() {
+        Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+        Array2DRowRealMatrix mInv = new Array2DRowRealMatrix(testDataInv);
+        Array2DRowRealMatrix mPlusInv = new 
Array2DRowRealMatrix(testDataPlusInv);
+        Array2DRowRealMatrix identity = new Array2DRowRealMatrix(id);
+        
+        TestUtils.assertEquals("m^0", m.power(0),
+            identity, entryTolerance);        
+        TestUtils.assertEquals("mInv^0", mInv.power(0),
+                identity, entryTolerance);        
+        TestUtils.assertEquals("mPlusInv^0", mPlusInv.power(0),
+                identity, entryTolerance);
+        
+        TestUtils.assertEquals("m^1", m.power(1),
+                m, entryTolerance);        
+        TestUtils.assertEquals("mInv^1", mInv.power(1),
+                mInv, entryTolerance);        
+        TestUtils.assertEquals("mPlusInv^1", mPlusInv.power(1),
+                mPlusInv, entryTolerance); 
+        
+        RealMatrix C1 = m.copy();
+        RealMatrix C2 = mInv.copy();
+        RealMatrix C3 = mPlusInv.copy();
+        
+        for (int i = 2; i <= 10; ++i) {
+            C1 = C1.multiply(m);
+            C2 = C2.multiply(mInv);
+            C3 = C3.multiply(mPlusInv);
+            
+            TestUtils.assertEquals("m^" + i, m.power(i),
+                    C1, entryTolerance);        
+            TestUtils.assertEquals("mInv^" + i, mInv.power(i),
+                    C2, entryTolerance);        
+            TestUtils.assertEquals("mPlusInv^" + i, mPlusInv.power(i),
+                    C3, entryTolerance);            
+        }
+        
+        try {
+            Array2DRowRealMatrix mNotSquare = new 
Array2DRowRealMatrix(testData2T);
+            mNotSquare.power(2);
+            Assert.fail("Expecting NonSquareMatrixException");
+        } catch (NonSquareMatrixException ex) {
+            // ignored
+        }
+        
+        try {
+            m.power(-1);
+            Assert.fail("Expecting IllegalArgumentException");
+        } catch (IllegalArgumentException ex) {
+            // ignored
+        }
+    }
+    
     /** test trace */
     @Test
     public void testTrace() {


Reply via email to