eigen.h
Go to the documentation of this file.
1 /*
2  * This file is a part of TiledArray.
3  * Copyright (C) 2013 Virginia Tech
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * Justus Calvin
19  * Department of Chemistry, Virginia Tech
20  *
21  * eigen.h
22  * May 02, 2015
23  *
24  */
25 
26 #ifndef TILEDARRAY_CONVERSIONS_EIGEN_H__INCLUDED
27 #define TILEDARRAY_CONVERSIONS_EIGEN_H__INCLUDED
28 
29 #include <TiledArray/error.h>
33 #include <TiledArray/tensor.h>
34 #include <tiledarray_fwd.h>
35 #include <cstdint>
36 #include "TiledArray/dist_array.h"
37 
38 namespace TiledArray {
39 
40 // Convenience typedefs
41 typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
43 typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
45 typedef Eigen::Matrix<std::complex<double>, Eigen::Dynamic, Eigen::Dynamic,
46  Eigen::RowMajor>
48 typedef Eigen::Matrix<std::complex<float>, Eigen::Dynamic, Eigen::Dynamic,
49  Eigen::RowMajor>
51 typedef Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
53 typedef Eigen::Matrix<long, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
55 typedef Eigen::Matrix<double, Eigen::Dynamic, 1> EigenVectorXd;
56 typedef Eigen::Matrix<float, Eigen::Dynamic, 1> EigenVectorXf;
57 typedef Eigen::Matrix<std::complex<double>, 1, Eigen::Dynamic> EigenVectorXcd;
58 typedef Eigen::Matrix<std::complex<float>, 1, Eigen::Dynamic> EigenVectorXcf;
59 typedef Eigen::Matrix<int, Eigen::Dynamic, 1> EigenVectorXi;
60 typedef Eigen::Matrix<long, Eigen::Dynamic, 1> EigenVectorXl;
61 
63 
72 template <typename T, Eigen::StorageOptions Storage = Eigen::RowMajor,
73  std::enable_if_t<detail::is_contiguous_tensor_v<T>>* = nullptr>
74 inline Eigen::Map<const Eigen::Matrix<typename T::value_type, Eigen::Dynamic,
75  Eigen::Dynamic, Storage>,
76  Eigen::AutoAlign>
77 eigen_map(const T& tensor, const std::size_t m, const std::size_t n) {
78  TA_ASSERT((m * n) == tensor.size());
79 
80  return Eigen::Map<const Eigen::Matrix<typename T::value_type, Eigen::Dynamic,
81  Eigen::Dynamic, Storage>,
82  Eigen::AutoAlign>(tensor.data(), m, n);
83 }
84 
86 
95 template <typename T, Eigen::StorageOptions Storage = Eigen::RowMajor,
96  std::enable_if_t<detail::is_contiguous_tensor_v<T>>* = nullptr>
97 inline Eigen::Map<Eigen::Matrix<typename T::value_type, Eigen::Dynamic,
98  Eigen::Dynamic, Storage>,
99  Eigen::AutoAlign>
100 eigen_map(T& tensor, const std::size_t m, const std::size_t n) {
101  TA_ASSERT((m * n) == tensor.size());
102 
103  return Eigen::Map<Eigen::Matrix<typename T::value_type, Eigen::Dynamic,
104  Eigen::Dynamic, Storage>,
105  Eigen::AutoAlign>(tensor.data(), m, n);
106 }
107 
109 
115 template <typename T,
116  std::enable_if_t<detail::is_contiguous_tensor_v<T>>* = nullptr>
117 inline Eigen::Map<
118  const Eigen::Matrix<typename T::value_type, Eigen::Dynamic, 1>,
119  Eigen::AutoAlign>
120 eigen_map(const T& tensor, const std::size_t n) {
121  TA_ASSERT(n == tensor.size());
122 
123  return Eigen::Map<
124  const Eigen::Matrix<typename T::value_type, Eigen::Dynamic, 1>,
125  Eigen::AutoAlign>(tensor.data(), n);
126 }
127 
129 
135 template <typename T,
136  std::enable_if_t<detail::is_contiguous_tensor_v<T>>* = nullptr>
137 inline Eigen::Map<Eigen::Matrix<typename T::value_type, Eigen::Dynamic, 1>,
138  Eigen::AutoAlign>
139 eigen_map(T& tensor, const std::size_t n) {
140  TA_ASSERT(n == tensor.size());
141 
142  return Eigen::Map<Eigen::Matrix<typename T::value_type, Eigen::Dynamic, 1>,
143  Eigen::AutoAlign>(tensor.data(), n);
144 }
145 
147 
156 template <typename T, Eigen::StorageOptions Storage = Eigen::RowMajor,
157  std::enable_if_t<detail::is_contiguous_tensor_v<T>>* = nullptr>
158 inline Eigen::Map<const Eigen::Matrix<typename T::value_type, Eigen::Dynamic,
159  Eigen::Dynamic, Storage>,
160  Eigen::AutoAlign>
161 eigen_map(const T& tensor) {
162  TA_ASSERT((tensor.range().rank() == 2u) || (tensor.range().rank() == 1u));
163  const auto* MADNESS_RESTRICT const tensor_extent =
164  tensor.range().extent_data();
165  return eigen_map<T, Storage>(
166  tensor, tensor_extent[0],
167  (tensor.range().rank() == 2u ? tensor_extent[1] : 1ul));
168 }
169 
171 
179 template <typename T, Eigen::StorageOptions Storage = Eigen::RowMajor,
180  std::enable_if_t<detail::is_contiguous_tensor_v<T>>* = nullptr>
181 inline Eigen::Map<Eigen::Matrix<typename T::value_type, Eigen::Dynamic,
182  Eigen::Dynamic, Storage>,
183  Eigen::AutoAlign>
184 eigen_map(T& tensor) {
185  TA_ASSERT((tensor.range().rank() == 2u) || (tensor.range().rank() == 1u));
186  const auto* MADNESS_RESTRICT const tensor_extent =
187  tensor.range().extent_data();
188  return eigen_map<T, Storage>(
189  tensor, tensor_extent[0],
190  (tensor.range().rank() == 2u ? tensor_extent[1] : 1ul));
191 }
192 
194 
205 template <typename T, typename Derived,
206  std::enable_if_t<detail::is_contiguous_tensor_v<T>>* = nullptr>
207 inline void eigen_submatrix_to_tensor(const Eigen::MatrixBase<Derived>& matrix,
208  T& tensor) {
209  [[maybe_unused]] typedef typename T::index1_type size_type;
210  TA_ASSERT((tensor.range().rank() == 2u) || (tensor.range().rank() == 1u));
211 
212  // Get pointers to the tensor range data
213  const auto* MADNESS_RESTRICT const tensor_lower =
214  tensor.range().lobound_data();
215  const auto* MADNESS_RESTRICT const tensor_upper =
216  tensor.range().upbound_data();
217  const auto* MADNESS_RESTRICT const tensor_extent =
218  tensor.range().extent_data();
219 
220  if (tensor.range().rank() == 2u) {
221  // Get tensor range data
222  const std::size_t tensor_lower_0 = tensor_lower[0];
223  const std::size_t tensor_lower_1 = tensor_lower[1];
224  [[maybe_unused]] const std::size_t tensor_upper_0 = tensor_upper[0];
225  [[maybe_unused]] const std::size_t tensor_upper_1 = tensor_upper[1];
226  const std::size_t tensor_extent_0 = tensor_extent[0];
227  const std::size_t tensor_extent_1 = tensor_extent[1];
228 
229  TA_ASSERT(tensor_upper_0 <= std::size_t(matrix.rows()));
230  TA_ASSERT(tensor_upper_1 <= std::size_t(matrix.cols()));
231 
232  // Copy matrix
233  eigen_map(tensor, tensor_extent_0, tensor_extent_1) = matrix.block(
234  tensor_lower_0, tensor_lower_1, tensor_extent_0, tensor_extent_1);
235  } else {
236  // Get tensor range data
237  const std::size_t tensor_lower_0 = tensor_lower[0];
238  [[maybe_unused]] const std::size_t tensor_upper_0 = tensor_upper[0];
239  const std::size_t tensor_extent_0 = tensor_extent[0];
240 
241  // Check that matrix is a vector.
242  TA_ASSERT((matrix.rows() == 1) || (matrix.cols() == 1));
243 
244  if (matrix.rows() == 1) {
245  TA_ASSERT(tensor_upper_0 <= std::size_t(matrix.cols()));
246 
247  // Copy the row vector to tensor
248  eigen_map(tensor, 1, tensor_extent_0) =
249  matrix.block(0, tensor_lower_0, 1, tensor_extent_0);
250  } else {
251  TA_ASSERT(tensor_upper_0 <= std::size_t(matrix.rows()));
252 
253  // Copy the column vector to tensor
254  eigen_map(tensor, tensor_extent_0, 1) =
255  matrix.block(tensor_lower_0, 0, tensor_extent_0, 1);
256  }
257  }
258 }
259 
261 
272 template <typename T, typename Derived,
273  std::enable_if_t<detail::is_contiguous_tensor_v<T>>* = nullptr>
274 inline void tensor_to_eigen_submatrix(const T& tensor,
275  Eigen::MatrixBase<Derived>& matrix) {
276  [[maybe_unused]] typedef typename T::index1_type size_type;
277  TA_ASSERT((tensor.range().rank() == 2u) || (tensor.range().rank() == 1u));
278 
279  // Get pointers to the tensor range data
280  const auto* MADNESS_RESTRICT const tensor_lower =
281  tensor.range().lobound_data();
282  const auto* MADNESS_RESTRICT const tensor_upper =
283  tensor.range().upbound_data();
284  const auto* MADNESS_RESTRICT const tensor_extent =
285  tensor.range().extent_data();
286 
287  if (tensor.range().rank() == 2) {
288  // Get tensor range data
289  const std::size_t tensor_lower_0 = tensor_lower[0];
290  const std::size_t tensor_lower_1 = tensor_lower[1];
291  [[maybe_unused]] const std::size_t tensor_upper_0 = tensor_upper[0];
292  [[maybe_unused]] const std::size_t tensor_upper_1 = tensor_upper[1];
293  const std::size_t tensor_extent_0 = tensor_extent[0];
294  const std::size_t tensor_extent_1 = tensor_extent[1];
295 
296  TA_ASSERT(tensor_upper_0 <= std::size_t(matrix.rows()));
297  TA_ASSERT(tensor_upper_1 <= std::size_t(matrix.cols()));
298 
299  // Copy tensor into matrix
300  matrix.block(tensor_lower_0, tensor_lower_1, tensor_extent_0,
301  tensor_extent_1) =
302  eigen_map(tensor, tensor_extent_0, tensor_extent_1);
303  } else {
304  // Get tensor range data
305  const std::size_t tensor_lower_0 = tensor_lower[0];
306  [[maybe_unused]] const std::size_t tensor_upper_0 = tensor_upper[0];
307  const std::size_t tensor_extent_0 = tensor_extent[0];
308 
309  TA_ASSERT((matrix.rows() == 1) || (matrix.cols() == 1));
310 
311  if (matrix.rows() == 1) {
312  TA_ASSERT(tensor_upper_0 <= std::size_t(matrix.cols()));
313 
314  // Copy tensor into row vector
315  matrix.block(0, tensor_lower_0, 1, tensor_extent_0) =
316  eigen_map(tensor, 1, tensor_extent_0);
317  } else {
318  TA_ASSERT(tensor_upper_0 <= std::size_t(matrix.rows()));
319 
320  // Copy tensor into column vector
321  matrix.block(tensor_lower_0, 0, tensor_extent_0, 1) =
322  eigen_map(tensor, tensor_extent_0, 1);
323  }
324  }
325 }
326 
327 namespace detail {
328 
330 
337 template <typename A, typename Derived>
338 void counted_eigen_submatrix_to_tensor(const Eigen::MatrixBase<Derived>* matrix,
339  A* array,
340  const typename A::ordinal_type i,
341  madness::AtomicInt* counter) {
342  typename A::value_type tensor(array->trange().make_tile_range(i));
343  eigen_submatrix_to_tensor(*matrix, tensor);
344  array->set(i, tensor);
345  (*counter)++;
346 }
347 
349 
355 template <typename Derived, typename T>
357  Eigen::MatrixBase<Derived>* matrix,
358  madness::AtomicInt* counter) {
359  tensor_to_eigen_submatrix(tensor, *matrix);
360  (*counter)++;
361 }
362 
363 } // namespace detail
364 
365 // clang-format off
367 
407 // clang-format on
408 template <typename A, typename Derived>
409 A eigen_to_array(World& world, const typename A::trange_type& trange,
410  const Eigen::MatrixBase<Derived>& matrix,
411  bool replicated = false,
412  std::shared_ptr<typename A::pmap_interface> pmap = {}) {
413  typedef typename A::index1_type size_type;
414  // Check that trange matches the dimensions of other
415  const auto rank = trange.tiles_range().rank();
416 
417  TA_ASSERT(rank == 1 || rank == 2 &&
418  "TiledArray::eigen_to_array(): The number of dimensions in "
419  "trange must match that of the Eigen matrix.");
420 
421  if(rank == 2) {
422  TA_ASSERT(
423  trange.elements_range().extent(0) == size_type(matrix.rows()) &&
424  "TiledArray::eigen_to_array(): The number of rows in trange is not "
425  "equal to the number of rows in the Eigen matrix.");
426  TA_ASSERT(
427  trange.elements_range().extent(1) == size_type(matrix.cols()) &&
428  "TiledArray::eigen_to_array(): The number of columns in trange is not "
429  "equal to the number of columns in the Eigen matrix.");
430  } else {
431  TA_ASSERT(
432  trange.elements_range().extent(0) == size_type(matrix.size()) &&
433  "TiledArray::eigen_to_array(): The size of trange must be equal to the "
434  "matrix size.");
435  }
436 
437  // Create a new tensor
438  if (replicated && (world.size() > 1))
439  pmap = std::static_pointer_cast<typename A::pmap_interface>(
440  std::make_shared<detail::ReplicatedPmap>(
441  world, trange.tiles_range().volume()));
442  A array = (pmap ? A(world, trange, pmap) : A(world, trange));
443 
444  // Spawn tasks to copy Eigen to an Array
445  madness::AtomicInt counter;
446  counter = 0;
447  std::int64_t n = 0;
448  for (std::size_t i = 0; i < array.size(); ++i) {
449  if (array.is_local(i)) {
450  world.taskq.add(&detail::counted_eigen_submatrix_to_tensor<A, Derived>,
451  &matrix, &array, i, &counter);
452  ++n;
453  }
454  }
455 
456  // Wait until the write tasks are complete
457  array.world().await([&counter, n]() { return counter == n; });
458 
459  // truncate, n.b. this can replace the wait above
460  array.truncate();
461 
462  return array;
463 }
464 
465 // clang-format off
467 
491 // clang-format on
492 template <typename Tile, typename Policy,
493  unsigned int EigenStorageOrder = Eigen::ColMajor>
494 Eigen::Matrix<typename Tile::value_type, Eigen::Dynamic, Eigen::Dynamic,
495  EigenStorageOrder>
497  typedef Eigen::Matrix<typename Tile::value_type, Eigen::Dynamic,
498  Eigen::Dynamic, EigenStorageOrder>
499  EigenMatrix;
500 
501  const auto rank = array.trange().tiles_range().rank();
502 
503  // Check that the array will fit in a matrix or vector
504  TA_ASSERT((rank == 2u) ||
505  (rank == 1u) &&
506  "TiledArray::array_to_eigen(): The array dimensions must be "
507  "equal to 1 or 2.");
508 
509  // Check that this is not a distributed computing environment or that the
510  // array is replicated
511  if (!array.pmap()->is_replicated())
512  TA_ASSERT(array.world().size() == 1 &&
513  "TiledArray::array_to_eigen(): non-replicated Array cannot "
514  "be assigned to an Eigen::Matrix when the number of World "
515  "ranks is greater than 1.");
516 
517  // Construct the Eigen matrix
518  const auto* MADNESS_RESTRICT const array_extent =
519  array.trange().elements_range().extent_data();
520  // if array is sparse must initialize to zero
521  EigenMatrix matrix =
522  EigenMatrix::Zero(array_extent[0], (rank == 2 ? array_extent[1] : 1));
523 
524  // Spawn tasks to copy array tiles to the Eigen matrix
525  madness::AtomicInt counter;
526  counter = 0;
527  int n = 0;
528  for (std::size_t i = 0; i < array.size(); ++i) {
529  if (!array.is_zero(i)) {
530  array.world().taskq.add(
532  EigenMatrix, typename DistArray<Tile, Policy>::value_type>,
533  array.find(i), &matrix, &counter);
534  ++n;
535  }
536  }
537 
538  // Wait until the above tasks are complete. Tasks will be processed by this
539  // thread while waiting.
540  array.world().await([&counter, n]() { return counter == n; });
541 
542  return matrix;
543 }
544 
546 
592 template <typename A>
594  World& world, const typename A::trange_type& trange,
595  const typename A::value_type::value_type* buffer, const std::size_t m,
596  const std::size_t n, const bool replicated = false,
597  std::shared_ptr<typename A::pmap_interface> pmap = {}) {
598  TA_ASSERT(trange.elements_range().extent(0) == m &&
599  "TiledArray::eigen_to_array(): The number of rows in trange "
600  "is not equal to m.");
601  TA_ASSERT(trange.elements_range().extent(1) == n &&
602  "TiledArray::eigen_to_array(): The number of columns in "
603  "trange is not equal to n.");
604 
605  typedef Eigen::Matrix<typename A::value_type::value_type, Eigen::Dynamic,
606  Eigen::Dynamic, Eigen::RowMajor>
607  matrix_type;
608  return eigen_to_array(
609  world, trange,
610  Eigen::Map<const matrix_type, Eigen::AutoAlign>(buffer, m, n), replicated,
611  pmap);
612 }
613 
615 
661 template <typename A>
663  World& world, const typename A::trange_type& trange,
664  const typename A::value_type::value_type* buffer, const std::size_t m,
665  const std::size_t n, const bool replicated = false,
666  std::shared_ptr<typename A::pmap_interface> pmap = {}) {
667  TA_ASSERT(trange.elements_range().extent(0) == m &&
668  "TiledArray::eigen_to_array(): The number of rows in trange "
669  "is not equal to m.");
670  TA_ASSERT(trange.elements_range().extent(1) == n &&
671  "TiledArray::eigen_to_array(): The number of columns in "
672  "trange is not equal to n.");
673 
674  typedef Eigen::Matrix<typename A::value_type::value_type, Eigen::Dynamic,
675  Eigen::Dynamic, Eigen::ColMajor>
676  matrix_type;
677  return eigen_to_array(
678  world, trange,
679  Eigen::Map<const matrix_type, Eigen::AutoAlign>(buffer, m, n), replicated,
680  pmap);
681 }
682 
683 } // namespace TiledArray
684 
685 #endif // TILEDARRAY_CONVERSIONS_EIGEN_H__INCLUDED
A column_major_buffer_to_array(World &world, const typename A::trange_type &trange, const typename A::value_type::value_type *buffer, const std::size_t m, const std::size_t n, const bool replicated=false, std::shared_ptr< typename A::pmap_interface > pmap={})
Convert a column-major matrix buffer into an Array object.
Definition: eigen.h:662
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > EigenMatrixXd
Definition: eigen.h:42
Eigen::Matrix< long, Eigen::Dynamic, 1 > EigenVectorXl
Definition: eigen.h:60
Eigen::Matrix< int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > EigenMatrixXi
Definition: eigen.h:52
Eigen::Matrix< std::complex< float >, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > EigenMatrixXcf
Definition: eigen.h:50
Eigen::Matrix< std::complex< float >, 1, Eigen::Dynamic > EigenVectorXcf
Definition: eigen.h:58
Eigen::Matrix< std::complex< double >, 1, Eigen::Dynamic > EigenVectorXcd
Definition: eigen.h:57
Eigen::Map< const Eigen::Matrix< typename T::value_type, Eigen::Dynamic, Eigen::Dynamic, Storage >, Eigen::AutoAlign > eigen_map(const T &tensor, const std::size_t m, const std::size_t n)
Construct a const Eigen::Map object for a given Tensor object.
Definition: eigen.h:77
A eigen_to_array(World &world, const typename A::trange_type &trange, const Eigen::MatrixBase< Derived > &matrix, bool replicated=false, std::shared_ptr< typename A::pmap_interface > pmap={})
Convert an Eigen matrix into an Array object.
Definition: eigen.h:409
void eigen_submatrix_to_tensor(const Eigen::MatrixBase< Derived > &matrix, T &tensor)
Copy a block of an Eigen matrix into a tensor.
Definition: eigen.h:207
auto rank(const DistArray< Tile, Policy > &a)
Definition: dist_array.h:1617
typename tensor_type::value_type value_type
value type
Definition: tile.h:89
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > EigenMatrixXf
Definition: eigen.h:44
#define TA_ASSERT(EXPR,...)
Definition: error.h:39
auto size() const
Returns the number of tiles in the tensor.
Definition: dist_array.h:972
const trange_type & trange() const
Tiled range accessor.
Definition: dist_array.h:917
Future< value_type > find(const Index &i) const
Find local or remote tile by index.
Definition: dist_array.h:524
Eigen::Matrix< double, Eigen::Dynamic, 1 > EigenVectorXd
Definition: eigen.h:55
void tensor_to_eigen_submatrix(const T &tensor, Eigen::MatrixBase< Derived > &matrix)
Copy the content of a tensor into an Eigen matrix block.
Definition: eigen.h:274
Forward declarations.
Definition: dist_array.h:57
World & world() const
World accessor.
Definition: dist_array.h:1007
Eigen::Matrix< long, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > EigenMatrixXl
Definition: eigen.h:54
Eigen::Matrix< std::complex< double >, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > EigenMatrixXcd
Definition: eigen.h:47
bool is_zero(const Index &i) const
Check for zero tiles.
Definition: dist_array.h:1137
void counted_tensor_to_eigen_submatrix(const T &tensor, Eigen::MatrixBase< Derived > *matrix, madness::AtomicInt *counter)
Task function for assigning a tensor to an Eigen submatrix.
Definition: eigen.h:356
A row_major_buffer_to_array(World &world, const typename A::trange_type &trange, const typename A::value_type::value_type *buffer, const std::size_t m, const std::size_t n, const bool replicated=false, std::shared_ptr< typename A::pmap_interface > pmap={})
Convert a row-major matrix buffer into an Array object.
Definition: eigen.h:593
Eigen::Matrix< float, Eigen::Dynamic, 1 > EigenVectorXf
Definition: eigen.h:56
Eigen::Matrix< int, Eigen::Dynamic, 1 > EigenVectorXi
Definition: eigen.h:59
::Eigen::Matrix< T, ::Eigen::Dynamic, ::Eigen::Dynamic, Options > Matrix
Definition: blas.h:63
const std::shared_ptr< pmap_interface > & pmap() const
Process map accessor.
Definition: dist_array.h:1019
Eigen::Matrix< typename Tile::value_type, Eigen::Dynamic, Eigen::Dynamic, EigenStorageOrder > array_to_eigen(const DistArray< Tile, Policy > &array)
Convert an Array object into an Eigen matrix object.
Definition: eigen.h:496
An N-dimensional shallow copy wrapper for tile objects.
Definition: tile.h:82
void counted_eigen_submatrix_to_tensor(const Eigen::MatrixBase< Derived > *matrix, A *array, const typename A::ordinal_type i, madness::AtomicInt *counter)
Task function for converting Eigen submatrix to a tensor.
Definition: eigen.h:338