C++版NumPy-Eigen库快速入门

2024-04-14 17:32
文章标签 c++ 入门 快速 numpy eigen

本文主要是介绍C++版NumPy-Eigen库快速入门,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Eigen库的使用

    • 零、前言
    • 一、Eigen矩阵类
      • 1、矩阵类模板参数
      • 2、向量
      • 3、特值动态
      • 4、矩阵和向量的初始化
      • 5、矩阵和向量的存取
      • 6、逗号初始化
      • 7、调整大小
    • 二、矩阵和向量算法
      • 1、加减
      • 2、标量乘法和除法
      • 3、转置、共轭和伴随
      • 4、矩阵矩阵和矩阵向量乘法
      • 5、点积和叉积
      • 6、基本算术归约运算
      • 7、操作的有效性
      • 三、数组类和按系数运算
      • 1、数组类
      • 2、访问数组内的值
      • 3、加减
      • 4、数组乘法
      • 5、其他系数运算
      • 6、在数组和矩阵表达式之间转换
    • 四、块操作
      • 1、使用块操作
      • 2、列和行
      • 3、与角相关的操作
      • 4、向量的块运算
    • 五、Eigen高级初始化
      • 1、逗号初始值设定项
      • 2、特殊矩阵和数组
      • 3、用作临时对象

零、前言

如果你是一个pythoner,一定知道NumPy操作维度数组和矩阵非常方便,我也在实际开发过程中使用过NumPy来进行过各种算术运算,真是大大地提高了工作效率;曾经一直羡慕pythoner能这么方便的使用NumPy进行各种维度和矩阵运算,直到遇到了Eigen库,它堪称C++版的NumPy,虽然在使用的过程中发现它和NumPy比还有一些不足,但已经很好了,希望它越来越强大。网上虽然已经有了很多有关Eigen的介绍使用,但是自己在使用过程中还是有很多疑惑,于是根据官方提供的文档整理而成这篇文章,希望能够帮助初学者快速入门。

一、Eigen矩阵类

在Eigen中,所有矩阵和向量都是Matrix模板类的对象。向量只是矩阵的一种特殊情况,具有1行或1列。

1、矩阵类模板参数

该矩阵类需要六个模板参数,平时我们主要用到的就前三个参数,剩下的三个参数默认即可。

1>必选模板参数
Matrix的三个必需模板参数是:
Matrix <typename Scalar,int RowsAtCompileTime,int ColsAtCompileTime>
Scalar是标量类型,即系数的类型,如果要使用浮点数矩阵,这里就传入float。

RowsAtCompileTime和ColsAtCompileTime是在编译时已知的矩阵的行数和列数。
例如:Matrix4f是一个4x4的浮点矩阵,Eigen定义的方式:
typedef Matrix <float,4,4> Matrix4f;

2>可选模板参数
Matrix类其余三个参数是可选的,模板参数的完整列表:
Matrix<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime,
int Options = 0,
int MaxRowsAtCompileTime = RowsAtCompileTime,
int MaxColsAtCompileTime = ColsAtCompileTime>

Options是位字段,用来指定是行优先存储还是列优先存储,默认情况下,矩阵和向量的存储方式是“列优先存储”,可以传值RowMajor和ColMajor。

例如,行优先的3x3矩阵:
<float,3、3,RowMajor>

MaxRowsAtCompileTime和MaxColsAtCompileTime用来指定矩阵大小的上限,即使在编译时不知道矩阵的确切大小,在编译时也知道固定的上限,这样做的最大原因是避免动态内存分配。

例如,下面矩阵类型使用12个浮点数的普通数组,而没有动态内存分配:
Matrix<float, Dynamic, Dynamic, 0, 3, 4>

2、向量

在Eigen中,向量只是具有1行或1列的矩阵的一种特殊情况,只有1列的向量称为列向量,只有1行的称为行向量。

例如,typedef Vector3f是3个浮点数的(列)向量。Eigen定义如下:
typedef Matrix <float,3,1> Vector3f;
行向量的定义:
typedef Matrix <int,1,2> RowVector2i;

3、特值动态

Eigen不仅限于其尺寸在编译时已知的矩阵,在RowsAtCompileTime和ColsAtCompileTime模板参数可以采取特殊值Dynamic这表明大小在编译时是未知的,所以必须作为运行时变量来处理。这种大小称为动态 大小;而在编译时已知的大小称为固定 大小。

例如typedef MatrixXd定义为具有动态大小的双精度矩阵,其定义如下:
typedef Matrix <double,Dynamic,Dynamic> MatrixXd;

定义固定数量的行和动态的列数,例如:
矩阵<float,3,Dynamic>

4、矩阵和向量的初始化

默认构造函数始终可用,从不执行任何动态内存分配,并且从不初始化矩阵系数。可以这样定义:

Matrix3f a;
MatrixXf b;

这里:
a 是一个3×3矩阵,具有未初始化系数的普通float [9]数组,
b 是一个动态大小的矩阵,其大小当前为0 x 0,并且其系数数组尚未分配。
也可以提供采用大小的构造函数。对于矩阵,总是先传递行数。对于矢量,只需传递矢量大小即可。他们分配给定大小的系数数组,但不自行初始化系数:

MatrixXf a(10,15);
VectorXf b(30);

这里:
a 是10x15动态尺寸矩阵,具有已分配但当前尚未初始化的系数。
b 是大小为30的动态大小向量,具有已分配但当前尚未初始化的系数。
为了在固定大小和动态大小的矩阵之间提供统一的API,合法的是在固定大小的矩阵上使用这些构造函数,即使在这种情况下传递大小都没有用。所以这是合法的:

Matrix3f a(3,3);

构造函数来初始化矢量的系数:

Vector2d a(5.06.0);
Vector3d b(5.06.07.0);
Vector4d c(5.06.07.08.0);

5、矩阵和向量的存取

Eigen中的主要系数访问器和变异器是重载的括号运算符。对于矩阵,总是先传递行索引。对于向量,只需传递一个索引。编号从0开始。
示例:

#include <iostream>
#include <Eigen/Dense> 
using namespace Eigen; 
int main()
{ MatrixXd m(2,2);  m(0,0) = 3;  m(1,0) = 2.5;  m(0,1) = -1;  m(1,1) = m(1,0) + m(0,1);  std::cout << "Here is the matrix m:\n" << m << std::endl;  VectorXd v(2);  v(0) = 4;  v(1) = v(0) - 1;  std::cout << "Here is the vector v:\n" << v << std::endl;} 程序输出:Here is the matrix m:  3  -12.5 1.5 Here is the vector v:43

语法m(index)不限于矢量,它也可用于一般矩阵,这意味着在系数数组中基于索引的访问。但是,这取决于矩阵的存储顺序。所有Eigen矩阵默认为列优先存储顺序,但是可以将其更改为行优先。

6、逗号初始化

Eigen矩阵和矢量支持逗号初始化的语法:
例:

Matrix3f m;
m << 1, 2, 3,    4, 5, 6,    7, 8, 9;
std::cout << m; 
输出结果:
1 2 34 5 67 8 9

7、调整大小

矩阵的当前大小可以通过rows(),cols()和size()检索。这些方法分别返回行数,列数和系数数。调整动态大小矩阵的大小是通过resize()方法完成的。
例:

#include <iostream>
#include <Eigen/Dense> 
using namespace Eigen; 
int main()
{  
MatrixXd m(2,5);  
m.resize(4,3);  
std::cout << "The matrix m is of size "            << m.rows() << "x" << m.cols() << std::endl;  
std::cout << "It has " << m.size() << " coefficients" << std::endl;  
VectorXd v(2);  v.resize(5);  
std::cout << "The vector v is of size " << v.size() << std::endl;  
std::cout << "As a matrix, v is of size "            << v.rows() << "x" << v.cols() << std::endl;
} 
输出结果:
The matrix m is of size 4x3
It has 12 coefficients
The vector v is of size 5
As a matrix, v is of size 5x1

如果实际矩阵大小不变,则resize()方法为空操作。否则具有破坏性:系数的值可能会更改。

为了API的统一性,所有这些方法仍然可以在固定大小的矩阵上使用。实际上不能调整固定大小的矩阵的大小。尝试将固定大小更改为实际不同的值将触发断言失败。但是以下代码是合法的:
例:

#include <iostream>
#include <Eigen/Dense> 
using namespace Eigen; 
int main()
{  
Matrix4d m;  
m.resize(4,4); // no operation  
std::cout << "The matrix m is of size "<< m.rows() << "x" << m.cols() << std::endl;
} 
输出结果:
The matrix m is of size 4x4

分配和调整大小
赋值是使用将矩阵复制到另一个矩阵中的操作operator=。Eigen自动调整左侧矩阵的大小,使其与右侧大小的矩阵大小匹配;当然,如果左侧尺寸固定,则不允许调整尺寸。
例:

MatrixXf a(2,2);
std::cout << "a is of size " << a.rows() << "x" << a.cols() << std::endl;
MatrixXf b(3,3);
a = b;
std::cout << "a is now of size " << a.rows() << "x" << a.cols() << std::endl; 
输出结果:
a is of size 2x2a is now of size 3x3

固定尺寸与动态尺寸
什么时候应该使用固定尺寸(例如Matrix4f),什么时候应该使用动态尺寸(例如MatrixXf)?
简单的答案是:将固定尺寸用于可以使用的非常小的尺寸,将动态尺寸用于较大的尺寸或必须使用的尺寸。对于小尺寸,特别是对于小于(大约)16的尺寸,使用固定尺寸对性能有极大的好处,因为它使Eigen避免动态内存分配。

在内部,固定大小的Eigen矩阵只是一个简单的数组,即
Matrix4f mymatrix;
等于float mymatrix[16];
MatrixXf对象还将其行数和列数存储为成员变量。

当然,使用固定大小的限制是,只有当您在编译时知道大小时,才有可能这样做。对于足够大的尺寸,例如,对于大于(大约)32的尺寸,使用固定尺寸的性能优势变得可以忽略不计。糟糕的是,尝试使用函数内部的固定大小创建非常大的矩阵可能会导致堆栈溢出,因为Eigen会尝试将数组自动分配为局部变量,而这通常是在堆栈上完成的。

二、矩阵和向量算法

Eigen通过常见的C ++算术运算符(例如+,-,*)的重载,或通过诸如dot(),cross()等特殊方法的方式提供矩阵/向量算术运算。对于Matrix类(矩阵和向量),运算符仅重载以支持线性代数运算。

1、加减

左侧和右侧必须具有相同数量的行和列,还必须具有相同的标量类型,因为Eigen不会自动进行类型升级。操作如下:
二进制运算符+如 a+b
二进制运算符-如 a-b
一元运算符-如 -a
复合运算符+ =如 a+=b
复合运算符-=如 a-=b
例:

#include <iostream>
#include <Eigen/Dense> 
using namespace Eigen;int main()
{  
Matrix2d a; a << 1, 2,       3, 4;  
MatrixXd b(2,2); b << 2, 3,       
1, 4;  
std::cout << "a + b =\n" << a + b << std::endl;  
std::cout << "a - b =\n" << a - b << std::endl;  
std::cout << "Doing a += b;" << std::endl;  
a += b;  
std::cout << "Now a =\n" << a << std::endl;  
Vector3d v(1,2,3);  
Vector3d w(1,0,0);  
std::cout << "-v + w - v =\n" << -v + w - v << std::endl;
} 
输出结果:
a + b =
3 5
4 8
a - b =
-1 -1 
2  0
Doing a += b;
Now a =
3 5
4 8
-v + w - v =
-1
-4
-6

2、标量乘法和除法

标量的乘法和除法也非常简单,操作如下:
二进制运算符如 matrixscalar
二进制运算符如 scalarmatrix
二元运算符/如 matrix/scalar
复合运算符* =如 matrix*=scalar
复合运算符/ =如 matrix/=scalar
例:

#include <iostream>
#include <Eigen/Dense> 
using namespace Eigen; 
int main()
{  
Matrix2d a;  
a << 1, 2,       
3, 4;  
Vector3d v(1,2,3); std::cout << "a * 2.5 =\n" << a * 2.5 << std::endl;  
std::cout << "0.1 * v =\n" << 0.1 * v << std::endl;  
std::cout << "Doing v *= 2;" << std::endl;  
v *= 2;  std::cout << <

这篇关于C++版NumPy-Eigen库快速入门的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/903575

相关文章

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

从入门到精通MySQL联合查询

《从入门到精通MySQL联合查询》:本文主要介绍从入门到精通MySQL联合查询,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下... 目录摘要1. 多表联合查询时mysql内部原理2. 内连接3. 外连接4. 自连接5. 子查询6. 合并查询7. 插入查询结果摘要前面我们学习了数据库设计时要满

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决

C++11委托构造函数和继承构造函数的实现

《C++11委托构造函数和继承构造函数的实现》C++引入了委托构造函数和继承构造函数这两个重要的特性,本文主要介绍了C++11委托构造函数和继承构造函数的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录引言一、委托构造函数1.1 委托构造函数的定义与作用1.2 委托构造函数的语法1.3 委托构造函