Author: celestin
Date: Sat Jan 28 15:10:42 2012
New Revision: 1237069

URL: http://svn.apache.org/viewvc?rev=1237069&view=rev
Log:
Added method o.a.c.m.linear.IterativeLinearSolverEvent.getNormOfResidual() 
(MATH-735).

Modified:
    
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/ConjugateGradient.java
    
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/IterativeLinearSolverEvent.java
    
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SymmLQ.java
    
commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/ConjugateGradientTest.java
    
commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SymmLQTest.java

Modified: 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/ConjugateGradient.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/ConjugateGradient.java?rev=1237069&r1=1237068&r2=1237069&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/ConjugateGradient.java
 (original)
+++ 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/ConjugateGradient.java
 Sat Jan 28 15:10:42 2012
@@ -99,6 +99,9 @@ public class ConjugateGradient
         /** The current estimate of the residual. */
         private final RealVector r;
 
+        /** The current estimate of the norm of the residual. */
+        private final double rnorm;
+
         /** The current estimate of the solution. */
         private final RealVector x;
 
@@ -112,12 +115,22 @@ public class ConjugateGradient
          * @param x the current estimate of the solution
          * @param b the right-hand side vector
          * @param r the current estimate of the residual
+         * @param rnorm the norm of the current estimate of the residual
          */
-        public ConjugateGradientEvent(final Object source, final int 
iterations, final RealVector x, final RealVector b, final RealVector r) {
+        public ConjugateGradientEvent(final Object source, final int 
iterations,
+            final RealVector x, final RealVector b, final RealVector r,
+            final double rnorm) {
             super(source, iterations);
             this.x = RealVector.unmodifiableRealVector(x);
             this.b = RealVector.unmodifiableRealVector(b);
             this.r = RealVector.unmodifiableRealVector(r);
+            this.rnorm = rnorm;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public double getNormOfResidual() {
+            return rnorm;
         }
 
         /** {@inheritDoc} */
@@ -206,7 +219,7 @@ public class ConjugateGradient
         final IterationManager manager = getIterationManager();
         // Initialization of default stopping criterion
         manager.resetIterationCount();
-        final double r2max = delta * delta * b.dotProduct(b);
+        final double rmax = delta * b.getNorm();
 
         // Initialization phase counts as one iteration.
         manager.incrementIterationCount();
@@ -218,7 +231,7 @@ public class ConjugateGradient
         RealVector q = a.operate(p);
 
         final RealVector r = b.combine(1, -1, q);
-        double r2 = r.dotProduct(r);
+        double rnorm = r.getNorm();
         RealVector z;
         if (m == null) {
             z = r;
@@ -226,16 +239,16 @@ public class ConjugateGradient
             z = null;
         }
         IterativeLinearSolverEvent evt;
-        evt = new ConjugateGradientEvent(this, manager.getIterations(), x, b, 
r);
+        evt = new ConjugateGradientEvent(this, manager.getIterations(), x, b, 
r, rnorm);
         manager.fireInitializationEvent(evt);
-        if (r2 <= r2max) {
+        if (rnorm <= rmax) {
             manager.fireTerminationEvent(evt);
             return x;
         }
         double rhoPrev = 0.;
         while (true) {
             manager.incrementIterationCount();
-            evt = new ConjugateGradientEvent(this, manager.getIterations(), x, 
b, r);
+            evt = new ConjugateGradientEvent(this, manager.getIterations(), x, 
b, r, rnorm);
             manager.fireIterationStartedEvent(evt);
             if (m != null) {
                 z = m.solve(r);
@@ -268,10 +281,10 @@ public class ConjugateGradient
             x.combineToSelf(1., alpha, p);
             r.combineToSelf(1., -alpha, q);
             rhoPrev = rhoNext;
-            r2 = r.dotProduct(r);
-            evt = new ConjugateGradientEvent(this, manager.getIterations(), x, 
b, r);
+            rnorm = r.getNorm();
+            evt = new ConjugateGradientEvent(this, manager.getIterations(), x, 
b, r, rnorm);
             manager.fireIterationPerformedEvent(evt);
-            if (r2 <= r2max) {
+            if (rnorm <= rmax) {
                 manager.fireTerminationEvent(evt);
                 return x;
             }

Modified: 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/IterativeLinearSolverEvent.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/IterativeLinearSolverEvent.java?rev=1237069&r1=1237068&r2=1237069&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/IterativeLinearSolverEvent.java
 (original)
+++ 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/IterativeLinearSolverEvent.java
 Sat Jan 28 15:10:42 2012
@@ -54,6 +54,23 @@ public abstract class IterativeLinearSol
     public abstract RealVector getRightHandSideVector();
 
     /**
+     * Returns the norm of the residual. The returned value is not required to
+     * be <em>exact</em>. Instead, the norm of the so-called <em>updated</em>
+     * residual (if available) should be returned. For example, the
+     * {@link ConjugateGradient conjugate gradient} method computes a sequence
+     * of residuals, the norm of which is cheap to compute. However, due to
+     * accumulation of round-off errors, this residual might differ from the
+     * true residual after some iterations. See e.g. A. Greenbaum and
+     * Z. Strakos, <em>Predicting the Behavior of Finite Precision Lanzos and
+     * Conjugate Gradient Computations</em>, Technical Report 538, Department 
of
+     * Computer Science, New York University, 1991 (available
+     * <a 
href="http://www.archive.org/details/predictingbehavi00gree";>here</a>).
+     *
+     * @return an estimate of the norm of the residual
+     */
+    public abstract double getNormOfResidual();
+
+    /**
      * Returns the current estimate of the solution to the linear system to be
      * solved. This method should return an unmodifiable view, or a deep copy 
of
      * the actual current solution, in order not to compromise subsequent

Modified: 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SymmLQ.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SymmLQ.java?rev=1237069&r1=1237068&r2=1237069&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SymmLQ.java
 (original)
+++ 
commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SymmLQ.java
 Sat Jan 28 15:10:42 2012
@@ -664,7 +664,7 @@ public class SymmLQ
          */
 
         /** */
-        private static final long serialVersionUID = 20120128L;
+        private static final long serialVersionUID = 2012012801L;
 
         /** A reference to the state of this solver. */
         private final State state;
@@ -681,6 +681,7 @@ public class SymmLQ
             this.state = state;
         }
 
+        /** {@inheritDoc} */
         @Override
         public int getIterations() {
             return getIterationManager().getIterations();
@@ -688,6 +689,12 @@ public class SymmLQ
 
         /** {@inheritDoc} */
         @Override
+        public double getNormOfResidual() {
+            return FastMath.min(state.cgnorm, state.lqnorm);
+        }
+
+        /** {@inheritDoc} */
+        @Override
         public RealVector getRightHandSideVector() {
             return RealVector.unmodifiableRealVector(state.b);
         }

Modified: 
commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/ConjugateGradientTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/ConjugateGradientTest.java?rev=1237069&r1=1237068&r2=1237069&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/ConjugateGradientTest.java
 (original)
+++ 
commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/ConjugateGradientTest.java
 Sat Jan 28 15:10:42 2012
@@ -20,6 +20,7 @@ import java.util.Arrays;
 
 import org.apache.commons.math.exception.DimensionMismatchException;
 import org.apache.commons.math.exception.MaxCountExceededException;
+import org.apache.commons.math.util.FastMath;
 import org.apache.commons.math.util.IterationEvent;
 import org.apache.commons.math.util.IterationListener;
 import org.junit.Assert;
@@ -175,7 +176,7 @@ public class ConjugateGradientTest {
             }
 
             public void iterationPerformed(final IterationEvent e) {
-                RealVector v = ((ProvidesResidual)e).getResidual();
+                RealVector v = ((ProvidesResidual) e).getResidual();
                 r.setSubVector(0, v);
                 v = ((IterativeLinearSolverEvent) e).getSolution();
                 x.setSubVector(0, v);
@@ -463,7 +464,6 @@ public class ConjugateGradientTest {
          */
         final int[] count = new int[] {0, 0, 0, 0};
         final IterationListener listener = new IterationListener() {
-
             public void initializationPerformed(final IterationEvent e) {
                 ++count[0];
             }
@@ -498,4 +498,97 @@ public class ConjugateGradientTest {
             Assert.assertEquals(msg, 1, count[3]);
         }
     }
+
+    @Test
+    public void testUnpreconditionedNormOfResidual() {
+        final int n = 5;
+        final int maxIterations = 100;
+        final RealLinearOperator a = new HilbertMatrix(n);
+        final IterativeLinearSolver solver;
+        final IterationListener listener = new IterationListener() {
+
+            private void doTestNormOfResidual(final IterationEvent e) {
+                final IterativeLinearSolverEvent evt;
+                evt = (IterativeLinearSolverEvent) e;
+                final RealVector x = evt.getSolution();
+                final RealVector b = evt.getRightHandSideVector();
+                final RealVector r = b.subtract(a.operate(x));
+                final double rnorm = r.getNorm();
+                Assert.assertEquals("iteration performed (residual)",
+                    rnorm, evt.getNormOfResidual(),
+                    FastMath.max(1E-5 * rnorm, 1E-10));
+            }
+
+            public void initializationPerformed(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+
+            public void iterationPerformed(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+
+            public void iterationStarted(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+
+            public void terminationPerformed(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+        };
+        solver = new ConjugateGradient(maxIterations, 1E-10, true);
+        solver.getIterationManager().addIterationListener(listener);
+        final RealVector b = new ArrayRealVector(n);
+        for (int j = 0; j < n; j++) {
+            b.set(0.);
+            b.setEntry(j, 1.);
+            solver.solve(a, b);
+        }
+    }
+
+    @Test
+    public void testPreconditionedNormOfResidual() {
+        final int n = 5;
+        final int maxIterations = 100;
+        final RealLinearOperator a = new HilbertMatrix(n);
+        final InvertibleRealLinearOperator m = JacobiPreconditioner.create(a);
+        final PreconditionedIterativeLinearSolver solver;
+        final IterationListener listener = new IterationListener() {
+
+            private void doTestNormOfResidual(final IterationEvent e) {
+                final IterativeLinearSolverEvent evt;
+                evt = (IterativeLinearSolverEvent) e;
+                final RealVector x = evt.getSolution();
+                final RealVector b = evt.getRightHandSideVector();
+                final RealVector r = b.subtract(a.operate(x));
+                final double rnorm = r.getNorm();
+                Assert.assertEquals("iteration performed (residual)",
+                    rnorm, evt.getNormOfResidual(),
+                    FastMath.max(1E-5 * rnorm, 1E-10));
+            }
+
+            public void initializationPerformed(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+
+            public void iterationPerformed(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+
+            public void iterationStarted(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+
+            public void terminationPerformed(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+        };
+        solver = new ConjugateGradient(maxIterations, 1E-10, true);
+        solver.getIterationManager().addIterationListener(listener);
+        final RealVector b = new ArrayRealVector(n);
+        for (int j = 0; j < n; j++) {
+            b.set(0.);
+            b.setEntry(j, 1.);
+            solver.solve(a, m, b);
+        }
+    }
 }

Modified: 
commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SymmLQTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SymmLQTest.java?rev=1237069&r1=1237068&r2=1237069&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SymmLQTest.java
 (original)
+++ 
commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SymmLQTest.java
 Sat Jan 28 15:10:42 2012
@@ -624,4 +624,98 @@ public class SymmLQTest {
         });
         new SymmLQ(100, 1., true).solve(a, m, b);
     }
+
+    @Test
+    public void testUnpreconditionedNormOfResidual() {
+        final int n = 5;
+        final int maxIterations = 100;
+        final RealLinearOperator a = new HilbertMatrix(n);
+        final IterativeLinearSolver solver;
+        final IterationListener listener = new IterationListener() {
+
+            private void doTestNormOfResidual(final IterationEvent e) {
+                final IterativeLinearSolverEvent evt;
+                evt = (IterativeLinearSolverEvent) e;
+                final RealVector x = evt.getSolution();
+                final RealVector b = evt.getRightHandSideVector();
+                final RealVector r = b.subtract(a.operate(x));
+                final double rnorm = r.getNorm();
+                Assert.assertEquals("iteration performed (residual)",
+                    rnorm, evt.getNormOfResidual(),
+                    FastMath.max(1E-5 * rnorm, 1E-10));
+            }
+
+            public void initializationPerformed(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+
+            public void iterationPerformed(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+
+            public void iterationStarted(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+
+            public void terminationPerformed(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+        };
+        solver = new ConjugateGradient(maxIterations, 1E-10, true);
+        solver.getIterationManager().addIterationListener(listener);
+        final RealVector b = new ArrayRealVector(n);
+        for (int j = 0; j < n; j++) {
+            b.set(0.);
+            b.setEntry(j, 1.);
+            solver.solve(a, b);
+        }
+    }
+
+    @Test
+    public void testPreconditionedNormOfResidual() {
+        final int n = 5;
+        final int maxIterations = 100;
+        final RealLinearOperator a = new HilbertMatrix(n);
+        final InvertibleRealLinearOperator m = JacobiPreconditioner.create(a);
+        final PreconditionedIterativeLinearSolver solver;
+        final IterationListener listener = new IterationListener() {
+
+            private void doTestNormOfResidual(final IterationEvent e) {
+                final IterativeLinearSolverEvent evt;
+                evt = (IterativeLinearSolverEvent) e;
+                final RealVector x = evt.getSolution();
+                final RealVector b = evt.getRightHandSideVector();
+                final RealVector r = b.subtract(a.operate(x));
+                final double rnorm = r.getNorm();
+                Assert.assertEquals("iteration performed (residual)",
+                    rnorm, evt.getNormOfResidual(),
+                    FastMath.max(1E-5 * rnorm, 1E-10));
+            }
+
+            public void initializationPerformed(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+
+            public void iterationPerformed(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+
+            public void iterationStarted(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+
+            public void terminationPerformed(final IterationEvent e) {
+                doTestNormOfResidual(e);
+            }
+        };
+        solver = new ConjugateGradient(maxIterations, 1E-10, true);
+        solver.getIterationManager().addIterationListener(listener);
+        final RealVector b = new ArrayRealVector(n);
+        for (int j = 0; j < n; j++) {
+            b.set(0.);
+            b.setEntry(j, 1.);
+            solver.solve(a, m, b);
+        }
+    }
 }
+


Reply via email to