cern.colt.matrix.tdouble.algo.solver.preconditioner.DoubleAMG Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of parallelcolt Show documentation
Show all versions of parallelcolt Show documentation
Parallel Colt is a multithreaded version of Colt - a library for high performance scientific computing in Java. It contains efficient algorithms for data analysis, linear algebra, multi-dimensional arrays, Fourier transforms, statistics and histogramming.
The newest version!
/*
* Copyright (C) 2003-2006 Bjørn-Ove Heimsund
*
* This file is part of MTJ.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package cern.colt.matrix.tdouble.algo.solver.preconditioner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import cern.colt.matrix.tdouble.DoubleMatrix1D;
import cern.colt.matrix.tdouble.DoubleMatrix2D;
import cern.colt.matrix.tdouble.algo.decomposition.DenseDoubleLUDecompositionQuick;
import cern.colt.matrix.tdouble.impl.DenseDoubleMatrix1D;
import cern.colt.matrix.tdouble.impl.DenseDoubleMatrix2D;
import cern.colt.matrix.tdouble.impl.SparseCCDoubleMatrix2D;
import cern.colt.matrix.tdouble.impl.SparseCCMDoubleMatrix2D;
import cern.colt.matrix.tdouble.impl.SparseRCDoubleMatrix2D;
import cern.colt.matrix.tdouble.impl.SparseRCMDoubleMatrix2D;
/**
* Algebraic multigrid preconditioner. Uses the smoothed aggregation method
* described by Vanek, Mandel, and Brezina (1996).
*/
public class DoubleAMG implements DoublePreconditioner {
/**
* Relaxations at each level
*/
private DoubleAMG.SSOR[] preM, postM;
/**
* The number of levels
*/
private int m;
/**
* System matrix at each level, except at the coarsest
*/
private SparseRCDoubleMatrix2D[] A;
/**
* LU factorization at the coarsest level
*/
private DenseDoubleLUDecompositionQuick lu;
/**
* Solution, right-hand side, and residual vectors at each level
*/
private DenseDoubleMatrix1D[] u, f, r;
/**
* Interpolation operators going to a finer mesh
*/
private SparseCCDoubleMatrix2D[] I;
/**
* Smallest matrix size before terminating the AMG setup phase. Matrices
* smaller than this will be solved by a direct solver
*/
private final int min;
/**
* Number of times to perform the pre- and post-smoothings
*/
private final int nu1, nu2;
/**
* Determines cycle type. gamma=1 is V, gamma=2 is W
*/
private final int gamma;
/**
* Overrelaxation parameters in the pre- and post-smoothings, and with the
* possibility of distinct values in the forward and reverse sweeps
*/
private final double omegaPreF, omegaPreR, omegaPostF, omegaPostR;
/**
* Perform a reverse (backwards) smoothing sweep
*/
private final boolean reverse;
/**
* Jacobi damping parameter, between zero and one. If it equals zero, the
* method reduces to the standard aggregate multigrid method
*/
private final double omega;
/**
* Operating in transpose mode?
*/
private boolean transpose;
/**
* Sets up the algebraic multigrid preconditioner
*
* @param omegaPreF
* Overrelaxation parameter in the forward sweep of the
* pre-smoothing
* @param omegaPreR
* Overrelaxation parameter in the backwards sweep of the
* pre-smoothing
* @param omegaPostF
* Overrelaxation parameter in the forward sweep of the
* post-smoothing
* @param omegaPostR
* Overrelaxation parameter in the backwards sweep of the
* post-smoothing
* @param nu1
* Number of pre-relaxations to perform
* @param nu2
* Number of post-relaxations to perform
* @param gamma
* Number of times to go to a coarser level
* @param min
* Smallest matrix size before using a direct solver
* @param omega
* Jacobi damping parameter, between zero and one. If it equals
* zero, the method reduces to the standard aggregate multigrid
* method
*/
public DoubleAMG(double omegaPreF, double omegaPreR, double omegaPostF, double omegaPostR, int nu1, int nu2,
int gamma, int min, double omega) {
this.omegaPreF = omegaPreF;
this.omegaPreR = omegaPreR;
this.omegaPostF = omegaPostF;
this.omegaPostR = omegaPostR;
reverse = true;
this.nu1 = nu1;
this.nu2 = nu2;
this.gamma = gamma;
this.min = min;
this.omega = omega;
}
/**
* Sets up the algebraic multigrid preconditioner. Uses an SOR method,
* without the backward sweep in SSOR
*
* @param omegaPre
* Overrelaxation parameter in the pre-smoothing
* @param omegaPost
* Overrelaxation parameter in the post-smoothing
* @param nu1
* Number of pre-relaxations to perform
* @param nu2
* Number of post-relaxations to perform
* @param gamma
* Number of times to go to a coarser level
* @param min
* Smallest matrix size before using a direct solver
* @param omega
* Jacobi damping parameter, between zero and one. If it equals
* zero, the method reduces to the standard aggregate multigrid
* method
*/
public DoubleAMG(double omegaPre, double omegaPost, int nu1, int nu2, int gamma, int min, double omega) {
this.omegaPreF = omegaPre;
this.omegaPreR = omegaPre;
this.omegaPostF = omegaPost;
this.omegaPostR = omegaPost;
reverse = false;
this.nu1 = nu1;
this.nu2 = nu2;
this.gamma = gamma;
this.min = min;
this.omega = omega;
}
/**
* Sets up the algebraic multigrid preconditioner using some default
* parameters. In the presmoothing, omegaF=1
and
* omegaR=1.85
, while in the postsmoothing,
* omegaF=1.85
and omegaR=1
. Sets
* nu1=nu2=gamma=1
, has a smallest matrix size of 40, and sets
* omega=2/3
.
*/
public DoubleAMG() {
this(1, 1.85, 1.85, 1, 1, 1, 1, 40, 2. / 3);
}
public DoubleMatrix1D apply(DoubleMatrix1D b, DoubleMatrix1D x) {
if (x == null) {
x = b.like();
}
u[0].assign(x);
f[0].assign(b);
transpose = false;
cycle(0);
return x.assign(u[0]);
}
public DoubleMatrix1D transApply(DoubleMatrix1D b, DoubleMatrix1D x) {
if (x == null) {
x = b.like();
}
u[0].assign(x);
f[0].assign(b);
transpose = true;
cycle(0);
return x.assign(u[0]);
}
public void setMatrix(DoubleMatrix2D A) {
List Al = new LinkedList();
List Il = new LinkedList();
SparseRCDoubleMatrix2D Arc = new SparseRCDoubleMatrix2D(A.rows(), A.columns());
Arc.assign(A);
if (!Arc.hasColumnIndexesSorted())
Arc.sortColumnIndexes();
Al.add(Arc);
for (int k = 0; Al.get(k).rows() > min; ++k) {
SparseRCDoubleMatrix2D Af = Al.get(k);
double eps = 0.08 * Math.pow(0.5, k);
// Create the aggregates
Aggregator aggregator = new Aggregator(Af, eps);
// If no aggregates were created, no interpolation operator will be
// created, and the setup phase stops
if (aggregator.getAggregates().size() == 0)
break;
// Create an interpolation operator using smoothing. This also
// creates the Galerkin operator
Interpolator sa = new Interpolator(aggregator, Af, omega);
Al.add(sa.getGalerkinOperator());
Il.add(sa.getInterpolationOperator());
}
// Copy to array storage
m = Al.size();
if (m == 0)
throw new RuntimeException("Matrix too small for AMG");
I = new SparseCCDoubleMatrix2D[m - 1];
this.A = new SparseRCDoubleMatrix2D[m - 1];
Il.toArray(I);
for (int i = 0; i < Al.size() - 1; ++i)
this.A[i] = Al.get(i);
// Create a LU decomposition of the smallest Galerkin matrix
DenseDoubleMatrix2D Ac = new DenseDoubleMatrix2D(Al.get(Al.size() - 1).toArray());
lu = new DenseDoubleLUDecompositionQuick();
lu.decompose(Ac);
// Allocate vectors at each level
u = new DenseDoubleMatrix1D[m];
f = new DenseDoubleMatrix1D[m];
r = new DenseDoubleMatrix1D[m];
for (int k = 0; k < m; ++k) {
int n = Al.get(k).rows();
u[k] = new DenseDoubleMatrix1D(n);
f[k] = new DenseDoubleMatrix1D(n);
r[k] = new DenseDoubleMatrix1D(n);
}
// Set up the SSOR relaxation schemes
preM = new SSOR[m - 1];
postM = new SSOR[m - 1];
for (int k = 0; k < m - 1; ++k) {
SparseRCDoubleMatrix2D Ak = this.A[k];
preM[k] = new SSOR(Ak, reverse, omegaPreF, omegaPreR);
postM[k] = new SSOR(Ak, reverse, omegaPostF, omegaPostR);
preM[k].setMatrix(Ak);
postM[k].setMatrix(Ak);
}
}
/**
* Performs a multigrid cycle
*
* @param k
* Level to cycle at. Start by calling cycle(0)
*/
private void cycle(int k) {
if (k == m - 1)
directSolve();
else {
// Presmoothings
preRelax(k);
u[k + 1].assign(0);
// Compute the residual
A[k].zMult(u[k], r[k].assign(f[k]), -1, 1, false);
// Restrict to the next coarser level
I[k].zMult(r[k], f[k + 1], 1, 0, true);
// Recurse to next level
for (int i = 0; i < gamma; ++i)
cycle(k + 1);
// Add residual correction by prolongation
I[k].zMult(u[k + 1], u[k], 1, 1, false);
// Postsmoothings
postRelax(k);
}
}
/**
* Solves directly at the coarsest level
*/
private void directSolve() {
int k = m - 1;
u[k].assign(f[k]);
if (transpose) {
lu.setLU(lu.getLU().viewDice());
lu.solve(u[k]);
lu.setLU(lu.getLU().viewDice());
} else
lu.solve(u[k]);
}
/**
* Applies the relaxation scheme at the given level
*
* @param k
* Multigrid level
*/
private void preRelax(int k) {
for (int i = 0; i < nu1; ++i)
if (transpose)
preM[k].transApply(f[k], u[k]);
else
preM[k].apply(f[k], u[k]);
}
/**
* Applies the relaxation scheme at the given level
*
* @param k
* Multigrid level
*/
private void postRelax(int k) {
for (int i = 0; i < nu2; ++i)
if (transpose)
postM[k].transApply(f[k], u[k]);
else
postM[k].apply(f[k], u[k]);
}
/**
* Creates aggregates. These are disjoint sets, each of which represents one
* node at a coarser mesh by aggregating together a set of fine nodes
*/
private static class Aggregator {
/**
* The aggregates
*/
private List> C;
/**
* Diagonal indexes into the sparse matrix
*/
private int[] diagind;
/**
* The strongly coupled node neighborhood of a given node
*/
private List> N;
/**
* Creates the aggregates
*
* @param A
* Sparse matrix
* @param eps
* Tolerance for selecting the strongly coupled node
* neighborhoods. Between zero and one.
*/
public Aggregator(SparseRCDoubleMatrix2D A, double eps) {
diagind = findDiagonalindexes(A);
N = findNodeNeighborhood(A, diagind, eps);
/*
* Initialization. Remove isolated nodes from the aggregates
*/
boolean[] R = createInitialR(A);
/*
* Startup aggregation. Use disjoint strongly coupled neighborhoods
* as the initial aggregate approximation
*/
C = createInitialAggregates(N, R);
/*
* Enlargment of the aggregates. Add nodes to each aggregate based
* on how strongly connected the nodes are to a given aggregate
*/
C = enlargeAggregates(C, N, R);
/*
* Handling of the remenants. Put all remaining unallocated nodes
* into new aggregates defined by the intersection of N and R
*/
C = createFinalAggregates(C, N, R);
}
/**
* Gets the aggregates
*/
public List> getAggregates() {
return C;
}
/**
* Returns the matrix diagonal indexes. This is a by-product of the
* aggregation
*/
public int[] getDiagonalindexes() {
return diagind;
}
/**
* Returns the strongly coupled node neighborhoods of a given node. This
* is a by-product of the aggregation
*/
public List> getNodeNeighborhoods() {
return N;
}
/**
* Finds the diagonal indexes of the matrix
*/
private int[] findDiagonalindexes(SparseRCDoubleMatrix2D A) {
int[] rowptr = A.getRowPointers();
int[] colind = A.getColumnIndexes();
int[] diagind = new int[A.rows()];
for (int i = 0; i < A.rows(); ++i) {
diagind[i] = cern.colt.Sorting.binarySearchFromTo(colind, i, rowptr[i], rowptr[i + 1]);
if (diagind[i] < 0)
throw new RuntimeException("Matrix is missing a diagonal entry on row " + (i + 1));
}
return diagind;
}
/**
* Finds the strongly coupled node neighborhoods
*/
private List> findNodeNeighborhood(SparseRCDoubleMatrix2D A, int[] diagind, double eps) {
N = new ArrayList>(A.rows());
int[] rowptr = A.getRowPointers();
int[] colind = A.getColumnIndexes();
double[] data = A.getValues();
for (int i = 0; i < A.rows(); ++i) {
Set Ni = new HashSet();
double aii = data[diagind[i]];
for (int j = rowptr[i]; j < rowptr[i + 1]; ++j) {
double aij = data[j];
double ajj = data[diagind[colind[j]]];
if (Math.abs(aij) >= eps * Math.sqrt(aii * ajj))
Ni.add(colind[j]);
}
N.add(Ni);
}
return N;
}
/**
* Creates the initial R-set by including only the connected nodes
*/
private boolean[] createInitialR(SparseRCDoubleMatrix2D A) {
boolean[] R = new boolean[A.rows()];
int[] rowptr = A.getRowPointers();
int[] colind = A.getColumnIndexes();
double[] data = A.getValues();
for (int i = 0; i < A.rows(); ++i) {
boolean hasOffDiagonal = false;
for (int j = rowptr[i]; j < rowptr[i + 1]; ++j)
if (colind[j] != i && data[j] != 0) {
hasOffDiagonal = true;
break;
}
R[i] = hasOffDiagonal;
}
return R;
}
/**
* Creates the initial aggregates
*/
private List> createInitialAggregates(List> N, boolean[] R) {
C = new ArrayList>();
for (int i = 0; i < R.length; ++i) {
// Skip non-free nodes
if (!R[i])
continue;
// See if all nodes in the current N-set are free
boolean free = true;
for (int j : N.get(i))
free &= R[j];
// Create an aggregate out of N[i]
if (free) {
C.add(new HashSet(N.get(i)));
for (int j : N.get(i))
R[j] = false;
}
}
return C;
}
/**
* Enlarges the aggregates
*/
private List> enlargeAggregates(List> C, List> N, boolean[] R) {
// Contains the aggregates each node is coupled to
List> belong = new ArrayList>(R.length);
for (int i = 0; i < R.length; ++i)
belong.add(new ArrayList());
// Find which aggregate each node is coupled to. This is used for
// the intersection between Ni and Ck
for (int k = 0; k < C.size(); ++k)
for (int j : C.get(k))
belong.get(j).add(k);
// Number of nodes in the intersection between each C and Ni
int[] intersect = new int[C.size()];
for (int i = 0; i < R.length; ++i) {
// Skip non-free nodes
if (!R[i])
continue;
// Find the number of nodes intersecting Ni and every C, and
// keep a track on the largest overlap
Arrays.fill(intersect, 0);
int largest = 0, maxValue = 0;
for (int j : N.get(i))
// The k-index is to an aggregate coupled to node j
for (int k : belong.get(j)) {
intersect[k]++;
if (intersect[k] > maxValue) {
largest = k;
maxValue = intersect[largest];
}
}
// Add the node to the proper C-set, and mark it as used
// Also, check if the node actually does couple to a set
if (maxValue > 0) {
R[i] = false;
C.get(largest).add(i);
}
}
return C;
}
/**
* Creates final aggregates from the remaining unallocated nodes
*/
private List> createFinalAggregates(List> C, List> N, boolean[] R) {
for (int i = 0; i < R.length; ++i) {
// Skip non-free nodes
if (!R[i])
continue;
// Create new aggregate from the nodes in N[i] which are free
Set Cn = new HashSet();
for (int j : N.get(i))
if (R[j]) {
R[j] = false;
Cn.add(j);
}
if (!Cn.isEmpty())
C.add(Cn);
}
return C;
}
}
/**
* Creates interpolation (prolongation) operators using based on the
* aggregates. Can optionally smooth the aggregates
*/
private static class Interpolator {
/**
* The Galerkin coarse-space operator
*/
private SparseRCDoubleMatrix2D Ac;
/**
* The interpolation (prolongation) matrix
*/
private SparseCCDoubleMatrix2D I;
/**
* Creates the interpolation (prolongation) and Galerkin operators
*
* @param aggregator
* Aggregates
* @param A
* Matrix
* @param omega
* Jacobi damping parameter between zero and one. If zero, no
* smoothing is performed, and a faster algorithm for forming
* the Galerkin operator will be used.
*/
public Interpolator(Aggregator aggregator, SparseRCDoubleMatrix2D A, double omega) {
List> C = aggregator.getAggregates();
List> N = aggregator.getNodeNeighborhoods();
int[] diagind = aggregator.getDiagonalindexes();
// Create the tentative prolongation, in compressed form
int[] pt = createTentativeProlongation(C, A.rows());
/*
* Apply Jacobi smoothing to the prolongator
*/
if (omega != 0) {
// Smooth the operator by a damped Jacobi method
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy