This bugzilla service is closed. All entries have been migrated to https://gitlab.com/libeigen/eigen
Bug 457 - Document CwiseNullaryOp
Summary: Document CwiseNullaryOp
Status: REOPENED
Alias: None
Product: Eigen
Classification: Unclassified
Component: Documentation (show other bugs)
Version: 3.0
Hardware: All All
: Normal enhancement
Assignee: Nobody
URL:
Whiteboard:
Keywords:
Depends on: 1286
Blocks: 138
  Show dependency treegraph
 
Reported: 2012-05-04 13:43 UTC by Bruno Daniel
Modified: 2019-12-04 11:38 UTC (History)
4 users (show)



Attachments
Code for some CwiseNullaryOp applications with tests (13.44 KB, text/x-c)
2012-05-20 23:14 UTC, Bruno Daniel
no flags Details

Description Bruno Daniel 2012-05-04 13:43:28 UTC
I'd like to suggest a new matrix type derived from MatrixBase with roughly the
following constructor and behavior:

/**
 * CalcMatrix is a read-only matrix without storage recalculating the
 * coefficients in each coeff() call by calling the user function func lazily.
 * 
 * func may be a function pointer, an object with overloaded operator() or
 * one of C++0x's lambda functions, std::function.
 * 
 * coeff(i, j) is calculated as
 * func(rowOffset + i * rowStride, colOffset + j * colStride)
 *
 * This is for matrices where the repeated recalculation of the coefficients
 * is less costly than creating and accessing the storage only a small but
 * part of the coefficients is actually accessed (which part is only known at
 * runtime).
 * 
 * The common views of CalcMatrix are easily implemented:
 * (Nontrivial rowStride and colStride settings are propagated to the
 * views. m_... is the member corresponding to the constructor parameter.)
 *
 * - row(i) = CalcMatrix(1, cols(), m_func, i, 0, m_rowStride)
 * 
 * - col(j) = CalcMatrix(rows(), 1, m_func, 0, j, 1, m_colStride)
 *
 * - diagonal() = CalcMatrix(rows(), 1, m_func, 0, 0,
 *     cols() * m_colStride + m_rowStride)
 *
 * - block(i, j, rows, cols) = CalcMatrix(rows, cols, m_func, i, j,
 *      m_rowStride, m_colStride)
 *
 *
 * Applications:
 * 
 * - iota(n) = CalcMatrix(n, [](Index i) { return i; })
 *
 * - Permuted-rows view of another matrix A or a selection of a subset of rows
 * of A in any order:
 * 
 * permute_rows(A, perm) =
 * CalcMatrix(perm.size(), A.cols(),
 *   [&](Index i, Index j) { return A(perm(i), j); }
 *
 * - access to non-contiguous matrix layouts of external libraries.
 *
 * - Kronecker delta: probably better accomplished with
 *   VectorXd::Ones(rows).asDiagonal()
 */
template <typename Functype>
CalcMatrix(Index rows, Index cols, Functype& func,
  Index rowOffset, Index colOffset, Index rowStride = 1, Index colStride = 1);

As an optimization, rowStride and colStride (and rowOffset, colOffset) could
also be offered as compile-time parameters in order to make

  func(rowOffset + i * rowStride, colOffset + j * colStride)
  
less costly.
  
A similar subclass for ArrayBase would have to be added in order to be able
to call array() without significant overhead.
Comment 1 Bruno Daniel 2012-05-04 13:56:55 UTC
Sorry for the typos:

 * This is for matrices where the repeated recalculation of the coefficients
 * is less costly than creating and accessing the storage
 * or only a small part of the coefficients is actually accessed.
 * (Which part is only known at runtime).
Comment 2 Bruno Daniel 2012-05-04 14:09:13 UTC
For cases where there actually is an underlying storage that is indirectly
accessed, the user function might be specified to return a reference. Then
the read-only-requirement could be dropped.
Comment 3 Gael Guennebaud 2012-05-04 23:14:59 UTC
That seems to be already supported by the CwiseNullaryOp expression except there is no offsets nor strides but these later, if needed, can be attached to the functor.
Comment 4 Bruno Daniel 2012-05-05 17:20:46 UTC
Thanks for the hint! Sorry for overlooking that.
Comment 5 Bruno Daniel 2012-05-07 22:51:49 UTC
I tried it out and implemented some permuted views on matrices
and vectors based on CwiseNullaryOp. Thanks a lot!
Comment 6 Bruno Daniel 2012-05-08 15:37:57 UTC
Please add an example like the implementation of identity_op 
(see src/Core/Functors.h) to the documentation. 

For the user of CwiseNullaryOp it's very important to know about the 
traits class functor_has_linear_access that must be specialized if 
the two-index variant of the overloaded operator() is to be used:

template<typename Scalar> struct functor_has_linear_access<scalar_identity_op<Scalar> > { 
  enum { ret = 0 }; 
};

Otherwise the CwiseNullaryOp will be linearly accessed even if it is
declared as a matrix.
Comment 7 Gael Guennebaud 2012-05-14 13:19:01 UTC
Would you like to propose a patch for the documentation?

Also, it might be interesting to share your piece of code for the permuted views. Using a CwiseNullaryOp for this use case does not seems natural at a first glance, but that's indeed probably the easiest way. Reshape-like and slicing operations could probably be implemented the same way.
Comment 8 Jitse Niesen 2012-05-14 18:15:04 UTC
I reopened the bug because CwiseNullaryOp really needs better documentation.
Comment 9 Bruno Daniel 2012-05-20 23:14:36 UTC
Created attachment 270 [details]
Code for some CwiseNullaryOp applications with tests

Dear Gael Guennebaud,

I attach my code for 5 new utilities based on CwiseNullaryOp here:

 * iota: virtual column vector with elements 0, ..., n - 1 with optional offset
 * iota_row: the same as a row vector
 * selection_of_onedim: a view to a an arbitrarily ordered selection selection
 of the elements of a vector x
 * selection_of_rows: a view to a an arbitrarily ordered selection of the rows 
 of 2-dimensional dense data (matrix or two-dimensional array).
 * selection_of_cols: the same for columns

There are also some tests based on Eigen's unit testing framework.

Please put <Eigen source>/test/main.h in the current directory and
compile this file with g++: 

g++ -I<path to include for Eigen> eigen_publish.cpp -o eigen_publish

In the code, I also discussed a question that arose in the implementation:
What is the best way to keep a reference to an Eigen matrix or vector
in an object without copying the data?
Comment 10 Bruno Daniel 2012-05-20 23:35:51 UTC
I tried to support both matrices and arrays in the above tools, the only
exception being that the results of selection_of_rows and selection_of_columns
are always matrices.

Here are the tests/examples:

void test_iota() {
  VectorXl x(4);
  x = iota(4, 10l);
  
  VectorXl expected(4);
  expected << 10, 11, 12, 13;

  VERIFY_IS_EQUAL(x, expected);

  VERIFY_IS_EQUAL(x.sum(), 46);

  RowVectorXd x2(4);
  x2 = iota_row(4, 0.0);

  RowVectorXd expected2(4);
  expected2 << 0, 1, 2, 3;
  VERIFY_IS_EQUAL(x2, expected2);
}

void test_selection_of_onedim() {
  VectorXd x(5);
  x << 4, 3, 9, 2, 10;
  
  VectorXi selection(4);
  selection << 4, 0, 2, 4;
  VectorXd expected(4);
  expected << 10, 4, 9, 10;

  VERIFY_IS_EQUAL(selection_of_onedim(x, selection), expected);

  selection.resize(6);
  selection << 4, 0, 2, 4, 1, 4;
  expected.resize(6);
  expected << 10, 4, 9, 10, 3, 10;

  VERIFY_IS_EQUAL(selection_of_onedim(x, selection), expected);
}

void test_selection_of_rows_cols() {
  MatrixXd A(5, 2);
  A <<
    3, 4,
    9, 1,
    2, 8,
    0, 9,
    5, 7;

  VectorXi selection(4);
  selection << 4, 0, 2, 4;
  
  MatrixXd expected(4, 2);
  expected <<
    5, 7,
    3, 4,
    2, 8,
    5, 7;

  VERIFY_IS_EQUAL(selection_of_rows(A, selection), expected);

  expected.resize(2, 4);
  expected <<
    5, 3, 2, 5,
    7, 4, 8, 7;

  VERIFY_IS_EQUAL(selection_of_cols(A.transpose(), selection), expected);
}
Comment 11 Nobody 2019-12-04 11:38:21 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to gitlab.com's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.com/libeigen/eigen/issues/457.

Note You need to log in before you can comment on or make changes to this bug.