Построение SparseMatrix в Eigen

Я строю разреженную линейную систему с несколькими (мягкими) ограничениями. Я конвертирую код, который использовался для построения матрицы с помощью boost :: ublas, в Eigen. Boost: ublas имеет удобный способ создания разреженной матрицы с известным (или предполагаемым) количеством ненулевых значений и имеет достаточно быстрый оператор (int row, int col) для обновления ее элементов.

Проблема в следующем:

  • Использование SparseMatrix :: setFromTriplets:
    Моя система имеет много ограничений. В качестве наивного, «слегка» преувеличенного примера допустим, что у меня есть разреженная матрица 100x100 с 500 nnz, но с 1 миллиардом избыточных ограничений (т.е. ненулевые коэффициенты изменены миллиард раз). setFromTriplets требует, чтобы я сохранил 1 миллиард коэффициентов, большинство из которых будут суммированы, чтобы сформировать мой набор из 500 ненулевых коэффициентов. Это не очень эффективно и не очень удобно для памяти. Конечно, я могу заменить свой std :: vector на std :: map и выполнить накопление ограничений вручную, но при этом каким-то образом упускается смысл наличия разреженного класса матриц, и это тоже было бы неэффективно.

  • Использование SparseMatrix :: insert (i, j, val):
    Не работает, если элемент уже присутствует. Моя проблема в том, чтобы иметь возможность накапливать коэффициенты, которые уже есть.

  • using SparseMatrix :: coeffRef (i, j):
    Это работает, и это будет функция, которую я ищу. Однако он на несколько порядков медленнее, чем boost :: ublas. Я удивлен, что не нашел для этого лучшей функции. Я думал, что это происходит из-за того, что количество ненулевых символов заранее не известно и приводит к многократным перераспределениям (что и происходит на практике). Однако использование SparseMatrix :: reserve () не имеет никакого эффекта, поскольку это функция, которая работает только для сжатых матриц (комментарий в источнике говорит: «Эта функция не имеет смысла в несжатом режиме» перед утверждением). .. и, как говорится в документации, «вставка нового элемента в SparseMatrix преобразует его позже в несжатый режим».

Каков наиболее эффективный способ построить разреженную матрицу в Eigen, сохранив при этом возможность обновлять ее коэффициенты несколько раз?

Спасибо

[РЕДАКТИРОВАТЬ: пример использования: матрица 10x10 с 10 ненулевыми значениями. Для простоты матрица диагональная]

SparseMatrix<double> mat(10, 10);
for (int i=0; i<10; i++) {
  for (int j=0; j<1000000; j++) {
    mat.coeffRef(i, i) += rand()%10;
  }
}

=> работает, но на порядки медленнее, чем оператор ublas () (для больших матриц и более реалистичных настроек, конечно).

std::vector<Eigen::Triplet<double> > triplets(10000000);
int k=0;
for (int i=0; i<10; i++) {
  for (int j=0; j<1000000; j++) {
    triplets[k++] = Eigen::Triplet<double>(i,i,rand()%10);
  }
}
SparseMatrix<double> mat(10, 10);
mat.setFromTriplets(triplets.begin(), triplets.end());

=> не дружественный к памяти ...


person nbonneel    schedule 09.08.2013    source источник
comment
Я не совсем понимаю вашу проблему. Вы бы опубликовали упрощенную версию своего варианта использования? В идеале, сделайте это с разреженной матрицей 10x10 с, скажем, 10 ненулевыми.   -  person Escualo    schedule 09.08.2013
comment
Я просто добавил банальный пример.   -  person nbonneel    schedule 09.08.2013
comment
ваш пример не несет никакого смысла. Я имел в виду свой вопрос как ваш реальный вариант использования. То есть: у вас есть разреженная матрица, а потом вы что-то с ней делаете, а потом что? ты меняешь матрицу? Вы перетасовываете коэффициенты? Вы добавляете новый набор коэффициентов?   -  person Escualo    schedule 09.08.2013
comment
Мой фактический вариант использования относится к нелокальным ограничениям при внутренней декомпозиции изображения, и я не уверен, что это очень актуально для вопроса. Получив матрицу, я решаю линейную систему с заданной правой частью, но точно так же я не уверен, почему это могло бы помочь. Моя проблема действительно заключается в том, чтобы иметь возможность выполнять операции, которые очень похожи на описанные выше (за исключением того, что матрица не диагональная и намного больше, и что коэффициенты не случайны)   -  person nbonneel    schedule 09.08.2013
comment
Вы пробовали с mat.coeffRef(i,j) += v_ij;? (см. eigen.tuxfamily.org/dox-devel/)   -  person Escualo    schedule 09.08.2013
comment
это тот, который я несколько раз упоминал в своем вопросе, и он выглядит на порядки медленнее, чем ublas :: operator () (int i, int j), что связано с перераспределением памяти матрицы.   -  person nbonneel    schedule 09.08.2013
comment
Мое единственное предложение - создать свой собственный конструктор триплетов. То есть: класс, который делает именно то, что вам нужно, и дает набор триплетов, которые можно использовать для инициализации любого класса SparseMatrix (либо ublas, либо Eigen). Я считаю, что такой класс может помочь вам уменьшить ваши зависимости от базовой реализации разреженной матрицы, и вы можете настроить его в соответствии со своими потребностями независимо от библиотек, на которые вы полагаетесь. Удачи.   -  person Escualo    schedule 10.08.2013
comment
спасибо за предложение. Но выполнение этого эффективно в конечном итоге сводится к созданию нового класса SparseMatrix, чтобы иметь возможность инициализировать Eigen :: SparseMatrix. На самом деле это то, что делает мой текущий код: он использует ublas как удобный способ создания матрицы, а затем преобразует его в Eigen :: SparseMatrix. Я хотел избавиться от этого причудливого шага ... Спасибо за вашу помощь!   -  person nbonneel    schedule 10.08.2013


Ответы (1)


Чтобы сделать insert из coeffRef эффективным, вам необходимо зарезервировать достаточно места, используя mat.reserve(nnz), где nnz - это Eigen::VectorXi, содержащее оценочное количество ненулевых значений каждого столбца. Эти цифры лучше немного завышать, чтобы избежать многочисленных перераспределений / копий. Еще один дополнительный прием - убедиться, что при первом доступе к элементу (i,j) этот элемент является последним в столбце j.

Если вы можете легко вычислить шаблон разреженности, то альтернативой является заполнение вектора уникального триплета 0 для значений, и тогда coeffRef будет быстрым.

person ggael    schedule 10.08.2013
comment
Благодарность! Документ был не совсем ясным: я думал, что coeffRef сделал матрицу несжатой, вставка нового элемента в SparseMatrix преобразует это позже в несжатый режим, а функция резервирования заявляет, что это не имеет смысла для несжатых матриц (что я не конечно, почему). Спасибо! - person nbonneel; 12.08.2013
comment
есть две резервные сигнатуры: одна принимает size_t для режима со сжатием, а другая принимает векторное выражение для режима без сжатия. - person ggael; 13.08.2013