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.util.IterationManager;
023import org.apache.commons.math3.util.MathUtils;
024
025/**
026 * This abstract class defines an iterative solver for the linear system A
027 * &middot; x = b. In what follows, the <em>residual</em> r is defined as r = b
028 * - A &middot; x, where A is the linear operator of the linear system, b is the
029 * right-hand side vector, and x the current estimate of the solution.
030 *
031 * @since 3.0
032 */
033public abstract class IterativeLinearSolver {
034
035    /** The object in charge of managing the iterations. */
036    private final IterationManager manager;
037
038    /**
039     * Creates a new instance of this class, with default iteration manager.
040     *
041     * @param maxIterations the maximum number of iterations
042     */
043    public IterativeLinearSolver(final int maxIterations) {
044        this.manager = new IterationManager(maxIterations);
045    }
046
047    /**
048     * Creates a new instance of this class, with custom iteration manager.
049     *
050     * @param manager the custom iteration manager
051     * @throws NullArgumentException if {@code manager} is {@code null}
052     */
053    public IterativeLinearSolver(final IterationManager manager)
054        throws NullArgumentException {
055        MathUtils.checkNotNull(manager);
056        this.manager = manager;
057    }
058
059    /**
060     * Performs all dimension checks on the parameters of
061     * {@link #solve(RealLinearOperator, RealVector, RealVector) solve} and
062     * {@link #solveInPlace(RealLinearOperator, RealVector, RealVector) solveInPlace},
063     * and throws an exception if one of the checks fails.
064     *
065     * @param a the linear operator A of the system
066     * @param b the right-hand side vector
067     * @param x0 the initial guess of the solution
068     * @throws NullArgumentException if one of the parameters is {@code null}
069     * @throws NonSquareOperatorException if {@code a} is not square
070     * @throws DimensionMismatchException if {@code b} or {@code x0} have
071     * dimensions inconsistent with {@code a}
072     */
073    protected static void checkParameters(final RealLinearOperator a,
074        final RealVector b, final RealVector x0) throws
075        NullArgumentException, NonSquareOperatorException,
076        DimensionMismatchException {
077        MathUtils.checkNotNull(a);
078        MathUtils.checkNotNull(b);
079        MathUtils.checkNotNull(x0);
080        if (a.getRowDimension() != a.getColumnDimension()) {
081            throw new NonSquareOperatorException(a.getRowDimension(),
082                                                       a.getColumnDimension());
083        }
084        if (b.getDimension() != a.getRowDimension()) {
085            throw new DimensionMismatchException(b.getDimension(),
086                                                 a.getRowDimension());
087        }
088        if (x0.getDimension() != a.getColumnDimension()) {
089            throw new DimensionMismatchException(x0.getDimension(),
090                                                 a.getColumnDimension());
091        }
092    }
093
094    /**
095     * Returns the iteration manager attached to this solver.
096     *
097     * @return the manager
098     */
099    public IterationManager getIterationManager() {
100        return manager;
101    }
102
103    /**
104     * Returns an estimate of the solution to the linear system A &middot; x =
105     * b.
106     *
107     * @param a the linear operator A of the system
108     * @param b the right-hand side vector
109     * @return a new vector containing the solution
110     * @throws NullArgumentException if one of the parameters is {@code null}
111     * @throws NonSquareOperatorException if {@code a} is not square
112     * @throws DimensionMismatchException if {@code b} has dimensions
113     * inconsistent with {@code a}
114     * @throws MaxCountExceededException at exhaustion of the iteration count,
115     * unless a custom
116     * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback}
117     * has been set at construction of the {@link IterationManager}
118     */
119    public RealVector solve(final RealLinearOperator a, final RealVector b)
120        throws NullArgumentException, NonSquareOperatorException,
121        DimensionMismatchException, MaxCountExceededException {
122        MathUtils.checkNotNull(a);
123        final RealVector x = new ArrayRealVector(a.getColumnDimension());
124        x.set(0.);
125        return solveInPlace(a, b, x);
126    }
127
128    /**
129     * Returns an estimate of the solution to the linear system A &middot; x =
130     * b.
131     *
132     * @param a the linear operator A of the system
133     * @param b the right-hand side vector
134     * @param x0 the initial guess of the solution
135     * @return a new vector containing the solution
136     * @throws NullArgumentException if one of the parameters is {@code null}
137     * @throws NonSquareOperatorException if {@code a} is not square
138     * @throws DimensionMismatchException if {@code b} or {@code x0} have
139     * dimensions inconsistent with {@code a}
140     * @throws MaxCountExceededException at exhaustion of the iteration count,
141     * unless a custom
142     * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback}
143     * has been set at construction of the {@link IterationManager}
144     */
145    public RealVector solve(RealLinearOperator a, RealVector b, RealVector x0)
146        throws NullArgumentException, NonSquareOperatorException,
147        DimensionMismatchException, MaxCountExceededException {
148        MathUtils.checkNotNull(x0);
149        return solveInPlace(a, b, x0.copy());
150    }
151
152    /**
153     * Returns an estimate of the solution to the linear system A &middot; x =
154     * b. The solution is computed in-place (initial guess is modified).
155     *
156     * @param a the linear operator A of the system
157     * @param b the right-hand side vector
158     * @param x0 initial guess of the solution
159     * @return a reference to {@code x0} (shallow copy) updated with the
160     * solution
161     * @throws NullArgumentException if one of the parameters is {@code null}
162     * @throws NonSquareOperatorException if {@code a} is not square
163     * @throws DimensionMismatchException if {@code b} or {@code x0} have
164     * dimensions inconsistent with {@code a}
165     * @throws MaxCountExceededException at exhaustion of the iteration count,
166     * unless a custom
167     * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback}
168     * has been set at construction of the {@link IterationManager}
169     */
170    public abstract RealVector solveInPlace(RealLinearOperator a, RealVector b,
171        RealVector x0) throws NullArgumentException, NonSquareOperatorException,
172        DimensionMismatchException, MaxCountExceededException;
173}