Eigen(4)-Block operation and Advanced initialzation(块操作与高级初始化)

块操作

块操作(矩阵子块)

块是矩阵或阵列的矩形部分。块表达式既可以用作右值,也可以用作左值。与Eigen表达式一样,只要让编译器进行优化,此抽象的运行时成本为零。
Eigen最通用的块操作是.block(),有两种版本,如下

Block operationVersion constructing a dynamic-size block expressionVersion constructing a fixed-size block expression
Block of size (p,q), starting at (i,j)matrix.block(i,j,p,q);matrix.block<p,q>(i,j);

与Eigen一样,索引从0开始。
两种版本均可用于固定大小和动态大小的矩阵和数组。这两个表达式在语义上是等效的。唯一的区别是,如果块大小较小,则固定大小版本通常会为您提供更快的代码,但是需要在编译时知道此大小。
以下程序使用动态尺寸和固定尺寸的版本来打印矩阵内几个块的值。

#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;
  }
}

output

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

output

	
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 2x2 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()方法可用于任何块操作,但对于特殊情况,还有其他方法可提供更专业的API和/或更好的性能。关于性能,最重要的是在编译时为Eigen提供尽可能多的信息。例如,如果您的块是矩阵中的单个整列,则使用下面描述的专用.col()函数让Eigen知道这一点,这可以为其提供优化机会

列和行

单独的列和行是块的特殊情况。Eigen提供了可以轻松解决它们的方法:.col()和.row()

块操作方法
ith row *matrix.row(i);
jth column *matrix.col(j);

.col() 和 .row()是列和行的索引,在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;
}

output

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>();
左下角p行q列matrix.bottomLeftCorner(p,q);matrix.bottomLeftCorner<p,q>();
右上角p行q列matrix.topRightCorner(p,q);matrix.topRightCorner<p,q>();
右下角p行q列matrix.bottomRightCorner(p,q);matrix.bottomRightCorner<p,q>();
包含前q行的块*matrix.topRows(q);matrix.topRows ();
包含最后q行的块*matrix.bottomRows(q)matrix.bottomRows ();
包含前p列的块*matrix.leftCols(p);matrix.leftCols

();

包含最后q列的块*matrix.rightCols(q);matrix.rightCols ();
#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;
}

output

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 ();
块包含n元素,从位置i开始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;
}

output

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
向量拼接

初始化列表的元素本身可以是向量或矩阵。通常的用途是将向量或矩阵连接在一起。例如,这是将两个行向量连接在一起的方法。请记住,必须先设置大小,然后才能使用逗号初始化程序。

int main()
{
  RowVectorXd vec1(3);
  vec1 << 1, 2, 3;
  std::cout << "vec1 = " << vec1 << std::endl;
  RowVectorXd vec2(4);
  vec2 << 1, 4, 9, 16;
  std::cout << "vec2 = " << vec2 << std::endl;
  RowVectorXd joined(7);
  joined << vec1, vec2;
  std::cout << "joined = " << joined << std::endl;
  return 0;
}

output

vec1 = 1 2 3
vec2 = 1 4 9 16
joined = 1 2 3 1 4 9 16

可以使用相同的技术来初始化具有块结构的矩阵:

MatrixXf matA(2,2);
matA << 1,2,3,4;
MatrixXf matB(4,4);
matB << matA,matA / 10,matA / 10,matA;
std :: cout << matB << std :: endl;

output

1 2 0.1 0.2
  3 4 0.3 0.4
0.1 0.2 1 2
0.3 0.4 3 4

逗号初始值设定项还可用于填充块表达式,例如m.row(i)。与上面的第一个示例相比,这是一种更复杂的方法来获得相同的结果:

Matrix3f m;
m.row(0)<< 1,2,3;
m.block(1,0,2,2)<< 4,5,7,8;
m.col(2).tail(2)<< 6,9;                   
std :: cout << m;

output

1 2 3
4 5 6
7 8 9
特殊矩阵和数组

矩阵和数组类都有.Zero(),可用于所有系数初始化为0,它有三种变体:
第一个变体不带参数,只能用于固定大小的对象。如果要将动态尺寸对象初始化为零,则需要指定尺寸。因此,第二个变体需要一个参数,并且可以用于一维动态尺寸的对象,而第三个变体需要两个参数,并且可以用于二维对象。以下示例说明了这三种变体:

std :: cout << “固定大小的数组:\ n” ;
Array33f a1 = Array33f :: Zero();
std :: cout << a1 << “ \ n \ n” ;
std :: cout << “一维动态尺寸数组:\ n” ;
ArrayXf a2 = ArrayXf :: Zero(3);
std :: cout << a2 << “ \ n \ n” ;
std :: cout << “二维动态尺寸数组:\ n” ;
ArrayXXf a3 = ArrayXXf :: Zero(3,4);
std :: cout << a3 << “ \ n” ;

output

A fixed-size array:
0 0 0
0 0 0
0 0 0

A one-dimensional dynamic-size array:
0
0
0

A two-dimensional dynamic-size array:
0 0 0 0
0 0 0 0
0 0 0 0

类似的,静态方法Constant(value)给所有元素设置值,如果需要指定对象的大小,则附加参数放在value参数之前,如中所示MatrixXd::Constant(rows, cols, value)。
方法Random()用随机系数填充矩阵或数组,调用Identity()可以获得确定的矩阵,这个方法只适用于矩阵;
方法Linspaced(size,low,high)只适用与 vectors and one-dimensional arrays;它产生一个指定大小的向量,其系数在low和high之间等距分布
LinSpaced()方法在下面的示例中进行了说明,该方法打印一个表,其中包含角度(度)、对应的角度(弧度)以及它们的正弦和余弦

ArrayXXf table(10, 4);
table.col(0) = ArrayXf::LinSpaced(10, 0, 90);
table.col(1) = M_PI / 180 * table.col(0);
table.col(2) = table.col(1).sin();
table.col(3) = table.col(1).cos();
std::cout << "  Degrees   Radians      Sine    Cosine\n";
std::cout << table << std::endl;

output

Degrees   Radians      Sine    Cosine
        0         0         0         1
       10     0.175     0.174     0.985
       20     0.349     0.342      0.94
       30     0.524       0.5     0.866
       40     0.698     0.643     0.766
       50     0.873     0.766     0.643
       60      1.05     0.866       0.5
       70      1.22      0.94     0.342
       80       1.4     0.985     0.174
       90      1.57         1 -4.37e-08

此示例显示可以将诸如LinSpaced()返回的对象的对象分配给变量(和表达式)。Eigen定义了诸如setZero(),MatrixBase :: setIdentity()和DenseBase :: setLinSpaced()之类的实用程序函数来方便地执行此操作。
下面的示例对比了三种构造矩阵的方法:使用静态方法和分配,使用静态方法和逗号初始化程序或使用setXxx()方法。

const  int size = 6;
MatrixXd mat1(大小,大小);
mat1.topLeftCorner(size / 2,size / 2)= MatrixXd :: Zero(size / 2,size / 2);
mat1.topRightCorner(size / 2,size / 2)= MatrixXd :: Identity(size / 2,size / 2);
mat1.bottomLeftCorner(size / 2,size / 2)= MatrixXd :: Identity(size / 2,size / 2);
mat1.bottomRightCorner(size / 2,size / 2)= MatrixXd :: Zero(size / 2,size / 2);
std :: cout << mat1 << std :: endl << std :: endl;
MatrixXd mat2(大小,大小);
mat2.topLeftCorner(size / 2,size / 2).setZero();
mat2.topRightCorner(size / 2,size / 2).setIdentity();
mat2.bottomLeftCorner(size / 2,size / 2).setIdentity();
mat2.bottomRightCorner(size / 2,size / 2).setZero();
std :: cout << mat2 << std :: endl << std :: endl;
MatrixXd mat3(大小,大小);
mat3 << MatrixXd :: 0(size / 2,size / 2),MatrixXd :: Identity(size / 2,size / 2),
        MatrixXd :: Identity(size / 2,size / 2),MatrixXd :: Zero(size / 2,size / 2);
std :: cout << mat3 << std :: endl;

output

0 0 0 1 0 0
0 0 0 0 1 0
0 0 0 0 0 1
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0

0 0 0 1 0 0
0 0 0 0 1 0
0 0 0 0 0 1
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0

0 0 0 1 0 0
0 0 0 0 1 0
0 0 0 0 0 1
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
用作临时对象

如上所示,可以在声明时或在赋值运算符的右侧使用静态方法Zero()和Constant()来初始化变量。您可以将这些方法视为返回矩阵或数组。实际上,它们返回所谓的表达式对象,这些表达式对象在需要时求值到矩阵或数组,因此该语法不会产生任何开销。
这些表达式也可以用作临时对象

#include <iostream>
#include <Eigen/Dense>
using namespace Eigen;
using namespace std;
int main()
{
  MatrixXd m = MatrixXd::Random(3,3);
  m = (m + MatrixXd::Constant(3,3,1.2)) * 50;
  cout << "m =" << endl << m << endl;
  VectorXd v(3);
  v << 1, 2, 3;
  cout << "m * v =" << endl << m * v << endl;
}

output

m =
  94 89.8 43.5
49.4  101 86.8
88.3 29.8 37.8
m * v =
404
512
261

该表达式构造所有系数等于1.2加上相应的m系数的3-by-3矩阵表达式m + MatrixXf::Constant(3,3,1.2)
逗号初始化程序也可以用来构造临时对象。以下示例构造了一个大小为2×3的随机矩阵,然后将该左边的矩阵乘以2*2矩阵

MatrixXf mat = MatrixXf::Random(2, 3);
std::cout << mat << std::endl << std::endl;
mat = (MatrixXf(2,2) << 0, 1, 1, 0).finished() * mat;
std::cout << mat << std::endl;

output

  0.68  0.566  0.823
-0.211  0.597 -0.605

-0.211  0.597 -0.605
  0.68  0.566  0.823

一旦临时子矩阵的逗号初始化完成,此处的finish()方法对于获取实际的矩阵对象是必需的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值