Code360 powered by Coding Ninjas X Naukri.com. Code360 powered by Coding Ninjas X Naukri.com
Table of contents
1.
Introduction
2.
Nullary Expressions
3.
Circulant Matrices
4.
Indexing Rows and Columns
5.
Frequently Asked Questions
5.1.
What are some common operations that the Eigen Library can perform?
5.2.
What are the most common fields in which the Eigen Library is used?
5.3.
What is the conjugate of a matrix?
5.4.
What is OpenGL?
5.5.
What are the most common operations that the Eigen Library can perform?
6.
Conclusion 
Last Updated: Mar 27, 2024
Medium

Matrix Manipulation via Nullary Expressions

Introduction

Eigen is a handy C++ library used for linear algebra and matrix multiplication. Another useful implementation it provides is its matrix manipulation. 

This article will discuss how Eigen does matrix manipulation via nullary expressions. Our main focus will be on nullary expression and matrix manipulation via nullary expressions. We will also discuss implementing circulant matrices and indexing rows and columns here. 

So let's get started!

matrix manipulation via nullary expressions

Nullary Expressions

Before starting matrix manipulation via nullary expressions, we should first go through nullary expressions. The Nullary Expressions class represents a generic nullary operator expression. The main purpose of the class is to define procedural matrices.  These matrices can be constant or random matrices. They are returned by the methods Ones(), Zero(), Constant(), Identity(), and Random().

NullaryOp must expose one of the following methods:

  1. operator()(): If the procedural generation is independent of the coefficient entries (e.g., random numbers).
     
  2. operator()(Index i): If the procedural generation is only applicable to vectors and is dependent on the coefficient index i (for e.g., linspace).
     
  3. operator()(Index i, Index j): If the procedural generation is affected by matrix coordinates i, j (e.g., to generate a checkerboard with 0 and 1).

Circulant Matrices

A circulant matrix is a square matrix in which all row vectors have the same elements.  And they have rotated one element to the right of the preceding row vector. This section will discuss making circulant matrices with matrix manipulation via nullary expressions.

circulant matrices

We first create a template (generic function) of struct type name circulant_helper. Which have a scaler-type argument. It stores the number of rows and columns dynamically, i.e. on the compile time. The storage order will be in column-major.

template<class ArgType>
struct circulant_helper 
{
	typedef Eigen::Matrix<typename ArgType::Scalar,
         ArgType::SizeAtCompileTime,
         ArgType::SizeAtCompileTime,
         Eigen::ColMajor,
         ArgType::MaxSizeAtCompileTime,
         ArgType::MaxSizeAtCompileTime> MatrixType;
};
You can also try this code with Online C++ Compiler
Run Code

 

This class represents a general nullary operator expression. It is the return type of the Ones(), Zero(), Constant(), Identity(), and Random() methods, and it is almost always used in this manner. This function will call circulant_functor and returns the value in a circulant order. The makeCirculant function uses the circulant_helper and circulant_functor to do row vice nullary operations.

template <class ArgType>
Eigen::CwiseNullaryOp<circulant_functor<ArgType>, typename circulant_helper<ArgType>::MatrixType>
makeCirculant(const Eigen::MatrixBase<ArgType>& arg)
{
 	  typedef typename circulant_helper<ArgType>::MatrixType MatrixTypes;
 	  return MatrixTypes::NullaryExp(arg.size(), arg.size(), circulant_functor<ArgType>(arg.derived()));
}
You can also try this code with Online C++ Compiler
Run Code


This function will return the value in circulant order to the main function. The index size is initialised as a difference between rows and columns. If the index < 0, the value of the index will get continuously added to the m_vec.size();

template<class ArgType>
class circulant_functor {
    const ArgType & m_vec;
    public: circulant_functor(const ArgType & arg): m_vec(arg) {}
    const typename ArgType::Scalar & operator()(Eigen::Index row, Eigen::Index col) 
    const {  
        Eigen::Index index = row - col; 
        if (index < 0) index += m_vec.size();  
        return m_vec(index);
    }
};
You can also try this code with Online C++ Compiler
Run Code


Below is the implementation of the main function. Here we are initialising the vector with the desired value. Finally, we are calling the makeCirculant function.

int main()
{
	 Eigen::VectorXd vec(4);
	 vec << 1, 3, 5, 6;
	 Eigen::MatrixXd mat;
	 mat = makeCirculant(vec);
	 cout << mat <<  endl;
}
You can also try this code with Online C++ Compiler
Run Code


Final Implementable Program:

#include <Eigen/Dense>
#include <iostream>
using namespace std;

template<class ArgType>
struct circulant_helper 
{
	typedef Eigen::Matrix<typename ArgType::Scalar,
         ArgType::SizeAtCompileTime,
         ArgType::SizeAtCompileTime,
         Eigen::ColMajor,
         ArgType::MaxSizeAtCompileTime,
         ArgType::MaxSizeAtCompileTime> MatrixType;
};

template <class ArgType>
Eigen::CwiseNullaryOp<circulant_functor<ArgType>, typename circulant_helper<ArgType>::MatrixType>
makeCirculant(const Eigen::MatrixBase<ArgType>& arg)
{
 	  typedef typename circulant_helper<ArgType>::MatrixType MatrixTypes;
 	  return MatrixTypes::NullaryExp(arg.size(), arg.size(), circulant_functor<ArgType>(arg.derived()));
}

template<class ArgType>
class circulant_functor 
{
 const ArgType &m_vec;
 public:
	 circulant_functor(const ArgType& arg) : m_vec(arg) {}
	 const typename ArgType::Scalar& operator() (Eigen::Index row, Eigen::Index col) const 
	 {
 	    Eigen::Index index = row - col;
  		if (index < 0) index += m_vec.size();
    	return m_vec(index);
	 }
};

int main()
{
	 Eigen::VectorXd vec(4);
	 vec << 1, 3, 5, 6;
	 Eigen::MatrixXd mat;
	 mat = makeCirculant(vec);
	 cout << mat <<  endl;
}
You can also try this code with Online C++ Compiler
Run Code


Output:

output of circulant matrix

Explanation:

The above program did matrix manipulation via nullary expression to make a circulant matrix. Here we create generic functions to do particular operations. The circulant_helper and circulant_functor functions store and generate nullary operator expressions. Afterwards, the makeCirculant function uses the circulant_helper and circulant_functor to do row vice nullary operations.

Try and compile by yourself with the help of online C++ Compiler for better understanding.

Indexing Rows and Columns

We use matrix manipulation via nullary expressions to match Matlab's indexing of a matrix using two vectors. It uses the vectors of indices that correspond to the rows and columns to be selected.

First, we will create a nullary_functor that stores references to the input matrix. The two arrays of indices and the necessary operator()(i,j). The order of storage will be row-major or column-major depending upon Flags&RowMajorBit. Afterwards, the indexing_functor function will be called.

template<class ArgType, class RowIndexType, class ColIndexType>
class indexing_functor 
{
	 const ArgType &m_arg;
	 const RowIndexType &m_rowIndices;
	 const ColIndexType &m_colIndices;
	 public:
		 typedef Eigen::Matrix<typename ArgType::Scalar,
          RowIndexType::SizeAtCompileTime,
          ColIndexType::SizeAtCompileTime,
          ArgType::Flags&Eigen::RowMajorBit?Eigen::RowMajor:Eigen::ColMajor,
          RowIndexType::MaxSizeAtCompileTime,
          ColIndexType::MaxSizeAtCompileTime> MatrixType;
 	indexing_functor(const ArgType& arg, const RowIndexType& row_indices, const ColIndexType& col_indices): m_arg(arg), m_rowIndices(row_indices), m_colIndices(col_indices)
 {}
 const typename ArgType::Scalar& operator() (Eigen::Index row, Eigen::Index col) const 
 {
   return m_arg(m_rowIndices[row], m_colIndices[col]);
 }
};
You can also try this code with Online C++ Compiler
Run Code


Now we will create an indexing() function. The value passed are Array, rows and cols, creating the nullary expression. Finally, it will return the size of rows and columns and rows indices and column indices.

template <class ArgType, class RowIndexType, class ColIndexType>
Eigen::CwiseNullaryOp<indexing_functor<ArgType,RowIndexType,ColIndexType>, typename indexing_functor<ArgType,RowIndexType,ColIndexType>::MatrixType>
mat_indexing(const Eigen::MatrixBase<ArgType>& arg, const RowIndexType& row_indices, const ColIndexType& col_indices)
{
	 typedef indexing_functor<ArgType,RowIndexType,ColIndexType> Func;
	 typedef typename Func::MatrixType MatrixType;
	 return MatrixType::NullaryExpr(row_indices.size(), col_indices.size(), Func(arg.derived(), row_indices, col_indices));
}
You can also try this code with Online C++ Compiler
Run Code


The following whole section builds up the main function. Which is further implemented in the final program.

Eigen::MatrixXi A = Eigen::MatrixXi::Random(4,4);
Eigen::Array3i ri(1,2,1);
Eigen::ArrayXi ci(6); ci << 3,2,1,0,0,2;
Eigen::MatrixXi B = mat_indexing(A, ri, ci);
	  cout << "A =" <<  endl;
	  cout << A <<  endl <<  endl;
	  cout << "A([" << ri.transpose() << "], [" << ci.transpose() << "]) =" <<  endl;
	  cout << B <<  endl;

B =  mat_indexing(A, ri+1, ci);
 	  cout << "A(ri+1,ci) =" <<  endl;
      cout << B <<  endl <<  endl;
B =  mat_indexing(A, Eigen::ArrayXi::LinSpaced(13,0,12).unaryExpr([](int x){return x%4;}),

Eigen::ArrayXi::LinSpaced(4,0,3));
 	  cout << "A(ArrayXi::LinSpaced(13,0,12).unaryExpr([](int x){return x%4;}), ArrayXi::LinSpaced(4,0,3)) =" <<  endl;
 	  cout << B <<  endl;
You can also try this code with Online C++ Compiler
Run Code


Final Implementable Program

#include <Eigen/Dense>
#include <iostream>
using namespace std;

template <class ArgType, class RowIndexType, class ColIndexType>
    class indexing_functor {
        const ArgType & m_arg;
        const RowIndexType & m_rowIndices;
        const ColIndexType & m_colIndices;
        public: typedef Eigen::Matrix < typename ArgType::Scalar,
                 RowIndexType::SizeAtCompileTime,
                 ColIndexType::SizeAtCompileTime,
                 ArgType::Flags & Eigen::RowMajorBit ? Eigen::RowMajor : Eigen::ColMajor,
                 RowIndexType::MaxSizeAtCompileTime,
                 ColIndexType::MaxSizeAtCompileTime > MatrixType;
        indexing_functor(const ArgType & arg, const RowIndexType & row_indices, const ColIndexType & col_indices): m_arg(arg),
        m_rowIndices(row_indices),
        m_colIndices(col_indices) {}
        const typename ArgType::Scalar & operator()(Eigen::Index row, Eigen::Index col) const {  
            return m_arg(m_rowIndices[row], m_colIndices[col]);
        }
};


template < class ArgType, class RowIndexType, class ColIndexType >
    Eigen::CwiseNullaryOp < indexing_functor < ArgType, RowIndexType, ColIndexType > , typename indexing_functor < ArgType, RowIndexType, ColIndexType > ::MatrixType >
    mat_indexing(const Eigen::MatrixBase < ArgType > & arg, const RowIndexType & row_indices, const ColIndexType & col_indices) {
        typedef indexing_functor < ArgType, RowIndexType, ColIndexType > Func;
        typedef typename Func::MatrixType MatrixType;
        return MatrixType::NullaryExpr(row_indices.size(), col_indices.size(), Func(arg.derived(), row_indices, col_indices));
}


int main() {
    Eigen::MatrixXi A = Eigen::MatrixXi::Random(4, 4);
    Eigen::Array3i ri(1, 2, 1);
    Eigen::ArrayXi ci(6);
    ci << 3, 2, 1, 0, 0, 2;
    Eigen::MatrixXi B = mat_indexing(A, ri, ci); 
    cout << "A =" <<  endl; 
    cout << A <<  endl <<  endl; 
    cout << "A([" << ri.transpose() << "], [" << ci.transpose() << "]) =" <<  endl; 
    cout << B <<  endl;

    B =  mat_indexing(A, ri + 1, ci); 
    cout << "A(ri+1,ci) =" <<  endl; 
    cout << B <<  endl <<  endl;
    B =  mat_indexing(A, Eigen::ArrayXi::LinSpaced(13, 0, 12).unaryExpr([](int x) {
            return x % 4;
        }),

        Eigen::ArrayXi::LinSpaced(4, 0, 3)); 
    cout << "A(ArrayXi::LinSpaced(13,0,12).unaryExpr([](int x){return x%4;}), ArrayXi::LinSpaced(4,0,3)) =" <<  endl; 
    cout << B <<  endl;
}
You can also try this code with Online C++ Compiler
Run Code


Output:

output of indexing

Explanation:

The above program indexes rows and columns using nullary operators. The nullary_functor function stores references to the input matrix. The order of storage will be row-order. Afterwards, the indexing() function will index rows, columns, rows indices and column indices.

Check out this problem - Matrix Median

Frequently Asked Questions

What are some common operations that the Eigen Library can perform?

The Eigen's Library can be used to fulfil functions such as the definition of vectors and matrices. It can be used for various math operations such as vector add/subtract, dot and cross product.

What are the most common fields in which the Eigen Library is used?

Robotics and other real-world applications benefit from the Eigen Geometry Library. Eigen's Library is used to perform many advanced maths computations.

What is the conjugate of a matrix?

The conjugate of a matrix is obtained by replacing the elements of a matrix with their complex conjugate numbers.

What is OpenGL?

OpenGL is a cross-platform and cross-interface vector graphics rendering library. The OpenGL API is utilised to connect with the graphics processing unit. It is also used to achieve hardware-accelerated rendering.

What are the most common operations that the Eigen Library can perform?

The Eigen Library can be used to execute functions such as vector and matrix declaration. Eigen's Library can be used to execute some math operations such as vector solving, dot product, and cross product.

Conclusion 

This article discussed matrix manipulation via nullary expressions. We discussed the nullary expression, circulant matrices, and indexing rows and columns. We hope this blog has helped you with matrix manipulation via nullary expressions. If you like to learn more, you can check out our articles: 

Refer to our Guided Path on Coding Ninjas Studio to upskill yourself in Data Structures and AlgorithmsCompetitive ProgrammingJavaScriptAWS and many more! If you wish to test your skills in coding, check out the mock test series and participate in the contests hosted on Coding Ninjas Studio! 

For placement preparations, you must look at the problemsinterview experiences, and interview bundles.

You can consider our paid courses to give your career an edge over others!

Happy Learning!

Live masterclass