[英]Eigen Sparse Matrix Multiplication Error with small number
我在我的程序中使用C ++ Eigen 3庫。 特別是,我需要將兩個特征稀疏矩陣相乘並將結果存儲到另一個特征稀疏矩陣中。 但是,我注意到如果特征稀疏矩陣的一些條目小於1e-13,則結果中的相應條目將是0而不是小數。 假設我乘以稀疏單位矩陣a和另一個稀疏矩陣b。 如果b的topleft條目,即b(0,0)小於1e-13,例如9e-14,則結果c = a * b的forpleft條目,即c(0,0),是0而不是9e-14。
這是我測試的代碼,
#include <iostream>
#include <math.h>
#include <Eigen/Sparse>
#include <Eigen/Dense>
#include <Eigen/Eigen>
#include <Eigen/Dense>
#include <Eigen/LU>
using namespace std;
using namespace Eigen;
int main() {
DynamicSparseMatrix<double, RowMajor> a(2,2);
DynamicSparseMatrix<double, ColMajor> b(2,2);
DynamicSparseMatrix<double, RowMajor> c(2,2);
a.coeffRef(0,0) = 1;
a.coeffRef(1,1) = 1;
b.coeffRef(0,0) = 9e-14;
b.coeffRef(1,1) = 1;
cout << "a" << endl;
cout << a << endl;
cout << "b" << endl;
cout << b << endl;
c = a * b;
cout << "c" << endl;
cout << c << endl;
Matrix<double, Dynamic, Dynamic> a2(2,2);
Matrix<double, Dynamic, Dynamic> b2(2,2);
Matrix<double, Dynamic, Dynamic> c2(2,2);
a2(0,0) = 1;
a2(1,1) = 1;
b2(0,0) = 9e-14;
b2(1,1) = 1;
cout << "a2" << endl;
cout << a2 << endl;
cout << "b2" << endl;
cout << b2 << endl;
c2 = a2 * b2;
cout << "c2" << endl;
cout << c2 << endl;
return 1;
}
這是奇怪的輸出
一個
1 0
0 1
b
非零條目:
(9e-14,0)(1,1)
列指針:
0 1 $
9e-14 0
0 1
C
0 0
0 1
a2
1 0
0 1
B2
9e-14 0
0 1
C2
9e-14 0
0 1
您可以看到密集矩陣的乘法很好,但稀疏矩陣的結果在左上角條目中是錯誤的,而b具有奇怪的輸出格式。
我調試了Eigen的源代碼,但無法找到矩陣中兩個數字相乘的位置。 任何的想法?
在線性代數庫中將小值截斷為零有幾個原因。
當你接近零時,浮點格式無法很好地表示計算。 例如,9e-14 + 1.0可能等於1.0,因為沒有足夠的精度來表示真實結果。
進入矩陣特定的東西,小的值可能會使你的矩陣病態,並導致不可靠的結果。 當您接近零時,舍入誤差會增加,因此微小的舍入誤差可能會產生大幅不同的結果。
最后,它有助於保持矩陣的稀疏性。 如果計算創建非常小的非零,則可以截斷它們並保持矩陣稀疏。 在許多物理應用中,例如有限元分析,小值在物理上並不重要,可以安全地移除它們。
我對Eigen不熟悉,但是我看了一眼他們的文檔,卻找不到改變這個舍入閾值的方法。 他們可能不希望用戶做出錯誤的選擇並破壞其結果的可靠性。 它們允許您手動“修剪”,但不能禁用自動修剪。
總體情況是:如果您的計算依賴於非常接近於零的浮點值,則應選擇不同的數值方法。 輸出永遠不可靠。 例如,您可以使用四元數旋轉替換3D矩陣旋轉。 這些方法被稱為“數值穩定”,它們是數值分析的主要焦點。
我不確定在Eigen中截斷的確切位置和方式,但用於截斷的epsilon值在Eigen/src/Core/NumTraits.h
中的NumTraits< Scalar >::dummy_precision()
中Eigen/src/Core/NumTraits.h
。
我知道這個問題現在很老了。 但是,以供將來參考: DynamicSparseMatrix
現在已經過時,而是應該使用常規的SparseMatrix
(如有必要,在非壓縮模式)。
此外,稀疏矩陣產品將不再自動修剪結果[ 1 ]。 如果現在想要修剪稀疏矩陣產品的結果,則需要明確地寫出:
C = A*B; // don't suppress numerical zeros
C = (A*B).pruned(); // suppress numerical zeros (exact)
C = (A*B).pruned(ref, eps); // suppress anything smaller than ref*eps
在后者中調用eps
是可選的,默認為使用中的標量的數字epsilon。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.