C++线性代数库——Eigen
内容概要
上次我们介绍了Eigen的一些基本概况,下面我们再来探讨一些Eigen中运算符重载、重要API以及矩阵建立的一些问题。
Operator overload
Eigen中基本数据类型Matrix
在其命名空间内重载了一些方便矩阵运算的运算符。
-
**+**运算符的重载
Matrix数据类型支持 + 法运算符。加法运算符的重载可以让我们完成矩阵以及向量的加法运算。值得注意的是
加法运算符不会在编译时检测加法运算的合法性,只会在程序运行时检测运算的合理性,如果运算不合理将会抛出异常,所以在大型程序编写中,我们应该捕获可能出现的异常。
#include <iostream> #include "Eigen/Dense" using namespace std; using namespace Eigen; int main() { Matrix<float,-1,-1> A(3,3); Matrix<float,-1,-1> B(3,2); try { A + B; } catch (exception e) { cout << "不合法的加法运算" <<endl; cout << e.what(); } return 0; }
-
- 运算符的重载
同 + 预算符的重载
-
*****运算符的重载
同 + 运算符的重载
-
/ 运算符的重载
rate divide
-
+= and **-=**运算符的重载
同**+**运算符的重载
-
<< 运算符的重载
为了方便对矩阵进行初始化,Eigen对右移位运算符进行了重载,类似流式操作,对矩阵进行初始化。但是对矩阵进行初始化时,右移位运算符必须一次性把矩阵中所有的元素都进行初始化。
也就是说,初始化操作要一次性完成。初始化元素个数不可以不等于矩阵的大小。
#include <iostream> #include "Eigen/Dense" using namespace std; using namespace Eigen; int main() { Matrix<float,-1,-1> A(3,3); Matrix<float,-1,-1> B(3,2); A << 1,2,3, 2,3,4, 3,4,5; return 0; }
-
ostream的重载
为了方便打印矩阵,Eigen对编译器的标准输出流做了重载。
#include <iostream> #include "Eigen/Dense" using namespace std; using namespace Eigen; int main() { Matrix<float,-1,-1> A(3,3); Matrix<float,-1,-1> B(3,2); A << 1,2,3, 2,3,4, 3,4,5; cout << A << endl; return 0; }
矢量化的操作(vector operation)
在Eigen中,矢量化的操作是极为便捷的。Matrix数据类型支持矢量化的乘除运算。也就是对矩阵进行倍率的放大和缩小。
#include <iostream>
#include "Eigen/Dense"
using namespace std;
using namespace Eigen;
int main()
{
Matrix<float,-1,-1> A(3,3);
Matrix<float,-1,-1> B(1,3);
A << 1,2,3,
2,3,4,
3,4,5;
B << 1,2,3;
cout << "now A is:" << endl;
cout << A << endl;
cout << "A * 2 = " << endl;
cout << A * 2 << endl;
cout << "As well as division" << endl;
cout << A / 2 << endl;
return 0;
}
程序执行结果
now A is:
1 2 3
2 3 4
3 4 5
A * 2 =
2 4 6
4 6 8
6 8 10
As well as division
0.5 1 1.5
1 1.5 2
1.5 2 2.5
as well as vector to do that
#include <iostream>
#include "Eigen/Dense"
using namespace std;
using namespace Eigen;
int main()
{
Matrix<float,-1,-1> A(3,3);
Matrix<float,-1,-1> B(1,3);
A << 1,2,3,
2,3,4,
3,4,5;
B << 1,2,3;
cout << "now B is:" << endl;
cout << B << endl;
cout << "B * 2 = " << endl;
cout << B * 2 << endl;
cout << "As well as division" << endl;
cout << B / 2 << endl;
return 0;
}
程序执行结果
now B is:
1 2 3
B * 2 =
2 4 6
As well as division
0.5 1 1.5
Transposition(转置) ,Conjugation(共轭) and Adjoint(伴随)
下面我们来说Matrix模板类中的重要的两个成员函数。transpose()
&conjugation()
&adjoint()
。这两个成员函数可以完成矩阵的转置,共轭矩阵的求解和伴随矩阵的问题。下面我们通过代码实例进行了解
#include <iostream>
#include "Eigen/Dense"
using namespace std;
using namespace Eigen;
int main()
{
/*this is an transpositon example*/
Matrix<float,-1,-1> A(3,3);
A << 1,2,3,
4,5,6,
7,8,9;
/* Matrix support copy assignment constructor*/
Matrix<float,-1,-1> B = A.transpose();
cout << "Now A is:" <<endl;
cout << A<< endl;
cout << "It's transposition is:" << endl;
cout << B << endl;
return 0;
}
程序执行结果
Now A is:
1 2 3
4 5 6
7 8 9
It's transposition is:
1 4 7
2 5 8
3 6 9
as well as conjugation
#include <iostream>
#include "Eigen/Dense"
using namespace std;
using namespace Eigen;
int main()
{
/*conjugation example*/
Matrix<float,-1,-1> A(3,3);
A << 1,2,3,
4,5,6,
7,8,9;
Matrix<float,-1,-1> B = A.conjugate();
cout << "Now A is:" <<endl;
cout << A<< endl;
cout << "It's conjugation is:" << endl;
cout << B << endl;
return 0;
}
程序执行结果
Now A is:
1 2 3
4 5 6
7 8 9
It's conjugation is:
1 2 3
4 5 6
7 8 9
and this is Adjoint
#include <iostream>
#include "Eigen/Dense"
using namespace std;
using namespace Eigen;
int main()
{
/*Adjoint example*/
Matrix<float,-1,-1> A(3,3);
A << 1,2,3,
4,5,6,
7,8,9;
Matrix<float,-1,-1> B = A.adjoint();
cout << "Now A is:" <<endl;
cout << A<< endl;
cout << "It's adjoint is:" << endl;
cout << B << endl;
return 0;
}
程序执行结果
Now A is:
1 2 3
4 5 6
7 8 9
It's adjoint is:
1 4 7
2 5 8
3 6 9
Dot production and cross production
点成和叉乘是在向量运算中有着很重要的地位。同样Eigen也提供了相应的向量点乘和叉乘的运算成员函数(Matrix)下面我们就通过代码实例进行了解
#include <iostream>
#include "Eigen/Dense"
using namespace std;
using namespace Eigen;
int main()
{
RowVector3d A,B;
A << 1,0,0;
B << 0,1,0;
cout << "Vector A is:"<< endl;
cout << A <<endl;
cout << "Vector B is:" << endl;
cout << B << endl;
cout <<"A . B" <<endl;
cout << A.dot(B) << endl;
cout << "A x B" <<endl;
cout << A.cross(B) << endl;
return 0;
}
程序执行结果
Vector A is:
1 0 0
Vector B is:
0 1 0
A . B
0
A x B
0 0 1
Vector方法的定义
在调用一些专属于Vector的方法时,我们应该注意,Eigen在Matrix这个模板类中做了一些偏特化的处理。
例如下面的这行代码
#include <iostream>
#include "Eigen/Dense"
using namespace std;
using namespace Eigen;
int main()
{
Matrix<double,-1,-1> A(1,3),B(1,3);//定义出错,该模板参数没有进行特化
A << 1,0,0;
B << 0,1,0;
cout << "Vector A is:"<< endl;
cout << A <<endl;
cout << "Vector B is:" << endl;
cout << B << endl;
cout <<"A . B" <<endl;
cout << A.dot(B) << endl;
cout << "A x B" <<endl;
cout << A.cross(B) << endl;
return 0;
}
我们动态定义出来的向量将无法使用一些专属于Vector的方法。虽然在字面上来看,这与RowVector3d定义没什么不同,但是Eigen对Vector做了模板偏特化处理。也就是说,RowVector3d不再是仅仅的typedef
的问题,而是在模板特化的问题。(的确Eigen时tyepdef Matrix<double,1,3> RowVector3d
)但是对于Matrix<double,1,3>这组模板做了偏特化的处理。进而是他能够使用一些专属的特化的函数。
程序执行结果
D:\CPP\EigenTest\Eigen\src\Core\Dot.h:74: error: static_assert failed due to requirement 'Matrix<double, -1, -1, 0, -1, -1>::IsVectorAtCompileTime' "YOU_TRIED_CALLING_A_VECTOR_METHOD_ON_A_MATRIX"
EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
更改的建议
#include <iostream>
#include "Eigen/Dense"
using namespace std;
using namespace Eigen;
int main()
{
Matrix<double,1,3> A,B;//该模板参数进行了特化
A << 1,0,0;
B << 0,1,0;
cout << "Vector A is:"<< endl;
cout << A <<endl;
cout << "Vector B is:" << endl;
cout << B << endl;
cout <<"A . B" <<endl;
cout << A.dot(B) << endl;
cout << "A x B" <<endl;
cout << A.cross(B) << endl;
return 0;
}
Inverse(求逆)
可以说,在线性代数中,最重要的问题就是矩阵的求逆,这是求解线性方程组的条件。
=>
下面是代码实例
#include <iostream>
#include "Eigen/Dense"
using namespace std;
using namespace Eigen;
int main()
{
Matrix<double,-1,-1> A(3,3);
A << 1,2,3,
4,5,6,
7,8,9;
cout <<A.inverse() << endl;
return 0;
}
创建矩阵和向量的建议
如果想使用动态的矩阵,我们可以用MatrixXd
等去创建我们想要的类型的矩阵。也可以使用Matrix<T,-1,-1>
去创建任意类型的矩阵。但是,我们要注意,一旦我们想使用向量,我们应该尽量避免使用上一个方法进行创建。因为通过上一个方法创建出来的向量没有经过类模板的特化处理,所以在使用一些成员函数时,会造成不必要的困扰。所以向量的创建我们尽量去指定其类模板参数中的行数和列数