Added support for iterative linear solvers (Conjugate Gradient only for now). See MATH-581.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1175404 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
c60827b0b7
commit
39eea3de3f
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* 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.DimensionMismatchException;
|
||||
import org.apache.commons.math.exception.MaxCountExceededException;
|
||||
import org.apache.commons.math.exception.NullArgumentException;
|
||||
import org.apache.commons.math.exception.util.ExceptionContext;
|
||||
import org.apache.commons.math.util.IterationManager;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This is an implementation of the conjugate gradient method for
|
||||
* {@link RealLinearOperator}. It follows closely the template by <a
|
||||
* href="#BARR1994">Barrett et al. (1994)</a> (figure 2.5). The linear system at
|
||||
* hand is A · x = b, and the residual is r = b - A · x.
|
||||
* </p>
|
||||
* <h3><a id="stopcrit">Default stopping criterion</a></h3>
|
||||
* <p>
|
||||
* A default stopping criterion is implemented. The iterations stop when || r ||
|
||||
* ≤ δ || b ||, where b is the right-hand side vector, r the current
|
||||
* estimate of the residual, and δ a user-specified tolerance. It should
|
||||
* be noted that r is the so-called <em>updated</em> residual, which might
|
||||
* differ from the true residual due to rounding-off errors (see e.g. <a
|
||||
* href="#STRA2002">Strakos and Tichy, 2002</a>).
|
||||
* </p>
|
||||
* <h3>Iteration count</h3>
|
||||
* <p>
|
||||
* In the present context, an iteration should be understood as one evaluation
|
||||
* of the matrix-vector product A · x. The initialization phase therefore
|
||||
* counts as one iteration.
|
||||
* </p>
|
||||
* <h3><a id="context">Exception context</a></h3>
|
||||
* <p>
|
||||
* Besides standard {@link DimensionMismatchException}, this class might throw
|
||||
* {@link NonPositiveDefiniteLinearOperatorException} if the linear operator or
|
||||
* the preconditioner are not positive definite. In this case, the
|
||||
* {@link ExceptionContext} provides some more information
|
||||
* <ul>
|
||||
* <li>key {@code "operator"} points to the offending linear operator, say L,</li>
|
||||
* <li>key {@code "vector"} points to the offending vector, say x, such that
|
||||
* x<sup>T</sup> · L · x < 0.</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <h3>References</h3>
|
||||
* <dl>
|
||||
* <dt><a id="BARR1994">Barret et al. (1994)</a></dt>
|
||||
* <dd>R. Barrett, M. Berry, T. F. Chan, J. Demmel, J. M. Donato, J. Dongarra,
|
||||
* V. Eijkhout, R. Pozo, C. Romine and H. Van der Vorst,
|
||||
* <em>Templates for the Solution of Linear Systems: Building Blocks for
|
||||
* Iterative Methods</em>, SIAM</dd>
|
||||
* <dt><a id="STRA2002">Strakos and Tichy (2002)
|
||||
* <dt>
|
||||
* <dd>Z. Strakos and P. Tichy, <a
|
||||
* href="http://etna.mcs.kent.edu/vol.13.2002/pp56-80.dir/pp56-80.pdf">
|
||||
* <em>On error estimation in the conjugate gradient method and why it works
|
||||
* in finite precision computations</em></a>, Electronic Transactions on
|
||||
* Numerical Analysis 13: 56-80, 2002</dd>
|
||||
* </dl>
|
||||
*
|
||||
* @version $Id$
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConjugateGradient
|
||||
extends PreconditionedIterativeLinearSolver {
|
||||
|
||||
/**
|
||||
* The type of all events fired by this implementation of the Conjugate
|
||||
* Gradient method.
|
||||
*
|
||||
* @version $Id$
|
||||
*/
|
||||
public abstract static class ConjugateGradientEvent
|
||||
extends IterativeLinearSolverEvent
|
||||
implements ProvidesResidual {
|
||||
|
||||
/** */
|
||||
private static final long serialVersionUID = 6461730085343318121L;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class.
|
||||
*
|
||||
* @param source The iterative algorithm on which the event initially
|
||||
* occurred.
|
||||
*/
|
||||
public ConjugateGradientEvent(final Object source) {
|
||||
super(source);
|
||||
}
|
||||
}
|
||||
|
||||
/** Key for the <a href="#context">exception context</a>. */
|
||||
public static final String OPERATOR = "operator";
|
||||
|
||||
/** Key for the <a href="#context">exception context</a>. */
|
||||
public static final String VECTOR = "vector";
|
||||
|
||||
/**
|
||||
* {@code true} if positive-definiteness of matrix and preconditioner should
|
||||
* be checked.
|
||||
*/
|
||||
private boolean check;
|
||||
|
||||
/** The value of δ, for the default stopping criterion. */
|
||||
private final double delta;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class, with <a href="#stopcrit">default
|
||||
* stopping criterion</a>.
|
||||
*
|
||||
* @param maxIterations Maximum number of iterations.
|
||||
* @param delta δ parameter for the default stopping criterion.
|
||||
* @param check {@code true} if positive definiteness of both matrix and
|
||||
* preconditioner should be checked.
|
||||
*/
|
||||
public ConjugateGradient(final int maxIterations, final double delta,
|
||||
final boolean check) {
|
||||
super(maxIterations);
|
||||
this.delta = delta;
|
||||
this.check = check;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class, with <a href="#stopcrit">default
|
||||
* stopping criterion</a> and custom iteration manager.
|
||||
*
|
||||
* @param manager Custom iteration manager.
|
||||
* @param delta δ parameter for the default stopping criterion.
|
||||
* @param check {@code true} if positive definiteness of both matrix and
|
||||
* preconditioner should be checked.
|
||||
*/
|
||||
public ConjugateGradient(final IterationManager manager,
|
||||
final double delta, final boolean check) {
|
||||
super(manager);
|
||||
this.delta = delta;
|
||||
this.check = check;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if positive-definiteness should be checked for both
|
||||
* matrix and preconditioner.
|
||||
*
|
||||
* @return {@code true} if the tests are to be performed.
|
||||
*/
|
||||
public final boolean getCheck() {
|
||||
return check;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public RealVector solve(final RealLinearOperator a,
|
||||
final InvertibleRealLinearOperator m,
|
||||
final RealVector b, final RealVector x0,
|
||||
final boolean inPlace)
|
||||
throws NullArgumentException, NonSquareLinearOperatorException,
|
||||
DimensionMismatchException, MaxCountExceededException {
|
||||
checkParameters(a, m, b, x0, inPlace);
|
||||
final IterationManager manager = getIterationManager();
|
||||
// Initialization of default stopping criterion
|
||||
manager.resetIterationCount();
|
||||
final double r2max = delta * delta * b.dotProduct(b);
|
||||
|
||||
// p and x are constructed as copies of x0, since presumably, the type
|
||||
// of x is optimized for the calculation of the matrix-vector product
|
||||
// A.x.
|
||||
final RealVector x;
|
||||
if (inPlace) {
|
||||
x = x0;
|
||||
} else {
|
||||
if (x0 != null) {
|
||||
x = x0.copy();
|
||||
} else {
|
||||
x = new ArrayRealVector(a.getColumnDimension());
|
||||
}
|
||||
}
|
||||
final RealVector p = x.copy();
|
||||
RealVector q = a.operate(p);
|
||||
manager.incrementIterationCount();
|
||||
final RealVector r = b.combine(1, -1, q);
|
||||
double r2 = r.dotProduct(r);
|
||||
RealVector z;
|
||||
if (m == null) {
|
||||
z = r;
|
||||
} else {
|
||||
z = null;
|
||||
}
|
||||
final IterativeLinearSolverEvent event;
|
||||
event = new ConjugateGradientEvent(this) {
|
||||
|
||||
public RealVector getResidual() {
|
||||
return ArrayRealVector.unmodifiableRealVector(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealVector getRightHandSideVector() {
|
||||
return ArrayRealVector.unmodifiableRealVector(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealVector getSolution() {
|
||||
return ArrayRealVector.unmodifiableRealVector(x);
|
||||
}
|
||||
};
|
||||
manager.fireInitializationEvent(event);
|
||||
if (r2 <= r2max) {
|
||||
manager.fireTerminationEvent(event);
|
||||
return x;
|
||||
}
|
||||
double rhoPrev = 0.;
|
||||
while (true) {
|
||||
manager.fireIterationStartedEvent(event);
|
||||
if (m != null) {
|
||||
z = m.solve(r);
|
||||
}
|
||||
final double rhoNext = r.dotProduct(z);
|
||||
if (check && (rhoNext <= 0.)) {
|
||||
final NonPositiveDefiniteLinearOperatorException e;
|
||||
e = new NonPositiveDefiniteLinearOperatorException();
|
||||
final ExceptionContext context = e.getContext();
|
||||
context.setValue(OPERATOR, m);
|
||||
context.setValue(VECTOR, r);
|
||||
throw e;
|
||||
}
|
||||
if (manager.getIterations() == 1) {
|
||||
p.setSubVector(0, z);
|
||||
} else {
|
||||
p.combineToSelf(rhoNext / rhoPrev, 1., z);
|
||||
}
|
||||
q = a.operate(p);
|
||||
manager.incrementIterationCount();
|
||||
final double pq = p.dotProduct(q);
|
||||
if (check && (pq <= 0.)) {
|
||||
final NonPositiveDefiniteLinearOperatorException e;
|
||||
e = new NonPositiveDefiniteLinearOperatorException();
|
||||
final ExceptionContext context = e.getContext();
|
||||
context.setValue(OPERATOR, a);
|
||||
context.setValue(VECTOR, p);
|
||||
throw e;
|
||||
}
|
||||
final double alpha = rhoNext / pq;
|
||||
x.combineToSelf(1., alpha, p);
|
||||
r.combineToSelf(1., -alpha, q);
|
||||
rhoPrev = rhoNext;
|
||||
r2 = r.dotProduct(r);
|
||||
manager.fireIterationPerformedEvent(event);
|
||||
if (r2 <= r2max) {
|
||||
manager.fireTerminationEvent(event);
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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.DimensionMismatchException;
|
||||
import org.apache.commons.math.exception.MaxCountExceededException;
|
||||
import org.apache.commons.math.exception.NullArgumentException;
|
||||
import org.apache.commons.math.util.IterationManager;
|
||||
import org.apache.commons.math.util.MathUtils;
|
||||
|
||||
/**
|
||||
* This abstract class defines an iterative solver for the linear system A
|
||||
* · x = b. In what follows, the <em>residual</em> r is defined as r = b
|
||||
* - A · x, where A is the linear operator of the linear system, b is the
|
||||
* right-hand side vector, and x the current estimate of the solution.
|
||||
*
|
||||
* @version $Id$
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class IterativeLinearSolver {
|
||||
|
||||
/** The object in charge of managing the iterations. */
|
||||
private final IterationManager manager;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class, with default iteration manager.
|
||||
*
|
||||
* @param maxIterations Maximum number of iterations.
|
||||
*/
|
||||
public IterativeLinearSolver(final int maxIterations) {
|
||||
this.manager = new IterationManager(maxIterations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class, with custom iteration manager.
|
||||
*
|
||||
* @param manager Custom iteration manager.
|
||||
*/
|
||||
public IterativeLinearSolver(final IterationManager manager) {
|
||||
MathUtils.checkNotNull(manager);
|
||||
this.manager = manager;
|
||||
}
|
||||
/**
|
||||
* Performs all dimension checks on the parameters of
|
||||
* {@link #solve(RealLinearOperator, RealVector, RealVector, boolean)}, and
|
||||
* throws an exception if one of the checks fails.
|
||||
*
|
||||
* @param a Linear operator A of the system.
|
||||
* @param b Right-hand side vector.
|
||||
* @param x0 Initial guess of the solution (can be {@code null} if
|
||||
* {@code inPlace} is set to {@code false}).
|
||||
* @param inPlace {@code true} if the initial guess is to be updated with
|
||||
* the current estimate of the solution.
|
||||
* @throws NullArgumentException if one of the parameters is {@code null}.
|
||||
* @throws NonSquareLinearOperatorException if {@code a} is not square.
|
||||
* @throws DimensionMismatchException if {@code b} or {@code x0} have
|
||||
* dimensions inconsistent with {@code a}.
|
||||
*/
|
||||
protected static void checkParameters(final RealLinearOperator a,
|
||||
final RealVector b,
|
||||
final RealVector x0,
|
||||
final boolean inPlace)
|
||||
throws NullArgumentException, NonSquareLinearOperatorException,
|
||||
DimensionMismatchException {
|
||||
MathUtils.checkNotNull(a);
|
||||
MathUtils.checkNotNull(b);
|
||||
if (a.getRowDimension() != a.getColumnDimension()) {
|
||||
throw new NonSquareLinearOperatorException(a.getRowDimension(),
|
||||
a.getColumnDimension());
|
||||
}
|
||||
if (b.getDimension() != a.getRowDimension()) {
|
||||
throw new DimensionMismatchException(b.getDimension(),
|
||||
a.getRowDimension());
|
||||
}
|
||||
if (inPlace) {
|
||||
MathUtils.checkNotNull(x0);
|
||||
if (x0.getDimension() != a.getColumnDimension()) {
|
||||
throw new DimensionMismatchException(x0.getDimension(),
|
||||
a.getColumnDimension());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link IterationManager} attached to this solver.
|
||||
*
|
||||
* @return the manager.
|
||||
*/
|
||||
public IterationManager getIterationManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an estimate of the solution to the linear system A · x =
|
||||
* b. If no initial estimate of the solution is provided, (0, …, 0)
|
||||
* is assumed.
|
||||
*
|
||||
* @param a Linear operator A of the system.
|
||||
* @param b Right-hand side vector.
|
||||
* @param x0 Initial guess of the solution (can be {@code null} if
|
||||
* {@code inPlace} is set to {@code false}).
|
||||
* @param inPlace {@code true} if the initial guess is to be updated with
|
||||
* the current estimate of the solution.
|
||||
* @return A reference to {@code x0} (shallow copy) if {@code inPlace} was
|
||||
* set to {@code true}. Otherwise, a new vector containing the
|
||||
* solution.
|
||||
* @throws NullArgumentException if one of the parameters is {@code null}.
|
||||
* @throws NonSquareLinearOperatorException if {@code a} is not square.
|
||||
* @throws DimensionMismatchException if {@code b} or {@code x0} have
|
||||
* dimensions inconsistent with {@code a}.
|
||||
* @throws MaxCountExceededException at exhaustion of the iteration count,
|
||||
* unless a custom {@link MaxCountExceededCallback callback} has
|
||||
* been set at construction.
|
||||
*/
|
||||
public abstract RealVector solve(RealLinearOperator a, RealVector b,
|
||||
RealVector x0, boolean inPlace)
|
||||
throws NullArgumentException, NonSquareLinearOperatorException,
|
||||
DimensionMismatchException, MaxCountExceededException;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.util.IterationEvent;
|
||||
|
||||
/**
|
||||
* This is the base class for all events occuring during the iterations of a
|
||||
* {@link IterativeLinearSolver}.
|
||||
*
|
||||
* @version $Id$
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class IterativeLinearSolverEvent
|
||||
extends IterationEvent {
|
||||
|
||||
/** */
|
||||
private static final long serialVersionUID = 283291016904748030L;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class.
|
||||
*
|
||||
* @param source The iterative algorithm on which the event initially
|
||||
* occurred.
|
||||
*/
|
||||
public IterativeLinearSolverEvent(final Object source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current right-hand side of the linear system to be solved.
|
||||
* This method should return an unmodifiable view, or a deep copy of the
|
||||
* actual right-hand side, in order not to compromise subsequent iterations
|
||||
* of the source {@link IterativeLinearSolver}.
|
||||
*
|
||||
* @return The right-hand side vector, b.
|
||||
*/
|
||||
public abstract RealVector getRightHandSideVector();
|
||||
|
||||
/**
|
||||
* 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
|
||||
* the actual current solution, in order not to compromise subsequent
|
||||
* iterations of the source {@link IterativeLinearSolver}.
|
||||
*
|
||||
* @return The solution, x.
|
||||
*/
|
||||
public abstract RealVector getSolution();
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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 class implements the standard Jacobi (diagonal) preconditioner.
|
||||
*
|
||||
* @version $Id$
|
||||
* @since 3.0
|
||||
*/
|
||||
public class JacobiPreconditioner
|
||||
extends InvertibleRealLinearOperator {
|
||||
|
||||
/** The diagonal coefficients of the preconditioner. */
|
||||
private final ArrayRealVector diag;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class.
|
||||
*
|
||||
* @param diag Diagonal coefficients of the preconditioner.
|
||||
* @param deep {@code true} if a deep copy of the above array should be
|
||||
* performed.
|
||||
*/
|
||||
public JacobiPreconditioner(final double[] diag, final boolean deep) {
|
||||
this.diag = new ArrayRealVector(diag, deep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class. This method extracts the diagonal
|
||||
* coefficients of the specified linear operator. If {@code a} does not
|
||||
* extend {@link AbstractRealMatrix}, then the coefficients of the
|
||||
* underlying matrix are not accessible, coefficient extraction is made by
|
||||
* matrix-vector products with the basis vectors (and might therefore take
|
||||
* some time). With matrices, direct entry access is carried out.
|
||||
*
|
||||
* @param a Linear operator for which the preconditioner should be built.
|
||||
* @return Preconditioner made of the diagonal coefficients of the specified
|
||||
* linear operator.
|
||||
* @throws NonSquareLinearOperatorException if {@code a} is not square.
|
||||
*/
|
||||
public static JacobiPreconditioner create(final RealLinearOperator a)
|
||||
throws NonSquareLinearOperatorException {
|
||||
final int n = a.getColumnDimension();
|
||||
if (a.getRowDimension() != n) {
|
||||
throw new NonSquareLinearOperatorException(a.getRowDimension(), n);
|
||||
}
|
||||
final double[] diag = new double[n];
|
||||
if (a instanceof AbstractRealMatrix) {
|
||||
final AbstractRealMatrix m = (AbstractRealMatrix) a;
|
||||
for (int i = 0; i < n; i++) {
|
||||
diag[i] = m.getEntry(i, i);
|
||||
}
|
||||
} else {
|
||||
final ArrayRealVector x = new ArrayRealVector(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
x.set(0.);
|
||||
x.setEntry(i, 1.);
|
||||
diag[i] = a.operate(x).getEntry(i);
|
||||
}
|
||||
}
|
||||
return new JacobiPreconditioner(diag, false);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getColumnDimension() {
|
||||
return diag.getDimension();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getRowDimension() {
|
||||
return diag.getDimension();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public RealVector operate(final RealVector x) {
|
||||
// Dimension check is carried out by ebeMultiply
|
||||
return x.ebeMultiply(diag);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public RealVector solve(final RealVector b) {
|
||||
// Dimension check is carried out by ebeDivide
|
||||
return b.ebeDivide(diag);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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.DimensionMismatchException;
|
||||
import org.apache.commons.math.exception.MaxCountExceededException;
|
||||
import org.apache.commons.math.exception.NullArgumentException;
|
||||
import org.apache.commons.math.util.IterationManager;
|
||||
|
||||
/**
|
||||
* This abstract class defines preconditioned iterative solvers. When A is
|
||||
* ill-conditioned, instead of solving system A · x = b directly, it is
|
||||
* preferable to solve M<sup>-1</sup> · A · x = M<sup>-1</sup>
|
||||
* · b, where M approximates in some way A, while remaining comparatively
|
||||
* easier to invert. M (not M<sup>-1</sup>!) is called the
|
||||
* <em>preconditionner</em>.
|
||||
*
|
||||
* @version $Id$
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class PreconditionedIterativeLinearSolver
|
||||
extends IterativeLinearSolver {
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class, with default iteration manager.
|
||||
*
|
||||
* @param maxIterations Maximum number of iterations.
|
||||
*/
|
||||
public PreconditionedIterativeLinearSolver(final int maxIterations) {
|
||||
super(maxIterations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class, with custom iteration manager.
|
||||
*
|
||||
* @param manager Custom iteration manager.
|
||||
*/
|
||||
public PreconditionedIterativeLinearSolver(final IterationManager manager) {
|
||||
super(manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs all dimension checks on the parameters of
|
||||
* {@link #solve(RealLinearOperator, InvertibleRealLinearOperator, RealVector, RealVector, boolean)}
|
||||
* , and throws an exception if one of the checks fails.
|
||||
*
|
||||
* @param a Linear operator A of the system.
|
||||
* @param m Preconditioner (can be {@code null}).
|
||||
* @param b Right-hand side vector.
|
||||
* @param x0 Initial guess of the solution (can be {@code null} if
|
||||
* {@code inPlace} is set to {@code false}).
|
||||
* @param inPlace {@code true} if the initial guess is to be updated with
|
||||
* the current estimate of the solution.
|
||||
* @throws NullArgumentException if one of the parameters is {@code null}.
|
||||
* @throws NonSquareLinearOperatorException if {@code a} or {@code m} is not
|
||||
* square.
|
||||
* @throws DimensionMismatchException if {@code m}, {@code b} or {@code x}
|
||||
* have dimensions inconsistent with {@code a}.
|
||||
*/
|
||||
protected static void checkParameters(final RealLinearOperator a,
|
||||
final InvertibleRealLinearOperator m,
|
||||
final RealVector b,
|
||||
final RealVector x0,
|
||||
final boolean inPlace)
|
||||
throws NullArgumentException, NonSquareLinearOperatorException,
|
||||
DimensionMismatchException {
|
||||
checkParameters(a, b, x0, inPlace);
|
||||
if (m != null) {
|
||||
if (m.getColumnDimension() != m.getRowDimension()) {
|
||||
throw new NonSquareLinearOperatorException(
|
||||
m.getColumnDimension(),
|
||||
m.getRowDimension());
|
||||
}
|
||||
if (m.getRowDimension() != a.getRowDimension()) {
|
||||
throw new DimensionMismatchException(m.getRowDimension(),
|
||||
a.getRowDimension());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an estimate of the solution to the linear system A · x =
|
||||
* b. If no initial estimate of the solution is provided, (0, …, 0)
|
||||
* is assumed.
|
||||
*
|
||||
* @param a Linear operator A of the system.
|
||||
* @param m Preconditioner (can be {@code null}).
|
||||
* @param b Right-hand side vector.
|
||||
* @param x0 Initial guess of the solution (can be {@code null} if
|
||||
* {@code inPlace} is set to {@code false}).
|
||||
* @param inPlace {@code true} if the initial guess is to be updated with
|
||||
* the current estimate of the solution.
|
||||
* @return A reference to {@code x0} (shallow copy) if {@code update} was
|
||||
* set to {@code true}. Otherwise, a new vector containing the
|
||||
* solution.
|
||||
* @throws NullArgumentException if one of the parameters is {@code null}.
|
||||
* @throws NonSquareLinearOperatorException if {@code a} or {@code m} is not
|
||||
* square.
|
||||
* @throws DimensionMismatchException if {@code m}, {@code b} or {@code x}
|
||||
* have dimensions inconsistent with {@code a}.
|
||||
* @throws MaxCountExceededException at exhaustion of the iteration count,
|
||||
* unless a custom {@link MaxCountExceededCallback callback} has
|
||||
* been set at construction.
|
||||
*/
|
||||
public abstract RealVector solve(RealLinearOperator a,
|
||||
InvertibleRealLinearOperator m,
|
||||
RealVector b, RealVector x0,
|
||||
final boolean inPlace)
|
||||
throws NullArgumentException, NonSquareLinearOperatorException,
|
||||
DimensionMismatchException, MaxCountExceededException;
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public RealVector solve(final RealLinearOperator a, final RealVector b,
|
||||
final RealVector x, final boolean inPlace)
|
||||
throws NullArgumentException, NonSquareLinearOperatorException,
|
||||
DimensionMismatchException, MaxCountExceededException {
|
||||
checkParameters(a, b, x, inPlace);
|
||||
return solve(a, null, b, x, inPlace);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
|
@ -0,0 +1,481 @@
|
|||
/*
|
||||
* 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.DimensionMismatchException;
|
||||
import org.apache.commons.math.exception.MaxCountExceededException;
|
||||
import org.apache.commons.math.util.IterationEvent;
|
||||
import org.apache.commons.math.util.IterationListener;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ConjugateGradientTest {
|
||||
|
||||
@Test(expected = NonSquareLinearOperatorException.class)
|
||||
public void testNonSquareOperator() {
|
||||
final Array2DRowRealMatrix a = new Array2DRowRealMatrix(2, 3);
|
||||
final IterativeLinearSolver solver;
|
||||
solver = new ConjugateGradient(10, 0., false);
|
||||
final ArrayRealVector b = new ArrayRealVector(a.getRowDimension());
|
||||
final ArrayRealVector x = new ArrayRealVector(a.getColumnDimension());
|
||||
solver.solve(a, b, x, false);
|
||||
}
|
||||
|
||||
@Test(expected = DimensionMismatchException.class)
|
||||
public void testDimensionMismatchRightHandSide() {
|
||||
final Array2DRowRealMatrix a = new Array2DRowRealMatrix(3, 3);
|
||||
final IterativeLinearSolver solver;
|
||||
solver = new ConjugateGradient(10, 0., false);
|
||||
final ArrayRealVector b = new ArrayRealVector(2);
|
||||
final ArrayRealVector x = new ArrayRealVector(3);
|
||||
solver.solve(a, b, x, false);
|
||||
}
|
||||
|
||||
@Test(expected = DimensionMismatchException.class)
|
||||
public void testDimensionMismatchSolution() {
|
||||
final Array2DRowRealMatrix a = new Array2DRowRealMatrix(3, 3);
|
||||
final IterativeLinearSolver solver;
|
||||
solver = new ConjugateGradient(10, 0., false);
|
||||
final ArrayRealVector b = new ArrayRealVector(3);
|
||||
final ArrayRealVector x = new ArrayRealVector(2);
|
||||
solver.solve(a, b, x, false);
|
||||
}
|
||||
|
||||
@Test(expected = NonPositiveDefiniteLinearOperatorException.class)
|
||||
public void testNonPositiveDefiniteLinearOperator() {
|
||||
final Array2DRowRealMatrix a = new Array2DRowRealMatrix(2, 2);
|
||||
a.setEntry(0, 0, -1.);
|
||||
a.setEntry(0, 1, 2.);
|
||||
a.setEntry(1, 0, 3.);
|
||||
a.setEntry(1, 1, 4.);
|
||||
final IterativeLinearSolver solver;
|
||||
solver = new ConjugateGradient(10, 0., true);
|
||||
final ArrayRealVector b = new ArrayRealVector(2);
|
||||
b.setEntry(0, -1.);
|
||||
b.setEntry(1, -1.);
|
||||
final ArrayRealVector x = new ArrayRealVector(2);
|
||||
solver.solve(a, b, x, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnpreconditionedSolution() {
|
||||
final int n = 5;
|
||||
final int maxIterations = 100;
|
||||
final RealLinearOperator a = new HilbertMatrix(n);
|
||||
final InverseHilbertMatrix ainv = new InverseHilbertMatrix(n);
|
||||
final IterativeLinearSolver solver;
|
||||
solver = new ConjugateGradient(maxIterations, 1E-10, true);
|
||||
final RealVector b = new ArrayRealVector(n);
|
||||
for (int j = 0; j < n; j++) {
|
||||
b.set(0.);
|
||||
b.setEntry(j, 1.);
|
||||
final RealVector x = solver.solve(a, b, null, false);
|
||||
for (int i = 0; i < n; i++) {
|
||||
final double actual = x.getEntry(i);
|
||||
final double expected = ainv.getEntry(i, j);
|
||||
final double delta = 1E-10 * Math.abs(expected);
|
||||
final String msg = String.format("entry[%d][%d]", i, j);
|
||||
Assert.assertEquals(msg, expected, actual, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnpreconditionedInPlaceSolutionWithInitialGuess() {
|
||||
final int n = 5;
|
||||
final int maxIterations = 100;
|
||||
final RealLinearOperator a = new HilbertMatrix(n);
|
||||
final InverseHilbertMatrix ainv = new InverseHilbertMatrix(n);
|
||||
final IterativeLinearSolver solver;
|
||||
solver = new ConjugateGradient(maxIterations, 1E-10, true);
|
||||
final RealVector b = new ArrayRealVector(n);
|
||||
for (int j = 0; j < n; j++) {
|
||||
b.set(0.);
|
||||
b.setEntry(j, 1.);
|
||||
final RealVector x0 = new ArrayRealVector(n);
|
||||
x0.set(1.);
|
||||
final RealVector x = solver.solve(a, b, x0, true);
|
||||
Assert.assertSame("x should be a reference to x0", x0, x);
|
||||
for (int i = 0; i < n; i++) {
|
||||
final double actual = x.getEntry(i);
|
||||
final double expected = ainv.getEntry(i, j);
|
||||
final double delta = 1E-10 * Math.abs(expected);
|
||||
final String msg = String.format("entry[%d][%d)", i, j);
|
||||
Assert.assertEquals(msg, expected, actual, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnpreconditionedSolutionWithInitialGuess() {
|
||||
final int n = 5;
|
||||
final int maxIterations = 100;
|
||||
final RealLinearOperator a = new HilbertMatrix(n);
|
||||
final InverseHilbertMatrix ainv = new InverseHilbertMatrix(n);
|
||||
final IterativeLinearSolver solver;
|
||||
solver = new ConjugateGradient(maxIterations, 1E-10, true);
|
||||
final RealVector b = new ArrayRealVector(n);
|
||||
for (int j = 0; j < n; j++) {
|
||||
b.set(0.);
|
||||
b.setEntry(j, 1.);
|
||||
final RealVector x0 = new ArrayRealVector(n);
|
||||
x0.set(1.);
|
||||
final RealVector x = solver.solve(a, b, x0, false);
|
||||
Assert.assertNotSame("x should not be a reference to x0", x0, x);
|
||||
for (int i = 0; i < n; i++) {
|
||||
final double actual = x.getEntry(i);
|
||||
final double expected = ainv.getEntry(i, j);
|
||||
final double delta = 1E-10 * Math.abs(expected);
|
||||
final String msg = String.format("entry[%d][%d]", i, j);
|
||||
Assert.assertEquals(msg, expected, actual, delta);
|
||||
Assert.assertEquals(msg, x0.getEntry(i), 1., Math.ulp(1.));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the estimate of the (updated) residual corresponds to the
|
||||
* exact residual. This fails to be true for a large number of iterations,
|
||||
* due to the loss of orthogonality of the successive search directions.
|
||||
* Therefore, in the present test, the number of iterations is limited.
|
||||
*/
|
||||
@Test(expected = MaxCountExceededException.class)
|
||||
public void testUnpreconditionedResidual() {
|
||||
final int n = 10;
|
||||
final int maxIterations = n;
|
||||
final RealLinearOperator a = new HilbertMatrix(n);
|
||||
final ConjugateGradient solver;
|
||||
solver = new ConjugateGradient(maxIterations, 1E-15, true);
|
||||
final RealVector r = new ArrayRealVector(n);
|
||||
final IterationListener listener = new IterationListener() {
|
||||
|
||||
public void terminationPerformed(final IterationEvent e) {
|
||||
r.setSubVector(0, ((ProvidesResidual) e).getResidual());
|
||||
}
|
||||
|
||||
public void iterationStarted(final IterationEvent e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void iterationPerformed(final IterationEvent e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void initializationPerformed(final IterationEvent e) {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
solver.getIterationManager().addIterationListener(listener);
|
||||
|
||||
final RealVector b = new ArrayRealVector(n);
|
||||
for (int j = 0; j < n; j++) {
|
||||
b.set(0.);
|
||||
b.setEntry(j, 1.);
|
||||
|
||||
final RealVector x = solver.solve(a, b, null, false);
|
||||
final RealVector y = a.operate(x);
|
||||
for (int i = 0; i < n; i++) {
|
||||
final double actual = b.getEntry(i) - y.getEntry(i);
|
||||
final double expected = r.getEntry(i);
|
||||
final double delta = 1E-6 * Math.abs(expected);
|
||||
final String msg = String
|
||||
.format("column %d, residual %d", i, j);
|
||||
Assert.assertEquals(msg, expected, actual, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = NonSquareLinearOperatorException.class)
|
||||
public void testNonSquarePreconditioner() {
|
||||
final Array2DRowRealMatrix a = new Array2DRowRealMatrix(2, 2);
|
||||
final InvertibleRealLinearOperator m;
|
||||
m = new InvertibleRealLinearOperator() {
|
||||
|
||||
@Override
|
||||
public RealVector operate(final RealVector x) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowDimension() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnDimension() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealVector solve(final RealVector b) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
final PreconditionedIterativeLinearSolver solver;
|
||||
solver = new ConjugateGradient(10, 0d, false);
|
||||
final ArrayRealVector b = new ArrayRealVector(a.getRowDimension());
|
||||
solver.solve(a, m, b, null, false);
|
||||
}
|
||||
|
||||
@Test(expected = DimensionMismatchException.class)
|
||||
public void testMismatchedOperatorDimensions() {
|
||||
final Array2DRowRealMatrix a = new Array2DRowRealMatrix(2, 2);
|
||||
final InvertibleRealLinearOperator m;
|
||||
m = new InvertibleRealLinearOperator() {
|
||||
|
||||
@Override
|
||||
public RealVector operate(final RealVector x) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowDimension() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnDimension() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealVector solve(final RealVector b) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
final PreconditionedIterativeLinearSolver solver;
|
||||
solver = new ConjugateGradient(10, 0d, false);
|
||||
final ArrayRealVector b = new ArrayRealVector(a.getRowDimension());
|
||||
solver.solve(a, m, b, null, false);
|
||||
}
|
||||
|
||||
@Test(expected = NonPositiveDefiniteLinearOperatorException.class)
|
||||
public void testNonPositiveDefinitePreconditioner() {
|
||||
final Array2DRowRealMatrix a = new Array2DRowRealMatrix(2, 2);
|
||||
a.setEntry(0, 0, 1d);
|
||||
a.setEntry(0, 1, 2d);
|
||||
a.setEntry(1, 0, 3d);
|
||||
a.setEntry(1, 1, 4d);
|
||||
final InvertibleRealLinearOperator m;
|
||||
m = new InvertibleRealLinearOperator() {
|
||||
|
||||
@Override
|
||||
public RealVector operate(final RealVector x) {
|
||||
final ArrayRealVector y = new ArrayRealVector(2);
|
||||
y.setEntry(0, -x.getEntry(0));
|
||||
y.setEntry(1, x.getEntry(1));
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowDimension() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnDimension() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealVector solve(final RealVector b) {
|
||||
final ArrayRealVector x = new ArrayRealVector(2);
|
||||
x.setEntry(0, -b.getEntry(0));
|
||||
x.setEntry(1, b.getEntry(1));
|
||||
return x;
|
||||
}
|
||||
};
|
||||
final PreconditionedIterativeLinearSolver solver;
|
||||
solver = new ConjugateGradient(10, 0d, true);
|
||||
final ArrayRealVector b = new ArrayRealVector(2);
|
||||
b.setEntry(0, -1d);
|
||||
b.setEntry(1, -1d);
|
||||
solver.solve(a, m, b, null, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreconditionedSolution() {
|
||||
final int n = 8;
|
||||
final int maxIterations = 100;
|
||||
final RealLinearOperator a = new HilbertMatrix(n);
|
||||
final InverseHilbertMatrix ainv = new InverseHilbertMatrix(n);
|
||||
final InvertibleRealLinearOperator m = JacobiPreconditioner.create(a);
|
||||
final PreconditionedIterativeLinearSolver solver;
|
||||
solver = new ConjugateGradient(maxIterations, 1E-15, true);
|
||||
final RealVector b = new ArrayRealVector(n);
|
||||
for (int j = 0; j < n; j++) {
|
||||
b.set(0.);
|
||||
b.setEntry(j, 1.);
|
||||
final RealVector x = solver.solve(a, m, b, null, false);
|
||||
for (int i = 0; i < n; i++) {
|
||||
final double actual = x.getEntry(i);
|
||||
final double expected = ainv.getEntry(i, j);
|
||||
final double delta = 1E-6 * Math.abs(expected);
|
||||
final String msg = String.format("coefficient (%d, %d)", i, j);
|
||||
Assert.assertEquals(msg, expected, actual, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = MaxCountExceededException.class)
|
||||
public void testPreconditionedResidual() {
|
||||
final int n = 10;
|
||||
final int maxIterations = n;
|
||||
final RealLinearOperator a = new HilbertMatrix(n);
|
||||
final InvertibleRealLinearOperator m = JacobiPreconditioner.create(a);
|
||||
final ConjugateGradient solver;
|
||||
solver = new ConjugateGradient(maxIterations, 1E-15, true);
|
||||
final RealVector r = new ArrayRealVector(n);
|
||||
final IterationListener listener = new IterationListener() {
|
||||
|
||||
public void terminationPerformed(final IterationEvent e) {
|
||||
r.setSubVector(0, ((ProvidesResidual) e).getResidual());
|
||||
}
|
||||
|
||||
public void iterationStarted(final IterationEvent e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void iterationPerformed(final IterationEvent e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void initializationPerformed(final IterationEvent e) {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
solver.getIterationManager().addIterationListener(listener);
|
||||
final RealVector b = new ArrayRealVector(n);
|
||||
|
||||
for (int j = 0; j < n; j++) {
|
||||
b.set(0.);
|
||||
b.setEntry(j, 1.);
|
||||
final RealVector x = solver.solve(a, m, b, null, false);
|
||||
final RealVector y = a.operate(x);
|
||||
double rnorm = 0.;
|
||||
for (int i = 0; i < n; i++) {
|
||||
final double actual = b.getEntry(i) - y.getEntry(i);
|
||||
final double expected = r.getEntry(i);
|
||||
final double delta = 1E-6 * Math.abs(expected);
|
||||
final String msg = String
|
||||
.format("column %d, residual %d", i, j);
|
||||
Assert.assertEquals(msg, expected, actual, delta);
|
||||
}
|
||||
rnorm = r.getNorm();
|
||||
Assert.assertEquals("norm of residual", rnorm, r.getNorm(),
|
||||
1E-6 * Math.abs(rnorm));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreconditionedSolution2() {
|
||||
final int n = 100;
|
||||
final int maxIterations = 100000;
|
||||
final Array2DRowRealMatrix a = new Array2DRowRealMatrix(n, n);
|
||||
double daux = 1.;
|
||||
for (int i = 0; i < n; i++) {
|
||||
a.setEntry(i, i, daux);
|
||||
daux *= 1.2;
|
||||
for (int j = i + 1; j < n; j++) {
|
||||
if (i == j) {
|
||||
} else {
|
||||
final double value = 1.0;
|
||||
a.setEntry(i, j, value);
|
||||
a.setEntry(j, i, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
final InvertibleRealLinearOperator m = JacobiPreconditioner.create(a);
|
||||
final PreconditionedIterativeLinearSolver pcg;
|
||||
final IterativeLinearSolver cg;
|
||||
pcg = new ConjugateGradient(maxIterations, 1E-6, true);
|
||||
cg = new ConjugateGradient(maxIterations, 1E-6, true);
|
||||
final RealVector b = new ArrayRealVector(n);
|
||||
final String pattern = "preconditioned gradient (%d iterations) should"
|
||||
+ " have been faster than unpreconditioned (%d iterations)";
|
||||
String msg;
|
||||
for (int j = 0; j < 1; j++) {
|
||||
b.set(0.);
|
||||
b.setEntry(j, 1.);
|
||||
final RealVector px = pcg.solve(a, m, b, null, false);
|
||||
final RealVector x = cg.solve(a, b, null, false);
|
||||
final int npcg = pcg.getIterationManager().getIterations();
|
||||
final int ncg = cg.getIterationManager().getIterations();
|
||||
msg = String.format(pattern, npcg, ncg);
|
||||
Assert.assertTrue(msg, npcg < ncg);
|
||||
for (int i = 0; i < n; i++) {
|
||||
msg = String.format("row %d, column %d", i, j);
|
||||
final double expected = x.getEntry(i);
|
||||
final double actual = px.getEntry(i);
|
||||
final double delta = 1E-6 * Math.abs(expected);
|
||||
Assert.assertEquals(msg, expected, actual, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventManagement() {
|
||||
final int n = 5;
|
||||
final int maxIterations = 100;
|
||||
final RealLinearOperator a = new HilbertMatrix(n);
|
||||
final IterativeLinearSolver solver;
|
||||
final int[] count = new int[] {
|
||||
0, 0, 0, 0
|
||||
};
|
||||
final IterationListener listener = new IterationListener() {
|
||||
|
||||
public void initializationPerformed(final IterationEvent e) {
|
||||
count[0] = 1;
|
||||
count[1] = 0;
|
||||
count[2] = 0;
|
||||
count[3] = 0;
|
||||
|
||||
}
|
||||
|
||||
public void iterationPerformed(final IterationEvent e) {
|
||||
++count[2];
|
||||
}
|
||||
|
||||
public void iterationStarted(IterationEvent e) {
|
||||
++count[1];
|
||||
|
||||
}
|
||||
|
||||
public void terminationPerformed(final IterationEvent e) {
|
||||
++count[3];
|
||||
}
|
||||
};
|
||||
solver = new ConjugateGradient(maxIterations, 1E-10, true);
|
||||
solver.getIterationManager().addIterationListener(listener);
|
||||
final RealVector b = new ArrayRealVector(n);
|
||||
for (int j = 0; j < n; j++) {
|
||||
b.set(0.);
|
||||
b.setEntry(j, 1.);
|
||||
solver.solve(a, b, null, false);
|
||||
String msg = String.format("column %d (initialization)", j);
|
||||
Assert.assertEquals(msg, 1, count[0]);
|
||||
msg = String.format("column %d (iterations started)", j);
|
||||
Assert.assertEquals(msg, solver.getIterationManager()
|
||||
.getIterations() - 1, count[1]);
|
||||
msg = String.format("column %d (iterations performed)", j);
|
||||
Assert.assertEquals(msg, solver.getIterationManager()
|
||||
.getIterations() - 1, count[2]);
|
||||
msg = String.format("column %d (finalization)", j);
|
||||
Assert.assertEquals(msg, 1, count[3]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.DimensionMismatchException;
|
||||
|
||||
/** This class implements Hilbert Matrices as {@link RealLinearOperator}. */
|
||||
public class HilbertMatrix
|
||||
extends RealLinearOperator {
|
||||
|
||||
/** The size of the matrix. */
|
||||
private final int n;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class.
|
||||
*
|
||||
* @param n Size of the matrix to be created..
|
||||
*/
|
||||
public HilbertMatrix(final int n) {
|
||||
this.n = n;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getColumnDimension() {
|
||||
return n;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getRowDimension() {
|
||||
return n;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public RealVector operate(final RealVector x) {
|
||||
if (x.getDimension() != n) {
|
||||
throw new DimensionMismatchException(x.getDimension(), n);
|
||||
}
|
||||
final double[] y = new double[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
double pos = 0.;
|
||||
double neg = 0.;
|
||||
for (int j = 0; j < n; j++) {
|
||||
final double xj = x.getEntry(j);
|
||||
final double coeff = 1. / (i + j + 1.);
|
||||
// Positive and negative values are sorted out in order to limit
|
||||
// catastrophic cancellations (do not forget that Hilbert
|
||||
// matrices are *very* ill-conditioned!
|
||||
if (xj > 0.) {
|
||||
pos += coeff * xj;
|
||||
} else {
|
||||
neg += coeff * xj;
|
||||
}
|
||||
}
|
||||
y[i] = pos + neg;
|
||||
}
|
||||
return new ArrayRealVector(y, false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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.DimensionMismatchException;
|
||||
import org.apache.commons.math.util.MathUtils;
|
||||
|
||||
/**
|
||||
* This class implements inverses of Hilbert Matrices as
|
||||
* {@link RealLinearOperator}.
|
||||
*/
|
||||
public class InverseHilbertMatrix
|
||||
extends RealLinearOperator {
|
||||
|
||||
/** The size of the matrix. */
|
||||
private final int n;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class.
|
||||
*
|
||||
* @param n Size of the matrix to be created.
|
||||
*/
|
||||
public InverseHilbertMatrix(final int n) {
|
||||
this.n = n;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getColumnDimension() {
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code (i, j)} entry of the inverse Hilbert matrix. Exact
|
||||
* arithmetic is used; in case of overflow, an exception is thrown.
|
||||
*
|
||||
* @param i Row index (starts at 0).
|
||||
* @param j Column index (starts at 0).
|
||||
* @return The coefficient of the inverse Hilbert matrix.
|
||||
*/
|
||||
public long getEntry(final int i, final int j) {
|
||||
long val = i + j + 1;
|
||||
long aux = MathUtils.binomialCoefficient(n + i, n - j - 1);
|
||||
val = MathUtils.mulAndCheck(val, aux);
|
||||
aux = MathUtils.binomialCoefficient(n + j, n - i - 1);
|
||||
val = MathUtils.mulAndCheck(val, aux);
|
||||
aux = MathUtils.binomialCoefficient(i + j, i);
|
||||
val = MathUtils.mulAndCheck(val, aux);
|
||||
val = MathUtils.mulAndCheck(val, aux);
|
||||
return ((i + j) & 1) == 0 ? val : -val;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getRowDimension() {
|
||||
return n;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public RealVector operate(final RealVector x) {
|
||||
if (x.getDimension() != n) {
|
||||
throw new DimensionMismatchException(x.getDimension(), n);
|
||||
}
|
||||
final double[] y = new double[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
double pos = 0.;
|
||||
double neg = 0.;
|
||||
for (int j = 0; j < n; j++) {
|
||||
final double xj = x.getEntry(j);
|
||||
final long coeff = getEntry(i, j);
|
||||
final double daux = coeff * xj;
|
||||
// Positive and negative values are sorted out in order to limit
|
||||
// catastrophic cancellations (do not forget that Hilbert
|
||||
// matrices are *very* ill-conditioned!
|
||||
if (daux > 0.) {
|
||||
pos += daux;
|
||||
} else {
|
||||
neg += daux;
|
||||
}
|
||||
}
|
||||
y[i] = pos + neg;
|
||||
}
|
||||
return new ArrayRealVector(y, false);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue