これはmaths.seかSOのどちらの質問か分かりませんが、私のJavaに関連していると思いますJavaでNaNを生成するコレスキー分解
私はGaussian Processes(R&W)のテキストブックに従い、Javaでいくつかの例を実装しています。いくつかの例の1つの共通ステップは、共分散行列のコレスキー分解を生成することです。私の試みでは、限られたサイズ(33x33)までのマトリックスの成功を得ることができます。しかし、より大きなものについては、NaNが対角線(32,32)に現れます。したがって、その後の行列の値も同様にNaNです。
コードは以下に示され、NaNのソースはcholesky
メソッドで示されます。本質的に共分散要素a[32][32]
は1.0ですが、sum
の値はこれを少し上回っています(1.0000001423291431)ので、平方根は虚数です。だから、私の質問は以下のとおりです。これは、線形代数から期待される結果、または、例えば、私の実装の アーチファクト
- ですか?
- どのようにこの問題を回避するのが最も効果的ですか?
私は使用するライブラリの推奨を探しているわけではありません。これは私の理解のためのものです。
長さについて謝罪が、私は完全にMWEを提供しようとしました:
import static org.junit.Assert.assertFalse;
import org.junit.Test;
public class CholeskyTest {
@Test
public void testCovCholesky() {
final int n = 34; // Test passes for n<34
final double[] xData = getSpread(-5, 5, n);
double[][] cov = covarianceSE(xData);
double[][] lower = cholesky(cov);
for(int i=0; i<n; ++i) {
for(int j=0; j<n; ++j) {
assertFalse("NaN at " + i + "," + j, Double.isNaN(lower[i][j]));
}
}
}
/**
* Generate n evenly space values from min to max inclusive
*/
private static double[] getSpread(final double min, final double max, final int n) {
final double[] values = new double[n];
final double delta = (max - min)/(n - 1);
for(int i=0; i<n; ++i) {
values[i] = min + i*delta;
}
return values;
}
/**
* Calculate the covariance matrix for the given observations using
* the squared exponential (SE) covariance function.
*/
private static double[][] covarianceSE (double[] v) {
final int m = v.length;
double[][] k = new double[m][];
for(int i=0; i<m; ++i) {
double vi = v[i];
double row[] = new double[m];
for(int j=0; j<m; ++j) {
double dist = vi - v[j];
row[j] = Math.exp(-0.5*dist*dist);
}
k[i] = row;
}
return k;
}
/**
* Calculate lower triangular matrix L such that LL^T = A
* Using Cholesky decomposition from
* https://rosettacode.org/wiki/Cholesky_decomposition#Java
*/
private static double[][] cholesky(double[][] a) {
final int m = a.length;
double[][] l = new double[m][m];
for(int i = 0; i< m;i++){
for(int k = 0; k < (i+1); k++){
double sum = 0;
for(int j = 0; j < k; j++){
sum += l[i][j] * l[k][j];
}
l[i][k] = (i == k) ? Math.sqrt(a[i][i] - sum) : // Source of NaN at 32,32
(1.0/l[k][k] * (a[i][k] - sum));
}
}
return l;
}
}
これは、これを解決するための 'BigDecimal'の使用に関する私の簡単な調査よりも優れた解決策です。 – AJNeufeld
@AJNeufeldああ、それは私の元々の思想(より精密な浮きを使うこと)でした。偉大な心は似ていると思う;-) – beldaz
それとも、「愚か者はめったに違いない」ですか? :-p – AJNeufeld