ConjugateGradient.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.math4.legacy.linear;

  18. import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
  19. import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
  20. import org.apache.commons.math4.legacy.exception.NullArgumentException;
  21. import org.apache.commons.math4.legacy.exception.util.ExceptionContext;

  22. /**
  23.  * <p>
  24.  * This is an implementation of the conjugate gradient method for
  25.  * {@link RealLinearOperator}. It follows closely the template by <a
  26.  * href="#BARR1994">Barrett et al. (1994)</a> (figure 2.5). The linear system at
  27.  * hand is A &middot; x = b, and the residual is r = b - A &middot; x.
  28.  * </p>
  29.  * <p><b><a id="stopcrit">Default stopping criterion</a></b></p>
  30.  * <p>
  31.  * A default stopping criterion is implemented. The iterations stop when || r ||
  32.  * &le; &delta; || b ||, where b is the right-hand side vector, r the current
  33.  * estimate of the residual, and &delta; a user-specified tolerance. It should
  34.  * be noted that r is the so-called <em>updated</em> residual, which might
  35.  * differ from the true residual due to rounding-off errors (see e.g. <a
  36.  * href="#STRA2002">Strakos and Tichy, 2002</a>).
  37.  * </p>
  38.  * <p><b>Iteration count</b></p>
  39.  * <p>
  40.  * In the present context, an iteration should be understood as one evaluation
  41.  * of the matrix-vector product A &middot; x. The initialization phase therefore
  42.  * counts as one iteration.
  43.  * </p>
  44.  * <p><b><a id="context">Exception context</a></b></p>
  45.  * <p>
  46.  * Besides standard {@link DimensionMismatchException}, this class might throw
  47.  * {@link NonPositiveDefiniteOperatorException} if the linear operator or
  48.  * the preconditioner are not positive definite. In this case, the
  49.  * {@link ExceptionContext} provides some more information
  50.  * <ul>
  51.  * <li>key {@code "operator"} points to the offending linear operator, say L,</li>
  52.  * <li>key {@code "vector"} points to the offending vector, say x, such that
  53.  * x<sup>T</sup> &middot; L &middot; x &lt; 0.</li>
  54.  * </ul>
  55.  *
  56.  * <p><b>References</b></p>
  57.  * <dl>
  58.  * <dt><a id="BARR1994">Barret et al. (1994)</a></dt>
  59.  * <dd>R. Barrett, M. Berry, T. F. Chan, J. Demmel, J. M. Donato, J. Dongarra,
  60.  * V. Eijkhout, R. Pozo, C. Romine and H. Van der Vorst,
  61.  * <a href="http://www.netlib.org/linalg/html_templates/Templates.html"><em>
  62.  * Templates for the Solution of Linear Systems: Building Blocks for Iterative
  63.  * Methods</em></a>, SIAM</dd>
  64.  * <dt><a id="STRA2002">Strakos and Tichy (2002)</a></dt>
  65.  * <dd>Z. Strakos and P. Tichy, <a
  66.  * href="http://etna.mcs.kent.edu/vol.13.2002/pp56-80.dir/pp56-80.pdf">
  67.  * <em>On error estimation in the conjugate gradient method and why it works
  68.  * in finite precision computations</em></a>, Electronic Transactions on
  69.  * Numerical Analysis 13: 56-80, 2002</dd>
  70.  * </dl>
  71.  *
  72.  * @since 3.0
  73.  */
  74. public class ConjugateGradient
  75.     extends PreconditionedIterativeLinearSolver {

  76.     /** Key for the <a href="#context">exception context</a>. */
  77.     public static final String OPERATOR = "operator";

  78.     /** Key for the <a href="#context">exception context</a>. */
  79.     public static final String VECTOR = "vector";

  80.     /**
  81.      * {@code true} if positive-definiteness of matrix and preconditioner should
  82.      * be checked.
  83.      */
  84.     private boolean check;

  85.     /** The value of &delta;, for the default stopping criterion. */
  86.     private final double delta;

  87.     /**
  88.      * Creates a new instance of this class, with <a href="#stopcrit">default
  89.      * stopping criterion</a>.
  90.      *
  91.      * @param maxIterations the maximum number of iterations
  92.      * @param delta the &delta; parameter for the default stopping criterion
  93.      * @param check {@code true} if positive definiteness of both matrix and
  94.      * preconditioner should be checked
  95.      */
  96.     public ConjugateGradient(final int maxIterations, final double delta,
  97.                              final boolean check) {
  98.         super(maxIterations);
  99.         this.delta = delta;
  100.         this.check = check;
  101.     }

  102.     /**
  103.      * Creates a new instance of this class, with <a href="#stopcrit">default
  104.      * stopping criterion</a> and custom iteration manager.
  105.      *
  106.      * @param manager the custom iteration manager
  107.      * @param delta the &delta; parameter for the default stopping criterion
  108.      * @param check {@code true} if positive definiteness of both matrix and
  109.      * preconditioner should be checked
  110.      * @throws NullArgumentException if {@code manager} is {@code null}
  111.      */
  112.     public ConjugateGradient(final IterationManager manager,
  113.                              final double delta, final boolean check)
  114.         throws NullArgumentException {
  115.         super(manager);
  116.         this.delta = delta;
  117.         this.check = check;
  118.     }

  119.     /**
  120.      * Returns {@code true} if positive-definiteness should be checked for both
  121.      * matrix and preconditioner.
  122.      *
  123.      * @return {@code true} if the tests are to be performed
  124.      */
  125.     public final boolean getCheck() {
  126.         return check;
  127.     }

  128.     /**
  129.      * {@inheritDoc}
  130.      *
  131.      * @throws NonPositiveDefiniteOperatorException if {@code a} or {@code m} is
  132.      * not positive definite
  133.      */
  134.     @Override
  135.     public RealVector solveInPlace(final RealLinearOperator a,
  136.                                    final RealLinearOperator m,
  137.                                    final RealVector b,
  138.                                    final RealVector x0)
  139.         throws NullArgumentException, NonPositiveDefiniteOperatorException,
  140.         NonSquareOperatorException, DimensionMismatchException,
  141.         MaxCountExceededException {
  142.         checkParameters(a, m, b, x0);
  143.         final IterationManager manager = getIterationManager();
  144.         // Initialization of default stopping criterion
  145.         manager.resetIterationCount();
  146.         final double rmax = delta * b.getNorm();
  147.         final RealVector bro = RealVector.unmodifiableRealVector(b);

  148.         // Initialization phase counts as one iteration.
  149.         manager.incrementIterationCount();
  150.         // p and x are constructed as copies of x0, since presumably, the type
  151.         // of x is optimized for the calculation of the matrix-vector product
  152.         // A.x.
  153.         final RealVector x = x0;
  154.         final RealVector xro = RealVector.unmodifiableRealVector(x);
  155.         final RealVector p = x.copy();
  156.         RealVector q = a.operate(p);

  157.         final RealVector r = b.combine(1, -1, q);
  158.         final RealVector rro = RealVector.unmodifiableRealVector(r);
  159.         double rnorm = r.getNorm();
  160.         RealVector z;
  161.         if (m == null) {
  162.             z = r;
  163.         } else {
  164.             z = null;
  165.         }
  166.         IterativeLinearSolverEvent evt;
  167.         evt = new DefaultIterativeLinearSolverEvent(this,
  168.             manager.getIterations(), xro, bro, rro, rnorm);
  169.         manager.fireInitializationEvent(evt);
  170.         if (rnorm <= rmax) {
  171.             manager.fireTerminationEvent(evt);
  172.             return x;
  173.         }
  174.         double rhoPrev = 0.;
  175.         while (true) {
  176.             manager.incrementIterationCount();
  177.             evt = new DefaultIterativeLinearSolverEvent(this,
  178.                 manager.getIterations(), xro, bro, rro, rnorm);
  179.             manager.fireIterationStartedEvent(evt);
  180.             if (m != null) {
  181.                 z = m.operate(r);
  182.             }
  183.             final double rhoNext = r.dotProduct(z);
  184.             if (check && rhoNext <= 0) {
  185.                 final NonPositiveDefiniteOperatorException e;
  186.                 e = new NonPositiveDefiniteOperatorException();
  187.                 final ExceptionContext context = e.getContext();
  188.                 context.setValue(OPERATOR, m);
  189.                 context.setValue(VECTOR, r);
  190.                 throw e;
  191.             }
  192.             if (manager.getIterations() == 2) {
  193.                 p.setSubVector(0, z);
  194.             } else {
  195.                 p.combineToSelf(rhoNext / rhoPrev, 1., z);
  196.             }
  197.             q = a.operate(p);
  198.             final double pq = p.dotProduct(q);
  199.             if (check && pq <= 0) {
  200.                 final NonPositiveDefiniteOperatorException e;
  201.                 e = new NonPositiveDefiniteOperatorException();
  202.                 final ExceptionContext context = e.getContext();
  203.                 context.setValue(OPERATOR, a);
  204.                 context.setValue(VECTOR, p);
  205.                 throw e;
  206.             }
  207.             final double alpha = rhoNext / pq;
  208.             x.combineToSelf(1., alpha, p);
  209.             r.combineToSelf(1., -alpha, q);
  210.             rhoPrev = rhoNext;
  211.             rnorm = r.getNorm();
  212.             evt = new DefaultIterativeLinearSolverEvent(this,
  213.                 manager.getIterations(), xro, bro, rro, rnorm);
  214.             manager.fireIterationPerformedEvent(evt);
  215.             if (rnorm <= rmax) {
  216.                 manager.fireTerminationEvent(evt);
  217.                 return x;
  218.             }
  219.         }
  220.     }
  221. }