From 53fd99ddf3a281718f2c2d9571047363b9985a1d Mon Sep 17 00:00:00 2001 From: Sebastien Brisard Date: Wed, 1 Feb 2012 02:22:00 +0000 Subject: [PATCH] In o.a.c.m.linear - Removed tagging interface ProvidesResidual - added double IterativeLinearSolverEvent.getNormOfResidual() - added RealVector IterativeLinearSolverEvent.getResidual() (optional operation) - added boolean IterativeLinearSolverEvent.providesResidual() - added default implementation DefaultIterativeLinearSolverEvent see MATH-735. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1238905 13f79535-47bb-0310-9956-ffa450edef68 --- .../math/linear/ConjugateGradient.java | 84 ++-------- .../DefaultIterativeLinearSolverEvent.java | 143 ++++++++++++++++++ .../linear/IterativeLinearSolverEvent.java | 36 ++++- .../commons/math/linear/ProvidesResidual.java | 36 ----- .../math/linear/ConjugateGradientTest.java | 40 ++++- 5 files changed, 223 insertions(+), 116 deletions(-) create mode 100644 src/main/java/org/apache/commons/math/linear/DefaultIterativeLinearSolverEvent.java delete mode 100644 src/main/java/org/apache/commons/math/linear/ProvidesResidual.java diff --git a/src/main/java/org/apache/commons/math/linear/ConjugateGradient.java b/src/main/java/org/apache/commons/math/linear/ConjugateGradient.java index 52203cc8d..a28e91ba0 100644 --- a/src/main/java/org/apache/commons/math/linear/ConjugateGradient.java +++ b/src/main/java/org/apache/commons/math/linear/ConjugateGradient.java @@ -79,78 +79,6 @@ import org.apache.commons.math.util.IterationManager; public class ConjugateGradient extends PreconditionedIterativeLinearSolver { - /** - * The type of all events fired by this implementation of the Conjugate - * Gradient method. - * - * @version $Id: ConjugateGradient.java 1175404 2011-09-25 14:48:18Z - * celestin $ - */ - public static class ConjugateGradientEvent - extends IterativeLinearSolverEvent - implements ProvidesResidual { - - /** */ - private static final long serialVersionUID = 20120128L; - - /** The right-hand side vector. */ - private final RealVector b; - - /** 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; - - /** - * Creates a new instance of this class. - * - * @param source the iterative algorithm on which the event initially - * occurred - * @param iterations the number of iterations performed at the time - * {@code this} event is created - * @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, - 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} */ - public RealVector getResidual() { - return r; - } - - /** {@inheritDoc} */ - @Override - public RealVector getRightHandSideVector() { - return b; - } - - /** {@inheritDoc} */ - @Override - public RealVector getSolution() { - return x; - } - } - /** Key for the exception context. */ public static final String OPERATOR = "operator"; @@ -219,6 +147,7 @@ public class ConjugateGradient // Initialization of default stopping criterion manager.resetIterationCount(); final double rmax = delta * b.getNorm(); + final RealVector bro = RealVector.unmodifiableRealVector(b); // Initialization phase counts as one iteration. manager.incrementIterationCount(); @@ -226,10 +155,12 @@ public class ConjugateGradient // of x is optimized for the calculation of the matrix-vector product // A.x. final RealVector x = x0; + final RealVector xro = RealVector.unmodifiableRealVector(x); final RealVector p = x.copy(); RealVector q = a.operate(p); final RealVector r = b.combine(1, -1, q); + final RealVector rro = RealVector.unmodifiableRealVector(r); double rnorm = r.getNorm(); RealVector z; if (minv == null) { @@ -238,7 +169,8 @@ public class ConjugateGradient z = null; } IterativeLinearSolverEvent evt; - evt = new ConjugateGradientEvent(this, manager.getIterations(), x, b, r, rnorm); + evt = new DefaultIterativeLinearSolverEvent(this, + manager.getIterations(), xro, bro, rro, rnorm); manager.fireInitializationEvent(evt); if (rnorm <= rmax) { manager.fireTerminationEvent(evt); @@ -247,7 +179,8 @@ public class ConjugateGradient double rhoPrev = 0.; while (true) { manager.incrementIterationCount(); - evt = new ConjugateGradientEvent(this, manager.getIterations(), x, b, r, rnorm); + evt = new DefaultIterativeLinearSolverEvent(this, + manager.getIterations(), xro, bro, rro, rnorm); manager.fireIterationStartedEvent(evt); if (minv != null) { z = minv.operate(r); @@ -281,7 +214,8 @@ public class ConjugateGradient r.combineToSelf(1., -alpha, q); rhoPrev = rhoNext; rnorm = r.getNorm(); - evt = new ConjugateGradientEvent(this, manager.getIterations(), x, b, r, rnorm); + evt = new DefaultIterativeLinearSolverEvent(this, + manager.getIterations(), xro, bro, rro, rnorm); manager.fireIterationPerformedEvent(evt); if (rnorm <= rmax) { manager.fireTerminationEvent(evt); diff --git a/src/main/java/org/apache/commons/math/linear/DefaultIterativeLinearSolverEvent.java b/src/main/java/org/apache/commons/math/linear/DefaultIterativeLinearSolverEvent.java new file mode 100644 index 000000000..24ed370e5 --- /dev/null +++ b/src/main/java/org/apache/commons/math/linear/DefaultIterativeLinearSolverEvent.java @@ -0,0 +1,143 @@ +/* + * 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.exception.MathUnsupportedOperationException; + +/** + * A default concrete implementation of the abstract class + * {@link IterativeLinearSolverEvent}. + * + * @version $Id$ + */ +public class DefaultIterativeLinearSolverEvent extends IterativeLinearSolverEvent { + + /** */ + private static final long serialVersionUID = 20120129L; + + /** The right-hand side vector. */ + private final RealVector b; + + /** 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; + + /** + * Creates a new instance of this class. This implementation does + * not deep copy the specified vectors {@code x}, {@code b}, + * {@code r}. Therefore the user must make sure that these vectors are + * either unmodifiable views or deep copies of the same vectors actually + * used by the {@code source}. Failure to do so may compromise subsequent + * iterations of the {@code source}. If the residual vector {@code r} is + * {@code null}, then {@link #getResidual()} throws a + * {@link MathUnsupportedOperationException}, and + * {@link #providesResidual()} returns {@code false}. + * + * @param source the iterative solver which fired this event + * @param iterations the number of iterations performed at the time + * {@code this} event is created + * @param x the current estimate of the solution + * @param b the right-hand side vector + * @param r the current estimate of the residual (can be {@code null}) + * @param rnorm the norm of the current estimate of the residual + */ + public DefaultIterativeLinearSolverEvent(final Object source, final int iterations, + final RealVector x, final RealVector b, final RealVector r, + final double rnorm) { + super(source, iterations); + this.x = x; + this.b = b; + this.r = r; + this.rnorm = rnorm; + } + + /** + * Creates a new instance of this class. This implementation does + * not deep copy the specified vectors {@code x}, {@code b}. + * Therefore the user must make sure that these vectors are either + * unmodifiable views or deep copies of the same vectors actually used by + * the {@code source}. Failure to do so may compromise subsequent iterations + * of the {@code source}. Callling {@link #getResidual()} on instances + * returned by this constructor throws a + * {@link MathUnsupportedOperationException}, while + * {@link #providesResidual()} returns {@code false}. + * + * @param source the iterative solver which fired this event + * @param iterations the number of iterations performed at the time + * {@code this} event is created + * @param x the current estimate of the solution + * @param b the right-hand side vector + * @param rnorm the norm of the current estimate of the residual + */ + public DefaultIterativeLinearSolverEvent(final Object source, final int iterations, + final RealVector x, final RealVector b, final double rnorm) { + super(source, iterations); + this.x = x; + this.b = b; + this.r = null; + this.rnorm = rnorm; + } + + /** {@inheritDoc} */ + @Override + public double getNormOfResidual() { + return rnorm; + } + + /** + * {@inheritDoc} + * + * This implementation throws an {@link MathUnsupportedOperationException} + * if no residual vector {@code r} was provided at construction time. + */ + public RealVector getResidual() { + if (r != null) { + return r; + } + throw new MathUnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override + public RealVector getRightHandSideVector() { + return b; + } + + /** {@inheritDoc} */ + @Override + public RealVector getSolution() { + return x; + } + + /** + * {@inheritDoc} + * + * This implementation returns {@code true} if a non-{@code null} value was + * specified for the residual vector {@code r} at construction time. + * + * @return {@code true} if {@code r != null} + */ + @Override + public boolean providesResidual() { + return r != null; + } +} diff --git a/src/main/java/org/apache/commons/math/linear/IterativeLinearSolverEvent.java b/src/main/java/org/apache/commons/math/linear/IterativeLinearSolverEvent.java index 289503cee..dc7ab326d 100644 --- a/src/main/java/org/apache/commons/math/linear/IterativeLinearSolverEvent.java +++ b/src/main/java/org/apache/commons/math/linear/IterativeLinearSolverEvent.java @@ -29,7 +29,7 @@ public abstract class IterativeLinearSolverEvent extends IterationEvent { /** */ - private static final long serialVersionUID = 20120128L; + private static final long serialVersionUID = 20120129L; /** * Creates a new instance of this class. @@ -70,6 +70,29 @@ public abstract class IterativeLinearSolverEvent */ public abstract double getNormOfResidual(); + /** + *

+ * Returns the residual. This is an optional operation, as all iterative + * linear solvers do not provide cheap estimate of the updated residual + * vector, in which case + *

+ * + *

+ * The default implementation throws a + * {@link MathUnsupportedOperationException}. If this method is overriden, + * then {@link #providesResidual()} should be overriden as well. + *

+ * + * @return the updated residual, r + */ + public RealVector getResidual() { + throw new UnsupportedOperationException(); + } + /** * 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 @@ -79,4 +102,15 @@ public abstract class IterativeLinearSolverEvent * @return the solution, x */ public abstract RealVector getSolution(); + + /** + * Returns {@code true} if {@link #getResidual()} is supported. The default + * implementation returns {@code false}. + * + * @return {@code false} if {@link #getResidual()} throws a + * {@link MathUnsupportedOperationException} + */ + public boolean providesResidual() { + return false; + } } diff --git a/src/main/java/org/apache/commons/math/linear/ProvidesResidual.java b/src/main/java/org/apache/commons/math/linear/ProvidesResidual.java deleted file mode 100644 index 6ad8440cd..000000000 --- a/src/main/java/org/apache/commons/math/linear/ProvidesResidual.java +++ /dev/null @@ -1,36 +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.linear; - -/** - * This interface provides access to the current value of the residual of an - * {@link IterativeLinearSolver}. It might be implemented by - * {@link IterativeLinearSolverEvent}, for example. - * - * @version $Id$ - * @since 3.0 - */ -public interface ProvidesResidual { - /** - * Returns the current value of the residual. This should be an - * unmodifiable view or a deep copy of the residual, in order not to - * compromise the subsequent iterations. - * - * @return the current value of the residual - */ - RealVector getResidual(); -} diff --git a/src/test/java/org/apache/commons/math/linear/ConjugateGradientTest.java b/src/test/java/org/apache/commons/math/linear/ConjugateGradientTest.java index b5a5b01d9..411eb9025 100644 --- a/src/test/java/org/apache/commons/math/linear/ConjugateGradientTest.java +++ b/src/test/java/org/apache/commons/math/linear/ConjugateGradientTest.java @@ -19,6 +19,7 @@ package org.apache.commons.math.linear; import java.util.Arrays; import org.apache.commons.math.exception.DimensionMismatchException; +import org.apache.commons.math.exception.MathUnsupportedOperationException; import org.apache.commons.math.exception.MaxCountExceededException; import org.apache.commons.math.util.FastMath; import org.apache.commons.math.util.IterationEvent; @@ -176,9 +177,11 @@ public class ConjugateGradientTest { } public void iterationPerformed(final IterationEvent e) { - RealVector v = ((ProvidesResidual) e).getResidual(); + final IterativeLinearSolverEvent evt; + evt = (IterativeLinearSolverEvent) e; + RealVector v = evt.getResidual(); r.setSubVector(0, v); - v = ((IterativeLinearSolverEvent) e).getSolution(); + v = evt.getSolution(); x.setSubVector(0, v); } @@ -345,9 +348,11 @@ public class ConjugateGradientTest { } public void iterationPerformed(final IterationEvent e) { - RealVector v = ((ProvidesResidual)e).getResidual(); + final IterativeLinearSolverEvent evt; + evt = (IterativeLinearSolverEvent) e; + RealVector v = evt.getResidual(); r.setSubVector(0, v); - v = ((IterativeLinearSolverEvent) e).getSolution(); + v = evt.getSolution(); x.setSubVector(0, v); } @@ -443,24 +448,51 @@ public class ConjugateGradientTest { */ final int[] count = new int[] {0, 0, 0, 0}; final IterationListener listener = new IterationListener() { + private void doTestVectorsAreUnmodifiable(final IterationEvent e) { + final IterativeLinearSolverEvent evt; + evt = (IterativeLinearSolverEvent) e; + try { + evt.getResidual().set(0.0); + Assert.fail("r is modifiable"); + } catch (MathUnsupportedOperationException exc){ + // Expected behavior + } + try { + evt.getRightHandSideVector().set(0.0); + Assert.fail("b is modifiable"); + } catch (MathUnsupportedOperationException exc){ + // Expected behavior + } + try { + evt.getSolution().set(0.0); + Assert.fail("x is modifiable"); + } catch (MathUnsupportedOperationException exc){ + // Expected behavior + } + } + public void initializationPerformed(final IterationEvent e) { ++count[0]; + doTestVectorsAreUnmodifiable(e); } public void iterationPerformed(final IterationEvent e) { ++count[2]; Assert.assertEquals("iteration performed", count[2], e.getIterations() - 1); + doTestVectorsAreUnmodifiable(e); } public void iterationStarted(final IterationEvent e) { ++count[1]; Assert.assertEquals("iteration started", count[1], e.getIterations() - 1); + doTestVectorsAreUnmodifiable(e); } public void terminationPerformed(final IterationEvent e) { ++count[3]; + doTestVectorsAreUnmodifiable(e); } }; solver = new ConjugateGradient(maxIterations, 1E-10, true);