本文介绍了如何使用操作运算符operator()
索引行和列的子集。该 API 在 Eigen 3.4 中引入。它支持 block API 提供的所有功能。特别是,它支持切片,即获取一组行、列或元素,以及等间隔的从矩阵或者数组中提取元素。
概述
所有上述操作都是通过DenseBase::operator()(const RowIndices&, const ColIndices&)
来完成的,每一个参数可以是:
- 索引单行或列的整数,包括符号索引
- 符号
Eigen::all
表示按递增顺序排列的所有行或列 - 由
Eigen::seq
,Eigen::seqN
或者Eigen::placeholders::lastN
函数构造的算数序列 - 任意一维整数向量、数组,形式如Eigen
向量
、数组
、表达式
、std::vector
、std::array
、 C的数组int[N]
更一般的,该函数可以接受任何有下列两个成员函数接口的对象
<integral type> operator[](<integral type>) const;
<integral type> size() const;
其中<integral type>
代表任何可以与Eigen::index
兼容的整数,如std::ptrdiff_t
基本的切片
通过Eigen::seq
或Eigen::seqN
函数,取矩阵或向量中均匀间隔的一组行、列或元素,其中seq
代表等差数列,用法如下:
方法 | 描述 | 示例 |
---|---|---|
seq(firstIdx,lastIdx) | 返回从firstIdx 到 lastIdx 的整数序列 |
seq(2,5) <=> {2,3,4,5} |
seq(firstIdx,lastIdx,incr) | 同上,但是索引步长为incr | seq(2,8,2) <=> {2,4,6,8} |
seqN(firstIdx,size) | 从firstIdx 开始,索引步长1,总的个数为size |
seqN(2,5) <=> {2,3,4,5,6} |
seqN(firstIdx,size,incr) | 同上,索引步长为incr | seqN(2,3,3) <=> {2,5,8} |
一旦等差数列通过operator()
传递给它,firstIdx
和lastIdx
参数也可以用Eigen::last
符号来定义,该符号表示矩阵/向量的最后一行、最后一列或最后一个元素的索引,使用如下:
目的 | 代码 | 等价的块操作 |
---|---|---|
包含从第i行到最后一行,从第0列开始,共n列的块 | A(seq(i,last), seqN(0,n)) | A.bottomLeftCorner(A.rows()-i,n) |
包含从第i行到第m行,从第j列开始,共n列的块 | A(seqN(i,m), seqN(j,n)) | A.block(i,j,m,n) |
包含从第i0行到第i1行,从第j0列到第j1列的块 | A(seq(i0,i1), seq(j0,j1) | A.block(i0,j0,i1-i0+1,j1-j0+1) |
A的偶数列 | A(all, seq(0,last,2)) | |
A的前n个奇数行 | A(seqN(1,n,2), all) | |
倒数第二列 | A(all, last-1) | A.col(A.cols()-2) |
A的中间一行 | A(last/2,all) | A.row((A.rows()-1)/2) |
向量v从第i个元素到最后一个元素 | v(seq(i,last)) | v.tail(v.size()-i) |
向量v的最后n个元素 | v(seq(last+1-n,last)) | v.tail(n) |
示例如下:
// 代码索引 3-5-1-1
MatrixXi A = MatrixXi::Random(7, 6);
cout << "Initial matrix A:\n"
<< A << "\n\n";
// 包含从第i行到最后一行,从第0列开始,共n列的块(i: 2, n: 3)
cout << "A(seq(i,Eigen::last), seqN(0,n)): (i: 2, n: 3)\n"
<< A(seq(2, Eigen::last), seqN(0, 3)) << "\n\n";
// 包含从第i行到第m行,从第j列开始,共n列的块(i: 1, m: 2, j: 2, n: 4)
cout << "A(seq(i,m), seqN(j,n)): (i: 1, m: 2, j: 2, n: 4)\n"
<< A(seq(1, 2), seqN(2, 4)) << "\n\n";
// 包含从第i0行到第i1行,从第j0列到第j1列的块(i0: 1, i1: 2, j0: 2, j1: 4)
cout << "A(seq(i0,i1), seq(j0,j1)): (i0: 1, i1: 2, j0: 2, j1: 4)\n"
<< A(seq(1, 2), seq(2, 4)) << "\n\n";
// A的偶数列
cout << "A的偶数列: \n"
<< A(Eigen::all, seq(0, Eigen::last, 2)) << "\n\n";
// A的前n个奇数行 (n: 3)
cout << "A的前n个奇数行: (n: 3)\n"
<< A(seqN(1, 3, 2), Eigen::all) << "\n\n";
// A的倒数第二列
cout << "A的倒数第二列: \n"
<< A(Eigen::all, Eigen::last-1) << "\n\n";
// A的中间一行
cout << "A的中间一行: \n"
<< A(Eigen::last/2, Eigen::all) << "\n\n";
Eigen::VectorXi v{
{
4,2,5,8,3}};
cout << "Initial vector v:\n"
<< v << "\n\n";
// 向量v从第i个元素到最后一个元素 (i: 2)
cout << "向量v从第i个元素到最后一个元素: (i: 2)\n"
<< v(seq(2, Eigen::last)) << "\n\n";
// 向量v最后n个元素
cout << "向量v最后n个元素: (n: 3)\n"
<< v(seq(Eigen::last-3+1, Eigen::last)) << "\n\n";
输出如下:
Initial matrix A:
730547559 576018668 971155939 -552146456 1071432243 52156343
-226810938 -477225175 893772102 -779039257 -605038689 -13780431
607950953 115899597 291438716 653214605 27772105 1015276632
640895091 -48539462 466641602 -737276042 728237978 -445566813
884005969 276748203 -769652652 -212720294 241892198 582736218
-649503489 -290373134 229713912 -795018962 -438018766 57434405
-353856438 28778235 -1038736613 -840076701 295391245 579635549
A(seq(i,Eigen::last), seqN(0,n)): (i: 2, n: 3)
607950953 115899597 291438716
640895091 -48539462 466641602
884005969 276748203 -769652652
-649503489 -290373134 229713912
-353856438 28778235 -1038736613
A(seq(i,m), seqN(j,n)): (i: 1, m: 2, j: 2, n: 4)
893772102 -779039257 -605038689 -13780431
291438716 653214605 27772105 1015276632
A(seq(i0,i1), seq(j0,j1)): (i0: 1, i1: 2, j0: 2, j1: 4)
893772102 -779039257 -605038689
291438716 653214605 27772105
A的偶数列:
730547559 971155939 1071432243
-226810938 893772102 -605038689
607950953 291438716 27772105
640895091 466641602 728237978
884005969 -769652652 241892198
-649503489 229713912 -438018766
-353856438 -1038736613 295391245
A的前n个奇数行: (n: 3)
-226810938 -477225175 893772102 -779039257 -605038689 -13780431
640895091 -48539462 466641602 -737276042 728237978 -445566813
-649503489 -290373134 229713912 -795018962 -438018766 57434405
A的倒数第二列:
1071432243
-605038689
27772105
728237978
241892198
-438018766
295391245
A的中间一行:
640895091 -48539462 466641602 -737276042 728237978 -445566813
Initial vector v:
4
2
5
8
3
向量v从第i个元素到最后一个元素: (i: 2)
5
8
3
向量v最后n个元素: (n: 3)
5
8
3
正如在上一个示例中看到的,引用最后n个元素(或行/列)编写起来有点麻烦。使用非默认增量时,这将变得更加棘手和容易出错。因此,Eigen提供了Eigen::placeholders::lastN(size)
和Eigen::placeholders::lastN(size,incr)
函数来完成最后几个元素的提取,用法如下:
Eigen官方建议使用
Eigen::lastN(size)
和Eigen::lastN(size,incr)
代替Eigen::placeholders::lastN(size)
和Eigen::placeholders::lastN(size,incr)
目的 | 代码 | 等价的块操作 |
---|---|---|
向量v的最后n个元素 | v(lastN(n)) | v.tail(n) |
A右下角m行n列的块 | A(lastN(m), lastN(n)) | A.bottomRightCorner(m,n) |
A的最后n列,步长为m | A(all, lastN(n,m)) |
示例如下:
Eigen::VectorXi v{
{
4,2,5,8,3}};
cout << "Initial vector v:\n"
<< v << "\n\n";
// Eigen提供最后几个元素的提取函数
// 向量v最后n个元素
cout << "向量v最后n个元素: (n: 3)\n"
<< v(Eigen::lastN(3)) << "\n\n";
MatrixXi A = MatrixXi::Random(7, 6);
cout << "Initial matrix A:\n"
<< A << "\n\n";
// A右下角m行n列的块
cout << "A右下角m行n列的块: (m: 3, n: 2)\n"
<< A(Eigen::lastN(3), Eigen::lastN(2)) << "\n\n";
// A的最后n列,步长为m
cout << "A的最后n列, 步长为m: (n: 3, m: 2)\n"
<< A(Eigen::all, Eigen::lastN(3, 2)) << "\n\n";
输出如下:
Initial vector v:
4
2
5
8
3
向量v最后n个元素: (n: 3)
5
8
3
Initial matrix A:
730547559 576018668 971155939 -552146456 1071432243 52156343
-226810938 -477225175 893772102 -779039257 -605038689 -13780431
607950953 115899597 291438716 653214605 27772105 1015276632
640895091 -48539462 466641602 -737276042 728237978 -445566813
884005969 276748203 -769652652 -212720294 241892198 582736218
-649503489 -290373134 229713912 -795018962 -438018766 57434405
-353856438 28778235 -1038736613 -840076701 295391245 579635549
A右下角m行n列的块: (m: 3, n: 2)
241892198 582736218
-438018766 57434405
295391245 579635549
A的最后n列, 步长为m: (n: 3, m: 2)
576018668 -552146456 52156343
-477225175 -779039257 -13780431
115899597 653214605 1015276632
-48539462 -737276042 -445566813
276748203 -212720294 582736218
-290373134 -795018962 57434405
28778235 -840076701 579635549
编译时的大小和步长
在性能方面,Eigen和编译器可以利用编译时的大小和步长。为此,可以使用Eigen::fix<val>
在编译时强制指定大小。而且,它可以和Eigen::last
符号一起使用:
v(seq(last-fix<7>, last-fix<2>))
在这个示例中,Eigen在编译时就知道返回的表达式有6个元素。它等价于:
v(seqN(last-7, fix<6>))
我们可以访问A的偶数列,如:
A(all, seq(0,last,fix<2>))
倒序
也可以把步长设置为负数,按降序枚举行/列索引,例如,从第 20 列开始到第 10 列结束, 步长为-2
:
A(all, seq(20, 10, fix<-2>))
从最后一行开始,取n行:
A(seqN(last, n, fix<-1>), all)
也可以使用ArithmeticSequence::reverse()
方法来反转序列,前面的例子也可以写成:
A(lastN(n).reverse(), all)
索引序列
operator()
输入的也可以是ArrayXi
, std::vector<int>
, std::array<int,N>
等,如:
示例:
std::vector<int> ind{
4,2,5,5,3};
MatrixXi A = MatrixXi::Random(4,6);
cout << "Initial matrix A:\n" << A << "\n\n";
cout << "A(all,ind):\n" << A(Eigen::placeholders::all,ind) << "\n\n";
输出如下:
Initial matrix A:
7 9 -5 -3 3 -10
-2 -6 1 0 5 -5
6 -3 0 9 -8 -8
6 6 3 9 2 6
A(all,ind):
3 -5 -10 -10 -3
5 1 -5 -5 0
-8 0 -8 -8 9
2 3 6 6 9
也可以直接传递一个静态数组:
MatrixXi A = MatrixXi::Random(4,6);
cout << "Initial matrix A:\n" << A << "\n\n";
cout << "A(all,{4,2,5,5,3}):\n" << A(Eigen::placeholders::all,{
4,2,5,5,3}) << "\n\n";
输出:
Initial matrix A:
7 9 -5 -3 3 -10
-2 -6 1 0 5 -5
6 -3 0 9 -8 -8
6 6 3 9 2 6
A(all,{4,2,5,5,3}):
3 -5 -10 -10 -3
5 1 -5 -5 0
-8 0 -8 -8 9
2 3 6 6 9
也可以传递一个表达式:
ArrayXi ind(5); ind<<4,2,5,5,3;
MatrixXi A = MatrixXi::Random(4,6);
cout << "Initial matrix A:\n" << A << "\n\n";
cout << "A(all,ind-1):\n" << A(Eigen::placeholders::all,ind-1) << "\n\n";
输出:
Initial matrix A:
7 9 -5 -3 3 -10
-2 -6 1 0 5 -5
6 -3 0 9 -8 -8
6 6 3 9 2 6
A(all,ind-1):
-3 9 3 3 -5
0 -6 5 5 1
9 -3 -8 -8 0
9 6 2 2 3
当传递一个具有编译时大小的对象(如Array4i
、std::array<int, N>
或静态数组)时,返回的表达式也会显示编译时维度。
自定义索引列表
更一般的,operator()
可以接受任何类型的对象:
Index s = ind.size(); or Index s = size(ind);
Index i;
i = ind[i];
这意味着可以构建自己的序列生成器并将其传递给operator()。下面是一个通过重复填充额外的第一行和列来扩大给定矩阵的示例:
struct pad {
Index size() const {
return out_size; }
Index operator[] (Index i) const {
return std::max<Index>(0,i-(out_size-in_size)); }
Index in_size, out_size;
};
Matrix3i A;
A.reshaped() = VectorXi::LinSpaced(9,1,9);
cout << "Initial matrix A:\n" << A << "\n\n";
MatrixXi B(5,5);
B = A(pad{
3,5}, pad{
3,5});
cout << "A(pad{3,N}, pad{3,N}):\n" << B << "\n\n";
输出:
Initial matrix A:
1 4 7
2 5 8
3 6 9
A(pad{3,N}, pad{3,N}):
1 1 1 4 7
1 1 1 4 7
1 1 1 4 7
2 2 2 5 8
3 3 3 6 9