[Eigen中文文档] 块操作

简介: 本文介绍了块操作。块是matrix或array的部分矩形元素。块表达式既可以用作右值也可以用作左值。与Eigen表达式一样,如果让编译器进行优化,则块操作的运行时间成本为零。

专栏总目录

英文原文(Block operations)

本文介绍了块操作。块是matrixarray的部分矩形元素。块表达式既可以用作右值也可以用作左值。与Eigen表达式一样,如果让编译器进行优化,则块操作的运行时间成本为零。

使用块操作

在Eigen中最常见的块操作是.block() ,这有两个版本,语法如下:

块操作 构建一个动态大小的块表达式 构建一个固定大小的块表达式
大小为 (p,q), 起始于 (i,j)的块 matrix.block(i,j,p,q); matrix.block(i,j);

Eigen的索引是以0开始的。

这两个版本都可以用在固定大小和动态大小的matricesarray上。两种表达式在语义上是一致的,唯一的区别是,固定大小的版本会在块比较小的时候快一点,但要求块大小在编译的时候就知道。

以下程序使用动态大小和固定大小版本打印matrix中的几个块:

#include <Eigen/Dense>
#include <iostream>

using namespace std;

int main()
{
   
  Eigen::MatrixXf m(4,4);
  m <<  1, 2, 3, 4,
        5, 6, 7, 8,
        9,10,11,12,
       13,14,15,16;
  cout << "Block in the middle" << endl;
  cout << m.block<2,2>(1,1) << endl << endl;
  for (int i = 1; i <= 3; ++i)
  {
   
    cout << "Block of size " << i << "x" << i << endl;
    cout << m.block(0,0,i,i) << endl << endl;
  }
}

输出如下:

Block in the middle
 6  7
10 11

Block of size 1x1
1

Block of size 2x2
1 2
5 6

Block of size 3x3
 1  2  3
 5  6  7
 9 10 11

在上述的例子中.block()函数被用作右值,即它只被读取。但块也可以用作左值,这意味着可以给块赋值。

以下示例中进行了说明。此示例还演示了数组中的块,其工作方式与上面演示的矩阵中的块完全相同。

#include <Eigen/Dense>
#include <iostream>

int main()
{
   
  Eigen::Array22f m;
  m << 1,2,
       3,4;
  Eigen::Array44f a = Eigen::Array44f::Constant(0.6);
  std::cout << "Here is the array a:\n" << a << "\n\n";
  a.block<2,2>(1,1) = m;
  std::cout << "Here is now a with m copied into its central 2x2 block:\n" << a << "\n\n";
  a.block(0,0,2,3) = a.block(2,1,2,3);
  std::cout << "Here is now a with bottom-right 2x3 block copied into top-left 2x3 block:\n" << a << "\n\n";
}

输出如下:

Here is the array a:
0.6 0.6 0.6 0.6
0.6 0.6 0.6 0.6
0.6 0.6 0.6 0.6
0.6 0.6 0.6 0.6

Here is now a with m copied into its central 2x2 block:
0.6 0.6 0.6 0.6
0.6   1   2 0.6
0.6   3   4 0.6
0.6 0.6 0.6 0.6

Here is now a with bottom-right 2x3 block copied into top-left 2x3 block:
  3   4 0.6 0.6
0.6 0.6 0.6 0.6
0.6   3   4 0.6
0.6 0.6 0.6 0.6

虽然 .block() 方法可用于任何块操作,但还有其他方法用于特殊情况,可以提供更好的性能。在性能表现上,最重要的是能在编译时给Eigen尽可能多的信息。例如,所使用的块是矩阵的一整列,那么使用.col()函数可以让Eigen知道这只是一列,从而给更多的优化机会。

以下部分描述了这些特殊方法。

列和行

单独的列和行是特殊的块,Eigen提供了更便捷的方法.col().row()处理这些块。

块操作 方法
第 i 行 matrix.row(i);
第 i 列 matrix.col(j);

row()col()的参数是需要访问的行和列的索引,在Eigen中是从0开始的。

示例如下:

#include <Eigen/Dense>
#include <iostream>

using namespace std;

int main()
{
   
  Eigen::MatrixXf m(3,3);
  m << 1,2,3,
       4,5,6,
       7,8,9;
  cout << "Here is the matrix m:" << endl << m << endl;
  cout << "2nd Row: " << m.row(1) << endl;
  m.col(2) += 3 * m.col(0);
  cout << "After adding 3 times the first column into the third column, the matrix m is:\n";
  cout << m << endl;
}

输出如下:

Here is the matrix m:
1 2 3
4 5 6
7 8 9
2nd Row: 4 5 6
After adding 3 times the first column into the third column, the matrix m is:
 1  2  6
 4  5 18
 7  8 30

如该示例所示块表达式可以像任何其他表达式一样用于算术。

关于角的操作

Eigen还对位于矩阵或数组的角或边的块提供了特殊方法,例如:.topLeftCorner() 可以用于引用一个矩阵左上角的块。

下表列出了各种各样的可能:

块操作 构建一个动态大小的块表达式 构建一个固定大小的块表达式
左上角的一个大小为p*q的块 matrix.topLeftCorner(p,q); matrix.topLeftCorner();
左下角的一个大小为p*q的块 matrix.bottomLeftCorner(p,q); matrix.bottomLeftCorner();
右上角的一个大小为p*q的块 matrix.topRightCorner(p,q); matrix.topRightCorner();
右下角的一个大小为p*q的块 matrix.bottomRightCorner(p,q); matrix.bottomRightCorner();
包含前 q 行的块 matrix.topRows(q); matrix.topRows();
包含后 q 行的块 matrix.bottomRows(q); matrix.bottomRows();
包含前 p 列的块 matrix.leftCols(p); matrix.leftCols

();

包含后 p 列的块 matrix.rightCols(q); matrix.rightCols();
包含从第 i 列开始的 q 列的块 matrix.middleCols(i,q); matrix.middleCols(i);
包含从第 i 行开始的 q 行的块 matrix.middleRows(i,q); matrix.middleRows(i);

下面是一个简单的例子,描述了上面介绍的操作的使用:

#include <Eigen/Dense>
#include <iostream>

using namespace std;

int main()
{
   
  Eigen::Matrix4f m;
  m << 1, 2, 3, 4,
       5, 6, 7, 8,
       9, 10,11,12,
       13,14,15,16;
  cout << "m.leftCols(2) =" << endl << m.leftCols(2) << endl << endl;
  cout << "m.bottomRows<2>() =" << endl << m.bottomRows<2>() << endl << endl;
  m.topLeftCorner(1,3) = m.bottomRightCorner(3,1).transpose();
  cout << "After assignment, m = " << endl << m << endl;
}

输出如下:

m.leftCols(2) =
 1  2
 5  6
 9 10
13 14

m.bottomRows<2>() =
 9 10 11 12
13 14 15 16

After assignment, m = 
 8 12 16  4
 5  6  7  8
 9 10 11 12
13 14 15 16

向量的块操作

Eigen提供了一组专门为向量和一维数组的特殊情况设计的块操作:

块操作 构建一个动态大小的块表达式 构建一个固定大小的块表达式
包含前n个元素的块 vector.head(n); vector.head();
包含后n个元素的块 vector.tail(n); vector.tail();
包含从第 i 个元素开始的 n 个元素的块 vector.segment(i,n); vector.segment(i);

示例如下:

#include <Eigen/Dense>
#include <iostream>

using namespace std;

int main()
{
   
  Eigen::ArrayXf v(6);
  v << 1, 2, 3, 4, 5, 6;
  cout << "v.head(3) =" << endl << v.head(3) << endl << endl;
  cout << "v.tail<3>() = " << endl << v.tail<3>() << endl << endl;
  v.segment(1,4) *= 2;
  cout << "after 'v.segment(1,4) *= 2', v =" << endl << v << endl;
}

输出如下:

v.head(3) =
1
2
3

v.tail<3>() = 
4
5
6

after 'v.segment(1,4) *= 2', v =
 1
 4
 6
 8
10
 6
相关文章
|
存储 编译器 C语言
[Eigen中文文档] 对未对齐数组断言的解释
本文将解释程序因断言失败而终止的问题。
167 0
|
存储 编译器
[Eigen中文文档] 深入了解 Eigen - 类层次结构
本页面介绍了Eigen类层次结构中 Core 类的设计及其相互关系。一般用户可能不需要关注这些细节,但对于高级用户和Eigen开发人员可能会有用。
294 0
|
存储 算法 NoSQL
[Eigen中文文档] 存储顺序
矩阵和二维数组有两种不同的存储顺序:列优先和行优先。本节解释了这些存储顺序以及如何指定应该使用哪一种。
175 0
|
存储 算法 NoSQL
[Eigen中文文档] 稀疏矩阵操作
在许多应用中(例如,有限元方法),通常要处理非常大的矩阵,其中只有少数系数不为零。在这种情况下,可以通过使用仅存储非零系数的特殊表示来减少内存消耗并提高性能。这样的矩阵称为稀疏矩阵。
468 0
[Eigen中文文档] 编写以特征类型为参数的函数(一)
Eigen使用表达式模板的方式导致每个表达式的类型可能都不同。如果将这样的表达式传递给一个需要Matrix类型参数的函数,则表达式将隐式地被评估为一个临时Matrix,然后再传递给函数。这意味着失去了表达式模板的好处。
147 0
|
存储 安全 编译器
[Eigen中文文档] 常见的陷阱
本文将介绍一些Eigen常见的陷阱
271 0
|
存储 索引
[Eigen中文文档] 扩展/自定义Eigen(三)
本页面针对非常高级的用户,他们不害怕处理一些Eigen的内部细节。在大多数情况下,可以通过使用自定义一元或二元函数避免使用自定义表达式,而极其复杂的矩阵操作可以通过零元函数(nullary-expressions)来实现,如前一页所述。 本页面通过示例介绍了如何在Eigen中实现新的轻量级表达式类型。它由三个部分组成:表达式类型本身、包含有关表达式编译时信息的特性类和评估器类,用于将表达式评估为矩阵。
141 1
[Eigen中文文档] 固定大小的可向量化Eigen对象
本文主要解释 固定大小可向量化 的含义。
115 0
|
存储 缓存 并行计算
[Eigen中文文档] 预处理器指令
可以通过定义预处理器宏来控制Eigen的某些方面。这些宏应该在包含任何Eigen头文件之前定义。通常最好在项目选项中设置它们。 本页面列出了Eigen支持的预处理器指令。
291 0
|
存储 编译器 对象存储
[Eigen中文文档] 包含Eigen对象的结构体
如果定义的结构体包含固定大小的可向量化 Eigen 类型成员,则必须确保对其调用 operator new 来分配正确的对齐缓冲区。如果仅使用足够新的编译器(例如,GCC>=7、clang>=5、MSVC>=19.12)以 [c++17] 模式编译,那么编译器会自动处理所有事情,可以跳过本节。 否则,必须重载它的 operator new 以便它生成正确对齐的指针(例如,Vector4d 和 AVX 的 32 字节对齐)。幸运的是,Eigen 为提供了一个宏 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 来完成这项工作。
207 0