001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.math3.linear; 018 019import org.apache.commons.math3.exception.DimensionMismatchException; 020import org.apache.commons.math3.exception.MaxCountExceededException; 021import org.apache.commons.math3.exception.NullArgumentException; 022import org.apache.commons.math3.exception.util.ExceptionContext; 023import org.apache.commons.math3.util.IterationManager; 024 025/** 026 * <p> 027 * This is an implementation of the conjugate gradient method for 028 * {@link RealLinearOperator}. It follows closely the template by <a 029 * href="#BARR1994">Barrett et al. (1994)</a> (figure 2.5). The linear system at 030 * hand is A · x = b, and the residual is r = b - A · x. 031 * </p> 032 * <h3><a id="stopcrit">Default stopping criterion</a></h3> 033 * <p> 034 * A default stopping criterion is implemented. The iterations stop when || r || 035 * ≤ δ || b ||, where b is the right-hand side vector, r the current 036 * estimate of the residual, and δ a user-specified tolerance. It should 037 * be noted that r is the so-called <em>updated</em> residual, which might 038 * differ from the true residual due to rounding-off errors (see e.g. <a 039 * href="#STRA2002">Strakos and Tichy, 2002</a>). 040 * </p> 041 * <h3>Iteration count</h3> 042 * <p> 043 * In the present context, an iteration should be understood as one evaluation 044 * of the matrix-vector product A · x. The initialization phase therefore 045 * counts as one iteration. 046 * </p> 047 * <h3><a id="context">Exception context</a></h3> 048 * <p> 049 * Besides standard {@link DimensionMismatchException}, this class might throw 050 * {@link NonPositiveDefiniteOperatorException} if the linear operator or 051 * the preconditioner are not positive definite. In this case, the 052 * {@link ExceptionContext} provides some more information 053 * <ul> 054 * <li>key {@code "operator"} points to the offending linear operator, say L,</li> 055 * <li>key {@code "vector"} points to the offending vector, say x, such that 056 * x<sup>T</sup> · L · x < 0.</li> 057 * </ul> 058 * </p> 059 * <h3>References</h3> 060 * <dl> 061 * <dt><a id="BARR1994">Barret et al. (1994)</a></dt> 062 * <dd>R. Barrett, M. Berry, T. F. Chan, J. Demmel, J. M. Donato, J. Dongarra, 063 * V. Eijkhout, R. Pozo, C. Romine and H. Van der Vorst, 064 * <a href="http://www.netlib.org/linalg/html_templates/Templates.html"><em> 065 * Templates for the Solution of Linear Systems: Building Blocks for Iterative 066 * Methods</em></a>, SIAM</dd> 067 * <dt><a id="STRA2002">Strakos and Tichy (2002) 068 * <dt> 069 * <dd>Z. Strakos and P. Tichy, <a 070 * href="http://etna.mcs.kent.edu/vol.13.2002/pp56-80.dir/pp56-80.pdf"> 071 * <em>On error estimation in the conjugate gradient method and why it works 072 * in finite precision computations</em></a>, Electronic Transactions on 073 * Numerical Analysis 13: 56-80, 2002</dd> 074 * </dl> 075 * 076 * @since 3.0 077 */ 078public class ConjugateGradient 079 extends PreconditionedIterativeLinearSolver { 080 081 /** Key for the <a href="#context">exception context</a>. */ 082 public static final String OPERATOR = "operator"; 083 084 /** Key for the <a href="#context">exception context</a>. */ 085 public static final String VECTOR = "vector"; 086 087 /** 088 * {@code true} if positive-definiteness of matrix and preconditioner should 089 * be checked. 090 */ 091 private boolean check; 092 093 /** The value of δ, for the default stopping criterion. */ 094 private final double delta; 095 096 /** 097 * Creates a new instance of this class, with <a href="#stopcrit">default 098 * stopping criterion</a>. 099 * 100 * @param maxIterations the maximum number of iterations 101 * @param delta the δ parameter for the default stopping criterion 102 * @param check {@code true} if positive definiteness of both matrix and 103 * preconditioner should be checked 104 */ 105 public ConjugateGradient(final int maxIterations, final double delta, 106 final boolean check) { 107 super(maxIterations); 108 this.delta = delta; 109 this.check = check; 110 } 111 112 /** 113 * Creates a new instance of this class, with <a href="#stopcrit">default 114 * stopping criterion</a> and custom iteration manager. 115 * 116 * @param manager the custom iteration manager 117 * @param delta the δ parameter for the default stopping criterion 118 * @param check {@code true} if positive definiteness of both matrix and 119 * preconditioner should be checked 120 * @throws NullArgumentException if {@code manager} is {@code null} 121 */ 122 public ConjugateGradient(final IterationManager manager, 123 final double delta, final boolean check) 124 throws NullArgumentException { 125 super(manager); 126 this.delta = delta; 127 this.check = check; 128 } 129 130 /** 131 * Returns {@code true} if positive-definiteness should be checked for both 132 * matrix and preconditioner. 133 * 134 * @return {@code true} if the tests are to be performed 135 */ 136 public final boolean getCheck() { 137 return check; 138 } 139 140 /** 141 * {@inheritDoc} 142 * 143 * @throws NonPositiveDefiniteOperatorException if {@code a} or {@code m} is 144 * not positive definite 145 */ 146 @Override 147 public RealVector solveInPlace(final RealLinearOperator a, 148 final RealLinearOperator m, 149 final RealVector b, 150 final RealVector x0) 151 throws NullArgumentException, NonPositiveDefiniteOperatorException, 152 NonSquareOperatorException, DimensionMismatchException, 153 MaxCountExceededException { 154 checkParameters(a, m, b, x0); 155 final IterationManager manager = getIterationManager(); 156 // Initialization of default stopping criterion 157 manager.resetIterationCount(); 158 final double rmax = delta * b.getNorm(); 159 final RealVector bro = RealVector.unmodifiableRealVector(b); 160 161 // Initialization phase counts as one iteration. 162 manager.incrementIterationCount(); 163 // p and x are constructed as copies of x0, since presumably, the type 164 // of x is optimized for the calculation of the matrix-vector product 165 // A.x. 166 final RealVector x = x0; 167 final RealVector xro = RealVector.unmodifiableRealVector(x); 168 final RealVector p = x.copy(); 169 RealVector q = a.operate(p); 170 171 final RealVector r = b.combine(1, -1, q); 172 final RealVector rro = RealVector.unmodifiableRealVector(r); 173 double rnorm = r.getNorm(); 174 RealVector z; 175 if (m == null) { 176 z = r; 177 } else { 178 z = null; 179 } 180 IterativeLinearSolverEvent evt; 181 evt = new DefaultIterativeLinearSolverEvent(this, 182 manager.getIterations(), xro, bro, rro, rnorm); 183 manager.fireInitializationEvent(evt); 184 if (rnorm <= rmax) { 185 manager.fireTerminationEvent(evt); 186 return x; 187 } 188 double rhoPrev = 0.; 189 while (true) { 190 manager.incrementIterationCount(); 191 evt = new DefaultIterativeLinearSolverEvent(this, 192 manager.getIterations(), xro, bro, rro, rnorm); 193 manager.fireIterationStartedEvent(evt); 194 if (m != null) { 195 z = m.operate(r); 196 } 197 final double rhoNext = r.dotProduct(z); 198 if (check && (rhoNext <= 0.)) { 199 final NonPositiveDefiniteOperatorException e; 200 e = new NonPositiveDefiniteOperatorException(); 201 final ExceptionContext context = e.getContext(); 202 context.setValue(OPERATOR, m); 203 context.setValue(VECTOR, r); 204 throw e; 205 } 206 if (manager.getIterations() == 2) { 207 p.setSubVector(0, z); 208 } else { 209 p.combineToSelf(rhoNext / rhoPrev, 1., z); 210 } 211 q = a.operate(p); 212 final double pq = p.dotProduct(q); 213 if (check && (pq <= 0.)) { 214 final NonPositiveDefiniteOperatorException e; 215 e = new NonPositiveDefiniteOperatorException(); 216 final ExceptionContext context = e.getContext(); 217 context.setValue(OPERATOR, a); 218 context.setValue(VECTOR, p); 219 throw e; 220 } 221 final double alpha = rhoNext / pq; 222 x.combineToSelf(1., alpha, p); 223 r.combineToSelf(1., -alpha, q); 224 rhoPrev = rhoNext; 225 rnorm = r.getNorm(); 226 evt = new DefaultIterativeLinearSolverEvent(this, 227 manager.getIterations(), xro, bro, rro, rnorm); 228 manager.fireIterationPerformedEvent(evt); 229 if (rnorm <= rmax) { 230 manager.fireTerminationEvent(evt); 231 return x; 232 } 233 } 234 } 235}