ComplexRumpMethod.java
/*
* Created on 2004/08/03
*
* Copyright 2004. Created by hiroki
*/
package org.mklab.cga.eigen;
import org.mklab.cga.interval.matrix.IntervalComplexNumericalMatrix;
import org.mklab.cga.interval.matrix.IntervalRealNumericalMatrix;
import org.mklab.cga.interval.scalar.IntervalComplexNumericalScalar;
import org.mklab.cga.interval.scalar.IntervalRealNumericalScalar;
import org.mklab.cga.util.IntervalUtil;
import org.mklab.nfc.eig.EigenSolution;
import org.mklab.nfc.matrix.BooleanMatrix;
import org.mklab.nfc.matrix.ComplexNumericalMatrix;
import org.mklab.nfc.matrix.ElementHolder;
import org.mklab.nfc.matrix.IndexedMatrix;
import org.mklab.nfc.matrix.IntMatrix;
import org.mklab.nfc.matrix.RealNumericalMatrix;
import org.mklab.nfc.scalar.ComplexNumericalScalar;
import org.mklab.nfc.scalar.RealNumericalScalar;
import org.mklab.nfc.util.RoundMode;
import org.mklab.nfc.util.RoundModeManager;
/**
* 多重および近接固有値の精度保証を行うクラスです。 Rumpの方法を実装しています。
*
* @author hiroki
* @param <RIS> 実区間スカラーの型
* @param <RIM> 実区間行列の型
* @param <CIS> 複素区間スカラーの型
* @param <CIM> 複素区間行列の型
* @param <RS> 実スカラーの型
* @param <RM> 実行列の型
* @param <CS> 複素スカラーの型
* @param <CM> 複素行列の型
*/
public class ComplexRumpMethod<RIS extends IntervalRealNumericalScalar<RIS, RIM, CIS, CIM, RS, RM, CS, CM>, RIM extends IntervalRealNumericalMatrix<RIS, RIM, CIS, CIM, RS, RM, CS, CM>, CIS extends IntervalComplexNumericalScalar<RIS, RIM, CIS, CIM, RS, RM, CS, CM>, CIM extends IntervalComplexNumericalMatrix<RIS, RIM, CIS, CIM, RS, RM, CS, CM>, RS extends RealNumericalScalar<RS, RM, CS, CM>, RM extends RealNumericalMatrix<RS, RM, CS, CM>, CS extends ComplexNumericalScalar<RS, RM, CS, CM>, CM extends ComplexNumericalMatrix<RS, RM, CS, CM>>implements ComplexEigenVerifier<RIS,RIM,CIS,CIM,RS,RM,CS,CM> {
/** 対象となる行列 */
private CM A;
/** 対象となる区間行列 */
private CIM IA;
/** 精度保証付き固有値 */
private CIM eigenValue;
/** 精度保証付き固有ベクトル(不変部分空間の基底ベクトル) */
private CIM eigenVector;
// /**
// * コンストラクタ。
// */
// public ComplexRumpMethod() {
// //
// }
// /**
// * 新しく生成された<code>RumpMethod</code>オブジェクトを初期化します。
// *
// * @param A matrix
// * @param IA interval matrix
// */
// public ComplexRumpMethod(CM A, CIM IA) {
// this.A = A;
// this.IA = IA;
// }
// /**
// * 新しく生成された<code>RumpMethod</code>オブジェクトを初期化します。
// *
// * @param IA 対象となる区間行列
// */
// public ComplexRumpMethod(CIM IA) {
// this.IA = IA;
// this.A = IA.getMiddle();
// }
/**
* {@inheritDoc}
*/
public CIM getEigenValue() {
return this.eigenValue;
}
/**
* {@inheritDoc}
*/
public CIM getEigenVector() {
return this.eigenVector;
}
// /**
// * @see org.mklab.cga.eigen.EigenVerifier#getSolution()
// */
// public EigenVerifiedSolution<RIS,RIM,CIS,CIM,RS,RM,CS,CM> getSolution() {
// return new EigenVerifiedSolution<>(new IntervalMatrix[] {this.eigenValue, this.eigenVector});
// }
// /**
// * {@inheritDoc}
// */
// public void solve() {
// solve(this.IA);
// }
// /**
// * @see org.mklab.cga.eigen.EigenVerifier#solveAsSparse()
// */
// public void solveAsSparse() {
// if (this.A != null) {
// solveAsSparse(this.A);
// } else if (this.IA != null) {
// solveAsSparse(this.IA);
// } else {
// throw new RuntimeException("Target matrix is not given."); //$NON-NLS-1$
// }
// }
// /**
// * {@inheritDoc}
// */
// public void solve(final CM matrix) {
// final EigenSolution<RS,RM,CS,CM> eigen = matrix.eigenDecompose();
// CM value = eigen.getValues();
// CM vector = eigen.getVectors();
// this.eigenValue = this.IA.create(value.createZero(value.getRowSize(), 1));
// this.eigenVector = this.IA.create(vector.createZero(vector.getRowSize(), vector.getColumnSize()));
//
// for (int i = 0; i < value.getRowSize(); i++) {
// final CS rambda = value.getElement(i + 1, i + 1);
// final CM xs = vector.getColumnVector(i + 1);
// final CIM[] ans = solve(matrix, rambda, xs);
// this.eigenValue.setRowVector(i + 1, ans[0]);// 固有値
// this.eigenVector.setColumnVector(i + 1, ans[1]);// 不変部分空間の基底
// }
// }
/**
* {@inheritDoc}
*/
public void solve(final CIM matrix) {
this.IA = matrix;
this.A = matrix.getMiddle();
final EigenSolution<RS,RM,CS,CM> eigen = matrix.getMiddle().eigenDecompose();
CM value = eigen.getValue();
CM vector = eigen.getVector();
this.eigenValue = this.IA.create(value.createZero(value.getRowSize(), 1));
this.eigenVector = this.IA.create(eigen.getVector().createZero(vector.getRowSize(), vector.getColumnSize()));
for (int i = 0; i < value.getRowSize(); i++) {
final CS rambda = (value).getElement(i + 1, i + 1);
final CM xs = vector.getColumnVector(i + 1);
final CIM[] ans = solve(matrix, rambda, xs);
this.eigenValue.setRowVector(i + 1, ans[0]);// 固有値
this.eigenVector.setColumnVector(i + 1, ans[1]);// 不変部分空間の基底
}
}
/**
* 多重および近接固有値の精度保証を行います。
*
* 一つの固有値に対し、精度保証を行います。
*
* 近接する固有値の境界点を求めたい場合は、 <code>xs</code> には 近接する固有値の固有ベクトル列を設定してください。
*
* @param matrix 固有値を求めたい行列
* @param lambda Aの固有値のひとつ
* @param xs lambdaに対応する固有ベクトル
* @return {精度保証付き固有値,精度保証付き固有ベクトル(不変部分空間の基底)}
*/
private CIM[] solve(final CM matrix, CS lambda, final CM xs) {
return solve(createInterval(matrix), lambda, xs);
}
/**
* 精度保証付き固有値と精度保証付き固有ベクトル求めます。
*
* @param intervalMatrix 固有値を求めたい区間行列
* @param lambda Aの固有値のひとつ
* @param xs lambdaに対応する固有ベクトル
* @return {精度保証付き固有値,精度保証付き固有ベクトル(不変部分空間の基底)}
*/
@SuppressWarnings("unchecked")
private CIM[] solve(CIM intervalMatrix, CS lambda, CM xs) {
RoundModeManager manager = RoundModeManager.getManager();
RoundMode oldRoundMode = manager.getRoundMode();
int n = xs.getRowSize();// 行数
int k = xs.getColumnSize();// 列数
manager.setRoundMode(RoundMode.ROUND_NEAR);
CM xss = (xs).absElementWise().sumRowWise();
IndexedMatrix<CS,CM> NI = (xss).sortColumnWise();
IntMatrix I = NI.getIndices();
IntMatrix u = I.getRowVectors(1, n - k).transpose();
IntMatrix v = I.getRowVectors(n - k + 1, n).transpose();
CM midA = intervalMatrix.getMiddle();
// TODO Aが実数行列じゃないときもこれでいいの?
CM R = midA.subtract(midA.createUnit(n).multiply(lambda));// R=midA-lambda*I
R.setColumnVectors(v, xs.unaryMinus()); // R(:, v) = -xs
CM y = R.leftDivide(midA.multiply(xs).subtract(xs.multiply(lambda)));// y = R\(midA*xs-lambda*xs)
xs.setRowVectors(u, (xs.getRowVectors(u).subtract(y.getRowVectors(u))));// xs(u,:)= xs(u,:)-y(u,:)
// lambda - sum(diag(y(v,:)))/k
CS locallambda = lambda.subtract((y.getRowVectors(v).diagonalToVector().sumColumnWise()).getElement(1,1).divide(k));
R = midA.subtract(midA.createUnit(n).multiply(locallambda));// R=midA-lambda*I
R.setColumnVectors(v, xs.unaryMinus());// R(:,v)=-xs
R = R.inverse();// R=inv(R)
CIM II = createInterval(midA.createUnit(n));// 単位行列のIntervalMatrixの作成
CIS I_lambda = createInterval(locallambda);
CIM IR = createInterval(R);
CIM IC = intervalMatrix.subtract(II.multiply(I_lambda));// C=A-intval(lambda)*I
CIM IZ = (createInterval(R.unaryMinus())).multiply(IC.multiply(createInterval(xs)));// Z=-R*(C*xs)
IC.setColumnVectors(v, createInterval(xs.unaryMinus()));// C(:, v)=-xs
IC = II.subtract(IR.multiply(IC));// C=I-RC
CIM IY = IZ.createClone();// Y=Z
CIM Eps = createInterval(IY.abssElementWise().multiply(0.1)).multiply(createIntervalInfSup(-1.0, 1.0)).addElementWise(createMidRad(0, Double.MIN_VALUE));
// TODO こうしたらいいのかな?(NumericalMatrixElementIntervalの実装が必要みたい)
CIM Z_row = IZ.getRowVectors(v);
CM Z_row_abs = Z_row.abssElementWise();
BooleanMatrix Z_flag = Z_row_abs.compareElementWise(".>", 0.1); //$NON-NLS-1$
int z_sum = Z_flag.find().length();
double m = 0;
double mmax = Math.min(15 * (z_sum + 1), 20);
boolean ready = false;
CIM X = null;
CIM XX = createInterval(R.createZero(IY.getRowSize(), IY.getColumnSize()));
while ((!ready) && (m < mmax) && (!IY.isNanElementWise().anyTrue())) {
m = m + 1;
X = IY.add(Eps);
XX.copy(X);
XX.setRowVectors(v, XX.createZero(v.getColumnSize(), XX.getColumnSize()));
IY = IZ.add(IC.multiply(X)).add(IR.multiply(XX.multiply(X.getRowVectors(v))));
ready = IntervalUtil.in0forRump(IY, X).allTrue();
}
CIM L = null;
if (ready) {
RM M = (IY.getRowVectors(v)).abssElementWise().getRealPart();
if (v.length() == 1) {
CS[] value = locallambda.createArray(1);
value[0] = locallambda;
CM mid = locallambda.createGrid(value);
L = createMidRad(mid, M);
} else {
EigenSolution<RS,RM,CS,CM> EV = (M).eigenDecompose();
ElementHolder<?> list = ((EV.getValue().diagonalToVector()).absElementWise()).maximum();
int index = list.getRow();
CM Perronx = EV.getVector().getColumnVector(index);
manager.setRoundMode(RoundMode.ROUND_UP);
/* upper bound for Perron root */
RM rad = ((M.toComplex().multiply(Perronx).divideElementWise(Perronx))).maxColumnWise().getRealPart();
manager.setRoundMode(RoundMode.ROUND_NEAR);
CS[] value = locallambda.createArray(1);
value[0] = locallambda;
CM mid = locallambda.createGrid(value);
L = createMidRad(mid, rad);
}
IY.setRowVectors(v, IY.createZero(v.getColumnSize(), IY.getColumnSize()));
X = createInterval(xs).add(IY);
} else {
CS nanElement = intervalMatrix.getMiddle().getElement(1).getNaN();
CS[] value = nanElement.createArray(1);
value[0] = nanElement;
CM nan = intervalMatrix.getMiddle().getElement(1).createGrid(value);
CM nanL = xs.createZero(1, 1);
nanL.setRowVector(1, nan);
CM nanX = xs.createZero(intervalMatrix.getRowSize(), 1);
for (int i = 0; i < intervalMatrix.getColumnSize(); i++) {
nanX.setRowVector(i + 1, nanL);
}
L = createMidRad(nanL, nanL.getRealPart());
X = createMidRad(nanX, nanX.getRealPart());
System.err.println("no inclusion achieved"); //$NON-NLS-1$
//throw new RuntimeException("no inclusion achieved"); //$NON-NLS-1$
}
manager.setRoundMode(oldRoundMode);
CIM[] ans = (CIM[])new IntervalComplexNumericalMatrix[2];
ans[0] = L;
ans[1] = X;
return ans;
}
/**
* @param IA 固有値を求めたい区間行列
* @param lambda Aの固有値のひとつ
* @param xs lambdaに対応する固有ベクトル
* @return {精度保証付き固有値,精度保証付き固有ベクトル(不変部分空間の基底)}
*/
private CIM[] solveSparse(CIM IA, CS lambda, CM xs) {
RoundModeManager manager = RoundModeManager.getManager();
RoundMode oldRoundMode = manager.getRoundMode();
int n = xs.getRowSize();// 行数
int k = xs.getColumnSize();// 列数
manager.setRoundMode(RoundMode.ROUND_NEAR);
CM xss = (xs).absElementWise().sumRowWise();
IndexedMatrix<CS,CM> NI = (xss).sortColumnWise();
IntMatrix I = NI.getIndices();
IntMatrix v = I.getRowVectors(n - k + 1, n).transpose();
CM midA = IA.getMiddle();
CS locallambda = lambda.clone();
CM R = midA.subtract(midA.createUnit(n).multiply(locallambda));// R=midA-lambda*I
R.setColumnVectors(v, xs.unaryMinus());// R(:,v)=-xs
CIM G = createInterval(R).inverse();
CIM IZ = IA.unaryMinus().multiply(createInterval(xs)).add(createInterval(xs.multiply(lambda)));
CIM IY = IZ.createClone();// Y=Z
// TODO ここ精度に依存しそう
CIM Eps = createInterval(IY.abssElementWise().multiply(0.1)).multiply(createIntervalInfSup(-1.0, 1.0)).addElementWise(createMidRad(0, Double.MIN_VALUE));
CIM Z_row = IZ.getRowVectors(v);
CM Z_row_abs = Z_row.abssElementWise();
BooleanMatrix Z_flag = Z_row_abs.compareElementWise(".>", 0.1); //$NON-NLS-1$
int z_sum = Z_flag.find().length();
double m = 0;
double mmax = Math.min(15 * (z_sum + 1), 20);
boolean ready = false;
CIM X = null;
CIM XX = createInterval(R.createZero(IY.getRowSize(), IY.getColumnSize()));
while ((!ready) && (m < mmax) && (!IY.isNanElementWise().anyTrue())) {
m = m + 1;
X = IY.add(Eps);
XX.copy(X);
XX.setRowVectors(v, XX.createZero(v.getColumnSize(), XX.getColumnSize()));
CIM b = IZ.add(XX.multiply(X.getRowVectors(v)));
IY = G.inverse().multiply(b);
ready = IntervalUtil.in0forRump(IY, X).allTrue();
}
CIM L = null;
if (ready) {
RM M = (IY.getRowVectors(v)).abssElementWise().getRealPart();
if (v.length() == 1) {
CS[] value = locallambda.createArray(1);
value[0] = locallambda;
CM mid = locallambda.createGrid(value);
L = createMidRad(mid, M);
} else {
EigenSolution<RS,RM,CS,CM> EV = (M).eigenDecompose();
ElementHolder<?> list = (EV.getValue().diagonalToVector()).absElementWise().maximum();
int index = list.getRow();
CM Perronx = EV.getVector().getColumnVector(index);
manager.setRoundMode(RoundMode.ROUND_UP);
/* upper bound for Perron root */
RM rad = ((M.toComplex().multiply(Perronx).divideElementWise(Perronx))).maxColumnWise().getRealPart();
manager.setRoundMode(RoundMode.ROUND_NEAR);
CS[] value = locallambda.createArray(1);
value[0] = locallambda;
CM mid = locallambda.createGrid(value);
L = createMidRad(mid, rad);
}
IY.setRowVectors(v, IY.createZero(v.getColumnSize(), IY.getColumnSize()));
X = createInterval(xs).add(IY);
} else {
CS nanElement = IA.getMiddle().getElement(1).getNaN();
CS[] value = nanElement.createArray(1);
value[0] = nanElement;
CM nan = IA.getMiddle().getElement(1).createGrid(value);
CM nanL = xs.createZero(1, 1);
nanL.setRowVector(1, nan);
CM nanX = xs.createZero(IA.getRowSize(), 1);
for (int i = 0; i < IA.getColumnSize(); i++) {
nanX.setRowVector(i + 1, nanL);
}
L = createMidRad(nanL, nanL.getRealPart());
X = createMidRad(nanX, nanX.getRealPart());
System.err.println("no inclusion achieved"); //$NON-NLS-1$
//throw new RuntimeException("no inclusion achieved"); //$NON-NLS-1$
}
manager.setRoundMode(oldRoundMode);
CIM[] ans = (CIM[])new IntervalComplexNumericalMatrix[2];
ans[0] = L;
ans[1] = X;
return ans;
}
// /**
// * {@inheritDoc}
// */
// public void solveAsSparse(CM A) {
// throw new UnsupportedOperationException();
// }
/**
* {@inheritDoc}
*/
public void solveAsSparse(CIM a) {
final EigenSolution<RS,RM,CS,CM> eigen = (a.getMiddle()).eigenDecompose();
CM value = eigen.getValue();
CM vector = eigen.getVector();
this.eigenValue = this.IA.create(value.createZero(value.getRowSize(), 1));
this.eigenVector = this.IA.create(vector.createZero(vector.getRowSize(), vector.getColumnSize()));
for (int i = 0; i < value.getRowSize(); i++) {
final CS rambda = (value).getElement(i + 1, i + 1);
final CM xs = vector.getColumnVector(i + 1);
final CIM[] ans = solveSparse(a, rambda, xs);
this.eigenValue.setRowVector(i + 1, ans[0]);// 固有値
this.eigenVector.setColumnVector(i + 1, ans[1]);// 不変部分空間の基底
}
}
// /**
// * 不変部分空間の基底を求めます。
// *
// * @param a 不変部分空間の基底を求める行列
// * @return 不変部分空間の基底
// */
// CIM getBasis(CM a) {
// solve(a);
// return this.eigenVector;
// }
/**
* 不変部分空間の基底を求めます。
*
* @param a 不変部分空間の基底を求める行列
* @return 不変部分空間の基底
*/
CIM getBasis(CIM a) {
solve(a);
return this.eigenVector;
}
/**
* Creates interval matrix
*
* @param matrix real matrix
* @return interval real matrix
*/
private CIM createInterval(CM matrix) {
return this.IA.create(matrix);
}
/**
* Creates interval scalar.
*
* @param scalar scalar
* @return interval scalar
*/
private CIS createInterval(CS scalar) {
return this.IA.getElement(1,1).create(scalar);
}
/**
* Creates interval matrix
*
* @param mid middle
* @param rad radius
* @return cim
*/
private CIM createMidRad(CM mid, RM rad) {
return this.IA.createMidRad(mid, rad);
}
/**
* Creates interval scalar with infimum and supremum.
*
* @param inf infimum
* @param sup supremum
* @return interval scalara
*/
private CIS createIntervalInfSup(double inf, double sup) {
CS type = this.A.getElement(1,1);
CIS itype = this.IA.getElement(1,1);
return itype.createInfSup(type.create(inf), type.create(sup));
}
/**
* Creates interval scalar with middle and radius.
*
* @param inf infimum
* @param sup supremum
* @return interval scalara
*/
private CIS createMidRad(double inf, double sup) {
CS type = this.A.getElement(1,1);
CIS itype = this.IA.getElement(1,1);
return itype.createMidRad(type.create(inf), type.getRealPart().create(sup));
}
}