RealIntlabMethod.java
/*
* Created on 2008/01/31
* Copyright (C) 2008 Koga Laboratory. All rights reserved.
*
*/
package org.mklab.cga.linear;
import java.util.ArrayList;
import java.util.List;
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.matrix.ComplexNumericalMatrix;
import org.mklab.nfc.matrix.NormType;
import org.mklab.nfc.matrix.RealNumericalMatrix;
import org.mklab.nfc.scalar.ComplexNumericalScalar;
import org.mklab.nfc.scalar.NumericalScalar;
import org.mklab.nfc.scalar.RealNumericalScalar;
import org.mklab.nfc.util.RoundMode;
import org.mklab.nfc.util.RoundModeManager;
/**
* 線形方程式の精度保証付き解をINTLABに実装されている方法で求めるクラスです。
*
* {@link NumericalScalar}を用いているので多倍長精度に対応しています。
*
* @author yano
* @version $Revision: 1.7 $, 2008/01/31
* @param <RIS> 実区間スカラーの型
* @param <RIM> 実区間行列の型
* @param <CIS> 複素区間スカラーの型
* @param <CIM> 複素区間行列の型
* @param <RS> 実スカラーの型
* @param <RM> 実行列の型
* @param <CS> 複素スカラーの型
* @param <CM> 複素行列の型
*/
public class RealIntlabMethod<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 LinearEquationVerifier<RIS, RIM, RS, RM> {
/** 区間係数行列 */
private RIM intA;
/** 区間右辺ベクトル */
private RIM intB;
/** 精度保証付き解 */
private RIM solution;
/** 計算途中に使うパラメータ。何なのかよくわからない。 */
private static int INTERVAL_RESIDUAL = 1;
/** 計算途中に使うパラメータ。何なのかよくわからない。 */
private static boolean INTERVAL_FIRST_SECOND_STAGE = true;
// /**
// * 新しく生成された<code>NumericalMatrixElementIntlabMethod</code>オブジェクトを初期化します。
// */
// public IntlabMethod() {
// //
// }
//
// /**
// * 新しく生成された<code>NumericalMatrixElementIntlabMethod</code>オブジェクトを初期化します。
// *
// * @param A 係数行列
// * @param b 右辺係数ベクトル
// */
// public IntlabMethod(final M A, final M b) {
// this.intA = IntervalMatrixFactory.toInterval(A);
// this.intB = IntervalMatrixFactory.toInterval(b);
// }
//
// /**
// * 新しく生成された<code>NumericalMatrixElementIntlabMethod</code>オブジェクトを初期化します。
// *
// * @param A 係数行列
// * @param b 右辺係数区間ベクトル
// */
// public IntlabMethod(final M A, final IM b) {
// this.intA = IntervalMatrixFactory.toInterval(A);
// this.intB = b.clone();
// }
//
// /**
// * 新しく生成された<code>NumericalMatrixElementIntlabMethod</code>オブジェクトを初期化します。
// *
// * @param A 係数区間行列
// * @param b 右辺係数ベクトル
// */
// public IntlabMethod(final IM A, final M b) {
// this.intA = A.clone();
// this.intB = IntervalMatrixFactory.toInterval(b);
// }
//
// /**
// * {@inheritDoc}
// */
// public void solve(final M A, final M b) {
// this.intA = IntervalMatrixFactory.toInterval(A);
// this.intB = IntervalMatrixFactory.toInterval(b);
// solve();
// }
//
// /**
// * {@inheritDoc}
// */
// public void solve(final M A, final IM b) {
// this.intA = IntervalMatrixFactory.toInterval(A);
// this.intB = b.clone();
// solve();
// }
//
// /**
// * {@inheritDoc}
// */
// public void solve(final IM A, final M b) {
// this.intA = A.clone();
// this.intB = IntervalMatrixFactory.toInterval(b);
// solve();
// }
/**
* 新しく生成された<code>NumericalMatrixElementIntlabMethod</code>オブジェクトを初期化します。
*
* @param A 係数区間行列
* @param b 右辺係数区間ベクトル
*/
public RealIntlabMethod(final RIM A, final RIM b) {
this.intA = A;
this.intB = b;
}
/**
* {@inheritDoc}
*/
public void solve() {
// this.intA = A;
// this.intB = b;
RM midA = this.intA.getMiddle();
RM midb = this.intB.getMiddle();
// String message;
// if (((message = Abcdchk.abcdchk(midA, midb)).length()) > 0) {
// throw new IllegalArgumentException(message);
// }
int k = midA.getColumnSize();
int n = midA.getColumnSize();
RM R = midA.inverse();
RM xs = R.multiply(midb);// 近似解の計算。
// Improve residual calculation
// Interval iteration
List<RIM> ires_Z_RA = intervalIteration(this.intA, this.intB, R, xs);
RIM ires = ires_Z_RA.get(0);
RIM Z = ires_Z_RA.get(1);
RIM RA = ires_Z_RA.get(2);
this.solution = stage12(ires, Z, RA, R, xs, n, k);
}
// /**
// * @see org.mklab.cga.linear.LinearEquationVerifier#getSolution()
// */
// public LinearEquationVerifiedSolution<E> getSolution() {
// return new LinearEquationVerifiedSolution(this.solution);
// }
/**
* Creates interval matrix
*
* @param matrix matrix
* @return interval matrix
*/
private RIM createInterval(RM matrix) {
return this.intA.create(matrix);
}
/**
* Creates interval with middle and radius.
*
* @param mid middle
* @param rad radius
* @return interval matrxi
*/
private RIM createMidRad(RM mid, RM rad) {
RoundModeManager manager = RoundModeManager.getManager();
RoundMode oldRoundMode = manager.getRoundMode();
manager.setRoundMode(RoundMode.ROUND_DOWN);
RM inf = mid.subtract(rad);
manager.setRoundMode(RoundMode.ROUND_UP);
RM sup = mid.add(rad);
RIM ans = this.intA.createInfSup(inf, sup);
manager.setRoundMode(oldRoundMode);
return ans;
}
/**
* @param A A
* @param b b
* @param R R
* @param xs xs
* @return ??
*/
private List<RIM> intervalIteration(RIM A, RIM b, RM R, RM xs) {
RIM ires = b.subtract(A.multiply(createInterval(xs)));
RIM Z = createInterval(R).multiply(ires);
RIM RA = createInterval(R).multiply(A);
List<RIM> ans = new ArrayList<>();
ans.add(ires);
ans.add(Z);
ans.add(RA);
return ans;
}
/**
* @param midA Aの中心
* @param midb Bの中心
* @param R R
* @param xs xs
* @return ??
*/
private List<Object> improveResidual(RM midA, RM midb, RM R, RM xs) {
boolean improvedresidual = (INTERVAL_RESIDUAL == 1) | ((INTERVAL_RESIDUAL == 2) & (midb.getColumnSize() > 1));
RM localxs = xs;
// IMPROVE RESIDUAL CALCULATION
if (improvedresidual) {
// TODO 表現可能な最大数を返すメソッドがいる?(これよりは小さい値のはず?? 精度が落ちるから駄目?)
RS resnorm = midA.getElement(1).getInfinity(); //transformFrom(new MPFloat(1).divide(new MPFloat(0)));
int i = 0;
while (i < 15) {
i = i + 1;
RS resnormold = resnorm.clone();
RM res = R.multiply(ResidualImprover.lssresidual(midA, xs, midb));
// TODO ここで計算失敗(0に値が近いから??)
resnorm = (res).norm(NormType.TWO);// 最大特異値
if (resnorm.isLessThan(resnormold)) {
localxs = localxs.add(res);
}
// TODO 精度が落ちそう
if (resnorm.isGreaterThanOrEquals(resnormold.divide(10))) {// 0の剰余に注意
break;
}
}
} else if (INTERVAL_RESIDUAL == 2) {// quadruple precision residual
// calculation??
// TODO 最大値を返すメソッドがいる?(この値より小さいはず? 精度に影響がある??)
//S resnorm = (midA).getElement(1).transformFrom(Double.POSITIVE_INFINITY);
RS resnorm = (midA).getElement(1);
int i = 0;
while (i < 15) {
i = i + 1;
RS resnormold = resnorm.clone();
RM res = R.multiply(midb.unaryMinus().add(midA.multiply(localxs)));
resnorm = (res).norm(NormType.TWO);
if (resnorm.isLessThan(resnormold)) {
localxs = localxs.subtract(res);
}
// TODO 精度に影響しない?
if (resnorm.isLessThanOrEquals(resnormold.multiply(1e-1))) {// beware of zero residual
break;
}
}
} else {
localxs = localxs.add(R.multiply(midb.subtract(midA.multiply(localxs))));
}
List<Object> list = new ArrayList<>();
list.add(localxs);
list.add(Boolean.valueOf(improvedresidual));
return list;
}
/**
* intbalMethod内で使用する計算。
*
* @param ires ires
* @param Z Z
* @param RA RA
* @param R R
* @param xs xs
* @param n n
* @param k k
* @return result
*/
private RIM stage12(RIM ires, RIM Z, RIM RA, RM R, RM xs, int n, int k) {
RoundModeManager manager = RoundModeManager.getManager();
RoundMode oldRoundMode = manager.getRoundMode();
manager.setRoundMode(RoundMode.ROUND_NEAR);
// FIRST STAGE
RIM X = null;
if (INTERVAL_FIRST_SECOND_STAGE) {
RS unit = R.getElement(1, 1).abs();
// TODO 精度にいぞんする?これより小さい値もありうるよね?
RS realmin = unit.create(Double.MIN_VALUE); // Z.getMiddle().getElement(1).transformFrom(Double.MIN_VALUE);
RIM C = createInterval(R.createUnit(n)).subtract(RA);
RIS type = this.intA.getElement(1, 1);
RIM Y = Z;
// TODO 具体的なMPFloatを使わない(精度を落とさずにできるの?)
RIS minusPlusOneInterval = type.createInfSup(unit.createUnit().unaryMinus(), unit.createUnit());
RIS disk = type.createInfSup(realmin.multiply(10).unaryMinus(), realmin.multiply(10));
RM radius = Y.getSupremum().subtract(Y.getInfimum()).divide(2);
RIM yRadius = createInterval(radius.divide(10));
RIM E = yRadius.multiply(minusPlusOneInterval).addElementWise(disk);
int localk = 0;
final int kmax = 7;
boolean ready = false;
while ((!ready) & (localk < kmax) & (!Y.isNanElementWise().anyTrue())) {
localk = localk + 1;
X = Y.add(E);
Y = Z.add(C.multiply(X));
ready = IntervalUtil.in0(Y, X).allTrue();
}
if (ready) {// SUCCESS FIRST STAGE
X = Y.add(createInterval(xs));
manager.setRoundMode(oldRoundMode);
return X;
}
}
// SECOND STAGE STARTS
RIM b = createInterval(R).multiply(ires);
RIM dRA = RA.diagonalToVector();
RM A = RA.comparisonMatrix();
RM B = A.inverse();
RM v = (B.multiply(A.createOnes(n, 1))).absElementWise();
manager.setRoundMode(RoundMode.ROUND_DOWN);
RM u = A.multiply(v);
if ((u).minColumnWise().compareElementWise(".>", A.createOnes(1, u.getColumnSize())).allTrue()) { //$NON-NLS-1$
RM dAc = A.diagonalToVector();
A = A.multiply(B).subtract(A.createUnit(n));
manager.setRoundMode(RoundMode.ROUND_UP);
RM w = (A.unaryMinus().divideElementWise(u.multiply(A.createOnes(1, n)))).maxColumnWise().transpose();
RM dlow = v.multiplyElementWise(w.conjugateTranspose()).subtract(B.diagonalToVector());
dlow = (dlow.unaryMinus()).maxElementWise(A.createZero(dlow.getRowSize(), dlow.getColumnSize()));
B = B.add(v.multiply(w));
u = B.multiply(b.abssElementWise());
RM d = B.diagonalToVector();
RM alpha = dAc.add(A.createOnes(d.getRowSize(), d.getColumnSize()).unaryMinus().divideElementWise(d));
int localk = b.getColumnSize();
RIM Ixs = createInterval(xs);
if (localk == 1) {
RM beta = u.divideElementWise(dlow).subtract(b.abssElementWise());
RM mid1 = A.createZero(beta.getRowSize(), beta.getColumnSize());
RM mid2 = A.createZero(alpha.getRowSize(), alpha.getColumnSize());
X = Ixs.add((b.add(createMidRad(mid1, beta))).divideElementWise((dRA.add(createMidRad(mid2, alpha)))));
} else {
// d and dRA adapted for multiple r.h.s.
v = A.createOnes(1, localk);
RM beta = u.divideElementWise(d.multiply(v)).subtract(b.abssElementWise());
RM mid1 = A.createZero(beta.getRowSize(), beta.getColumnSize());
RM mid2 = A.createZero(alpha.getRowSize(), alpha.getColumnSize());
X = Ixs.add(b.add(createMidRad(mid1, beta)).divideElementWise((dRA.add(createMidRad(mid2, alpha)).multiply(createInterval(v)))));
}
}
manager.setRoundMode(oldRoundMode);
return X;
}
/**
* {@inheritDoc}
*/
public RIM getSolution() {
return this.solution;
}
/**
* Returns A.
*
* @return A
*/
protected RIM getA() {
return this.intA;
}
/**
* Returns B.
*
* @return B
*/
protected RIM getB() {
return this.intB;
}
}