Выделение и индексирование
Эта страница демонстрирует многочисленные возможности, предоставляемые operator() для индексирования подмножества строк и столбцов. Этот API был представлен в Eigen 3.4. Он поддерживает все функции, предлагаемые блочным API block API, и многое другое. В частности, он поддерживает выделение, которое состоит в взятии набора строк, столбцов или элементов, равномерно распределенных в матрице или индексированных из массива индексов.
Обзор
Все перечисленные выше операции обрабатываются с помощью универсального метода DenseBase::operator()(const RowIndices&, const ColIndices&). Каждый аргумент может быть:
- Целое число, индексирующее одну строку или столбец, включая символические индексы.
- Символ Eigen::all, представляющий весь набор соответствующих строк или столбцов в порядке возрастания.
- ArithmeticSequence, как построена функциями Eigen::seq, Eigen::seqN или Eigen::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) |
представляет последовательность из size целых чисел, начиная с firstIdx |
seqN(2,5) <=> {2,3,4,5,6} |
seqN(firstIdx,size,incr) |
то же самое, но используя приращение incr для перехода от одного индекса к следующему |
seqN(2,3,3) <=> {2,5,8} |
Параметры firstIdx и lastIdx также могут быть определены с помощью символа Eigen::last, представляющего индекс последней строки, столбца или элемента базовой матрицы/вектора после передачи арифметической последовательности в нее через оператор(). Вот несколько примеров для двумерного массива/матрицы A и одномерного массива/вектора v.
| Намерение | Код | Эквивалент блочного API |
|---|---|---|
Нижний левый угол, начиная со строки i с n столбцами |
A(seq(i,last), seqN(0,n)) |
A.bottomLeftCorner(A.rows()-i,n) |
Блок, начиная с i,j с m строками и n столбцами |
A(seqN(i,m), seqN(i,n) |
A.block(i,j,m,n) |
Блок, начиная с i0,j0 и заканчивая i1,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)) |
|
Первые n нечетные строки A |
A(seqN(1,n,2), all) |
|
| Последний столбец после первого |
A(all, last-1) |
A.col(A.cols()-2) |
| Средняя строка |
A(last/2,all) |
A.row((A.rows()-1)/2) |
| Последние элементы v, начиная с i |
v(seq(i,last)) |
v.tail(v.size()-i) |
Последние n элементов v |
v(seq(last+1-n,last)) |
v.tail(n) |
Как видно из последнего примера, ссылка на последние n элементов (или строк/столбцов) немного громоздка для записи. Это становится еще более сложным и подверженным ошибкам при использовании отличного от стандартного приращения. Здесь появляются Eigen::lastN(size) и Eigen::lastN(size,incr):
| Намерение | Код | Эквивалент блочного API |
|---|---|---|
Последние n элементов v |
v(lastN(n)) |
v.tail(n) |
Правый нижний угол A размером m на n |
v(lastN(m), lastN(n)) |
A.bottomRightCorner(m,n) |
Правый нижний угол A размером m на n |
v(lastN(m), lastN(n)) |
A.bottomRightCorner(m,n) |
Последние n столбцов, беря по 1 столбцу из 3 |
A(all, lastN(n,3)) |
Размер и приращение на этапе компиляции
С точки зрения производительности 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>))
Обратный порядок
Индексы строк/столбцов также могут быть перечислены в порядке убывания с использованием отрицательного приращения. Например, каждый второй столбец A от столбца 20 до 10:
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(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 |
Вы также можете напрямую передать статический массив:
| Пример: | Результат: |
|---|---|
#if EIGEN_HAS_STATIC_ARRAY_TEMPLATE MatrixXi A = MatrixXi::Random(4,6); cout << "Initial matrix A:\n" << A << "\n\n"; cout << "A(all,{4,2,5,5,3}):\n" << A(all,{4,2,5,5,3}) << "\n\n"; #endif |
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(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() может принимать в качестве входных данных любой объект ind типа T совместимый с:
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
|
© Eigen.
Licensed under the MPL2 License.
https://eigen.tuxfamily.org/dox/group__TutorialSlicingIndexing.html