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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.linear;

  22. import org.hipparchus.exception.LocalizedCoreFormats;
  23. import org.hipparchus.exception.MathIllegalArgumentException;
  24. import org.hipparchus.exception.MathIllegalStateException;
  25. import org.hipparchus.exception.NullArgumentException;
  26. import org.hipparchus.util.IterationManager;

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

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

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

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

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

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

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

  122.     /**
  123.      * Returns {@code true} if positive-definiteness should be checked for both
  124.      * matrix and preconditioner.
  125.      *
  126.      * @return {@code true} if the tests are to be performed
  127.      * @since 1.4
  128.      */
  129.     public final boolean shouldCheck() {
  130.         return check;
  131.     }

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

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

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