1 矩阵基本运算简介

Eigen重载了+,-,*运算符。同时提供了一些方法如dot(),cross()等。对于矩阵类的运算符重载只支持线性运算,比如matrix1*matrix2是矩阵相乘,当然必须要满足矩阵乘法规则。对于向量和标量的加法(vector+scalar)这里并不支持,关于非线性运算这里暂不介绍。

2 加减运算

矩阵加减运算中必须要保证左右矩阵的行列对应相等。此外更重要的一点是,矩阵的类型也必须一致,这里的矩阵运算并不支持隐式的类型转换。矩阵运算中重载的运算符有:

  1. 二元运算符+:a+b
  2. 二元运算符-:a-b
  3. 一元运算符-:-a
  4. 复合运算符+=:a+=b
  5. 复合运算符-=:a-=b

下面是使用示例:

  1. #include <iostream>
  2. #include "Eigen\Dense"
  3. using namespace Eigen;
  4. int main()
  5. {
  6. Matrix2d a;
  7. a<<1,2,
  8. 3,4;
  9. MatrixXd b(2,2);
  10. b<<2,3,
  11. 1,4;
  12. std::cout<<"a+b=\n"<<a+b<<std::endl;
  13. std::cout<<"a-b=\n"<<a-b<<std::endl;
  14. std::cout<<"Doing a+=b;"<<std::endl;
  15. a+=b;
  16. std::cout<<"Now a=\n"<<a<<std::endl;
  17. Vector3d v(1,2,3);
  18. Vector3d w(1,0,0);
  19. std::cout<<"-v+w-v=\n"<<-v+w-v<<std::endl;
  20. }

执行结果如下:

  1. a+b=
  2. 3 5
  3. 4 8
  4. a-b=
  5. -1 -1
  6. 2 0
  7. Doing a+=b;
  8. Now a=
  9. 3 5
  10. 4 8
  11. -v+w-v=
  12. -1
  13. -4
  14. -6

3 和标量的乘法和除法

对于矩阵和标量的乘法和除法也非常简单,重载的操作符如下:

  1. 二元运算符:matrixscalar
  2. 二元运算符:scalarmatrix
  3. 二元运算符/:matrix/scalar
  4. 复合运算符=:matrix=scalar
  5. 复合运算符/=:matrix/=scalar

下面是使用示例:

  1. #include <iostream>
  2. #include "Eigen\Dense"
  3. using namespace Eigen;
  4. int main()
  5. {
  6. Matrix2d a;
  7. a<<1,2,
  8. 3,4;
  9. Vector3d v(1,2,3);
  10. std::cout<<"a*2.5=\n"<<a*2.5<<std::endl;
  11. std::cout<<"0.1*v=\n"<<0.1*v<<std::endl;
  12. std::cout<<"Doing v*=2;"<<std::endl;
  13. v*=2;
  14. std::cout<<"Now v=\n"<<v<<std::endl;
  15. }

执行结果如下:

  1. a*2.5=
  2. 2.5 5
  3. 7.5 10
  4. 0.1*v=
  5. 0.1
  6. 0.2
  7. 0.3
  8. Doing v*=2;
  9. Now v=
  10. 2
  11. 4
  12. 6

4 表达式计算优化原则

关于矩阵表达式的计算这里有一点需要说明。在Eigen中,算数运算操作(比如+)并不会立即对表达式两端进行求值,而仅仅只是返回一个“表达式”,这个表达式对计算的结果的表现进行简单的描述,而真正的计算会等到最后才会进行。一般来说会等到操作运算符=执行时进行计算。这个机制会极大的优化矩阵的计算性能。请看下面的这个例子:

  1. VectorXf a(50),b(50),c(50),d(50);
  2. ...
  3. a=3*b+4*c+5*d;

虽然上面这个表达式中有多个运算符,但并不会使用多个循环对每个运算符左右两边的矩阵进行求值。而是简化为下面这一个循环。

  1. for(int i = 0;i < 50;++i)
  2. a[i] = 3*b[i] + 4*c[i] + 5*d[i];

所以我们并不用担心较大或者复杂的运算表达式会降低我们的运算效率。它只会给Eigen提供更多的优化机会。

5 转置(Transposition)和共轭(conjugation)

对于矩阵的转置(transpose)



,共轭(conjugate)



以及伴随矩阵(adjoint--conjugate transose)a*可以使用transpose(),conjugate(),'adjoint()'函数求得。

  1. #include <iostream>
  2. #include "Eigen\Dense"
  3. using namespace Eigen;
  4. int main()
  5. {
  6. MatrixXcf a = MatrixXcf::Random(2,2);
  7. std::cout<<"Matrix a=\n"<<a<<std::endl;
  8. std::cout<<"Here is the matrix a^T\n"<<a.transpose()<<std::endl;
  9. std::cout<<"Here is the conjugate of a\n"<<a.conjugate()<<std::endl;
  10. std::cout<<"Here is the matrix a^*\n"<<a.adjoint()<<std::endl;
  11. }

执行结果如下:

  1. Matrix a=
  2. (0.127171,-0.997497) (-0.0402539,0.170019)
  3. (0.617481,-0.613392) (0.791925,-0.299417)
  4. Here is the matrix a^T
  5. (0.127171,-0.997497) (0.617481,-0.613392)
  6. (-0.0402539,0.170019) (0.791925,-0.299417)
  7. Here is the conjugate of a
  8. (0.127171,0.997497) (-0.0402539,-0.170019)
  9. (0.617481,0.613392) (0.791925,0.299417)
  10. Here is the matrix a^*
  11. (0.127171,0.997497) (0.617481,0.613392)
  12. (-0.0402539,-0.170019) (0.791925,0.299417)

对于实数矩阵,conjugate()不会有任何动作。所以adjoint()=transpose().

需要说明的是,作为基本运算符transpose()和adjoint()会简单的返回一个没有做任何转换的代理对象(proxy object).如果使用b=a.transpose(),会使结果转换和写入b同时进行。然后这种转换和写入同时也会引起如下问题。如果我们执行a=a.transpose(),由于在转换前就开始写入了,所以并不会将转换后的结果写入a中。

  1. #include <iostream>
  2. #include "Eigen\Dense"
  3. using namespace Eigen;
  4. using namespace std;
  5. int main()
  6. {
  7. Matrix2i a;
  8. a<<1,2,3,4;
  9. cout<<"Here is the matrix a:\n"<<endl;
  10. a=a.transpose(); //!!! do NOT do this!!!
  11. cout<<"and the result of the aliasing effect:\n"<<a<<endl;
  12. }

官方给的例程说执行上面的程序会发现转换后a的矩阵等于转换前的,但是我测试的结果是程序直接出错并停止运行。

如果我们想对a本身进行转换可使用transposeInPlace()函数。同样的如果求伴随矩阵的话可使用adjoinInPlace()函数。

  1. #include <iostream>
  2. #include "Eigen\Dense"
  3. using namespace Eigen;
  4. using namespace std;
  5. int main()
  6. {
  7. MatrixXf a(2,3);
  8. a<<1,2,3,
  9. 4,5,6;
  10. cout<<"Here is the initial matrix a:\n"<<a<<endl;
  11. a.transposeInPlace();
  12. cout<<"and after being transposed:\n"<<a<<endl;
  13. a.adjointInPlace();
  14. cout<<"and (a^T)^*=\n"<<a<<endl;
  15. }

执行结果如下:

  1. Here is the initial matrix a:
  2. 1 2 3
  3. 4 5 6
  4. and after being transposed:
  5. 1 4
  6. 2 5
  7. 3 6
  8. and (a^T)^*=
  9. 1 2 3
  10. 4 5 6

6 矩阵-矩阵以及矩阵-向量相乘

由于向量属于特殊的矩阵,所以我们只需考虑矩阵的相乘即可。矩阵和矩阵相乘可以使用如下两种运算符:

  1. 二元运算符:ab
  2. 复合运算符=:a=b

下面是使用示例:

  1. #include <iostream>
  2. #include "Eigen\Dense"
  3. using namespace Eigen;
  4. using namespace std;
  5. int main()
  6. {
  7. Matrix2d mat;
  8. mat<<1,2,
  9. 3,4;
  10. Vector2d u(1,-1),v(2,0);
  11. cout<<"Here is mat*mat:\n"<<mat*mat<<endl;
  12. cout<<"Here is mat*u:\n"<<mat*u<<endl;
  13. cout<<"Here is u^T*mat:\n"<<u.transpose()*mat<<endl;
  14. cout<<"Here is u^T*v:\n"<<u.transpose()*v<<endl;
  15. cout<<"Here is u*v^T:\n"<<u*v.transpose()<<endl;
  16. cout<<"Let's multiply mat by itsef"<<endl;
  17. mat = mat*mat;
  18. cout<<"Now mat is mat:\n"<<mat<<endl;
  19. }

执行结果如下:

  1. Here is mat*mat:
  2. 7 10
  3. 15 22
  4. Here is mat*u:
  5. -1
  6. -1
  7. Here is u^T*mat:
  8. -2 -2
  9. Here is u^T*v:
  10. 2
  11. Here is u*v^T:
  12. 2 0
  13. -2 -0
  14. Let's multiply mat by itsef
  15. Now mat is mat:
  16. 7 10
  17. 15 22

在矩阵的乘法中我们不用担心a=a*a会引起上面的使用别名问题(aliasing issues),因为这里会自动的引入一个中间变量因此a=a*a相当于temp=a*a;a=temp.如果你知道你的计算不会引起使用别名问题,那么你可以使用noalias()函数去避免使用这个中间变量以增加运算速度。如c.noalias()+=a*b;.关于使用别名的问题可以在官网aliasing中查看更多信息。

对于BLAS用户可能担心运算性能的问题,但正如我们前面所说的c.noalias-=2*a.adjoint()*b会完全的进行优化只触发一个函数调用。

7 点乘和叉乘

对于內积和外积的计算可以使用dot()cross()函数。当然点乘会得到一个1×1的矩阵(u.adjoint()*v)。

下面是一个使用示例:

  1. #include <iostream>
  2. #include "Eigen\Dense"
  3. using namespace Eigen;
  4. using namespace std;
  5. int main()
  6. {
  7. Vector3d v(1,2,3);
  8. Vector3d w(0,1,2);
  9. cout<<"DOt product:"<<v.dot(w)<<endl;
  10. double dp = v.adjoint()*w; //automatic conversion of the inner product to a scalar
  11. cout<<"Dot product via a matrix product: "<<dp<<endl;
  12. cout<<"Cross product:\n"<<v.cross(w)<<endl;
  13. }

计算结果如下所示:

  1. DOt product:8
  2. Dot product via a matrix product: 8
  3. Cross product:
  4. 1
  5. -2
  6. 1

需要记住叉积只能对3维向量使用,而点积可以对任意维的向量使用。对于复数,点积会对第一个向量首先进行共轭然后在和第二个向量相乘。

8 其他一些基本运算

Eigen同样提供了其他的函数对矩阵的所有元素进行操作,比如sum(对矩阵所有元素求和),product(全部元素相乘),maximum(求最大值)和minimum(求最小值)。

下面是一个示例:

  1. #include <iostream>
  2. #include "Eigen\Dense"
  3. using namespace Eigen;
  4. using namespace std;
  5. int main()
  6. {
  7. Matrix2d mat;
  8. mat<<1,2,
  9. 3,4;
  10. cout<<"Here is mat.sum():\t\t"<<mat.sum()<<endl;
  11. cout<<"Here is mat.prd():\t\t"<<mat.prod()<<endl;
  12. cout<<"Here is mat.mean():\t\t"<<mat.mean()<<endl;
  13. cout<<"Here is mat.minCoeff():\t\t"<<mat.minCoeff()<<endl;
  14. cout<<"Here is mat.maxCoeff():\t\t"<<mat.maxCoeff()<<endl;
  15. cout<<"Here is mat.trace():\t\t"<<mat.trace()<<endl;
  16. }

执行结果如下:

  1. Here is mat.sum(): 10
  2. Here is mat.prd(): 24
  3. Here is mat.mean(): 2.5
  4. Here is mat.minCoeff(): 1
  5. Here is mat.maxCoeff(): 4
  6. Here is mat.trace(): 5

对于求矩阵的迹除了使用trace()还可以使用高效的a.diagonal().sum()

对于求minCoeffmaxCoeff除了能求出最大值以外还能获取相关的索引下标。

  1. #include <iostream>
  2. #include <cstddef>
  3. #include "Eigen\Dense"
  4. using namespace Eigen;
  5. using namespace std;
  6. int main()
  7. {
  8. Matrix3f m = Matrix3f::Random();
  9. ptrdiff_t i,j;
  10. float minOfM = m.minCoeff(&i,&j);
  11. cout<<"Here is the matrix m:\n"<<m<<endl;
  12. cout<<"Its minimum coefficient ("<<minOfM
  13. <<") is at position ("<<i<<","<<j<<")"<<endl<<endl;
  14. RowVector4i v = RowVector4i::Random();
  15. int maxOfv=v.maxCoeff(&i);
  16. cout<<"Here is the vector v: "<<v<<endl;
  17. cout<<"Its maximun coefficient ("<<maxOfv
  18. <<") is at position "<<i<<endl;
  19. }

执行结果如下:

  1. Here is the matrix m:
  2. -0.997497 0.617481 -0.299417
  3. 0.127171 0.170019 0.791925
  4. -0.613392 -0.0402539 0.64568
  5. Its minimum coefficient (-0.997497) is at position (0,0)
  6. Here is the vector v: 8080 -10679 11761 6897
  7. Its maximun coefficient (11761) is at position 2

9 有效性检查

Eigen会进行操作的有效性检测。如果可能的话在编译阶段就会给出相关的错误信息,虽然这些信息看起来又臭又长。但是Eigen会将重要的信息使用大写加下划线的形式写出。比如:

  1. Matrix3f m;
  2. Vector4f v;
  3. v=m*v; //Compile-time error:YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES

当然对于大多数情况,比如检查动态数组大写。编译器并不能在编译阶段检查出错误。Eigen在运行中会使用断言(assertions)进行检测。这意味着,如果我们使用了"debug mode",那么程序在检测到一个非法操作后会被错误信息打断。如果没有使用断言机制,那么程序可能会遇到灾难性的错误。

  1. MatrixXf m(3,3);
  2. VectorXf v(4);
  3. v = m*v; //Run-time assertion failure here:"invalid matrix product"

Eigen矩阵基本运算的更多相关文章

  1. C++实现离散余弦变换(参数为Eigen矩阵)

    C++实现离散余弦变换(参数为Eigen矩阵) 问题描述 昨天写了一个参数为二维指针为参数的离散余弦变换,虽然改进了参数为二维数组时,当数组大小不确定时声明函数时带来的困难,但使用指针作为参数也存在一 ...

  2. Matlab/Eigen矩阵填充问题

    Matlab进行矩阵填充时可以填充空矩阵,相当于空矩阵不存在,例如一下代码: P_RES = [ P_xv P_xvy P_xv*dy_dxv'; P_yxv P_y P_yxv*dy_dxv'; d ...

  3. Eigen 矩阵库学习笔记

    最近为了在C++中使用矩阵运算,简单学习了一下Eigen矩阵库.Eigen比Armadillo相对底层一点,但是只需要添加头文库即可使用,不使用额外的编译和安装过程. 基本定义 Matrix3f是3* ...

  4. eigen矩阵操作练习

    // // Created by qian on 19-7-16. // /* 相机位姿用四元数表示 q = [0.35, 0.2, 0.3, 0.1] x,y,z,w * 注意:输入时Quatern ...

  5. 矩阵基本运算的 Python 实现

    from...import与import区别在于import直接导入指定的库,而from....import则是从指定的库中导入指定的模块 import...as则是将import A as B,给予 ...

  6. C++矩阵库 Eigen 快速入门

    最近需要用 C++ 做一些数值计算,之前一直采用Matlab 混合编程的方式处理矩阵运算,非常麻烦,直到发现了 Eigen 库,简直相见恨晚,好用哭了. Eigen 是一个基于C++模板的线性代数库, ...

  7. C++矩阵库 Eigen 简介

    最近需要用 C++ 做一些数值计算,之前一直采用Matlab 混合编程的方式处理矩阵运算,非常麻烦,直到发现了 Eigen 库,简直相见恨晚,好用哭了. Eigen 是一个基于C++模板的线性代数库, ...

  8. Eigen ,MKL和 matlab 矩阵乘法速度比较

    Eigen 矩阵乘法的速度  < MKL矩阵乘法的速度,MKL矩阵乘法的速度与matlab矩阵乘法的速度相差不大,但matlab GPU版本的矩阵乘法速度是CUP的两倍,在采用float数据类型 ...

  9. OpenCV - Operations on Arrays 对数组(矩阵)的一些操作

    Function (函数名) Use (函数用处) add 矩阵加法,A+B的更高级形式,支持mask scaleAdd 矩阵加法,一个带有缩放因子dst(I) = scale * src1(I) + ...

随机推荐

  1. 1.3 Linux分区类型

    1.主分区最多只能有4个. 2.扩展分区: 最多只能有一个: 主分区加扩展分区最多只能有4个: 扩展分区只能包含逻辑分区,不能写数据. 3.格式化目的: 写入文件系统:清除数据:划出文件分配表(i n ...

  2. LOJ 2292 「THUSC 2016」成绩单——区间DP

    题目:https://loj.ac/problem/2292 直接 DP 很难做,主要是有那种 “一个区间内部有很多个别的区间” 的情况. 自己想了一番枚举 max-min 的最大限制,然后在该基础上 ...

  3. [转]c++访问python3-实例化类的方法

    转自: http://blog.csdn.net/love_clc/article/details/76653100 此文是学习笔记,供日后翻阅.下面列出C++访问python所需的函数,按调用的先后 ...

  4. ClassNotFoundException与NoClassDefFoundError异常

    方法 loadClass()抛出的是 java.lang.ClassNotFoundException异常(一般是jar冲突或者没有引入jar):方法 defineClass()抛出的是 java.l ...

  5. android 2.3.4 编译中出错和解决办法

    需要安装的一些库,有如下一些: sudo apt-get install git-core gnupg flex bison gperf build-essential \ zip curl zlib ...

  6. 如何修改MSSQL的用户名

    Alter LOGIN sa DISABLE Alter LOGIN sa WITH NAME = [systemAccount] "systemAccount" 为SA的新名称, ...

  7. 格式化输出=========》format 和 %

    str.format()  实现格式化输出的功能 s1 = "i am {0},gae{1}".format("alex",18)   普通版,直接输入元祖  ...

  8. 3076: 神经网络(bfs和拓扑排序)

    3076: 神经网络 时间限制: 1 Sec  内存限制: 125 MB提交: 7  解决: 5[提交][状态][讨论版][命题人:外部导入][Edit] [TestData] [同步数据] 题目描述 ...

  9. for...in的改进版for...of

    for...in 用起来似乎还不错,为什么又弄个 for...of 呢? 来看个例子: 'user strict' var arr = [12,13,14,15,16]; for(var i in a ...

  10. Android 开发 系统组件集合

    常用的TextView.Button.ImageView和几个常用布局就不介绍了,我们介绍一些特别好用但是常常忘记的组件. 标题栏组件 <androidx.appcompat.widget.To ...