Решение разреженных линейных систем
В Eigen существует несколько методов для решения линейных систем, когда матрица коэффициентов разреженная. Из-за специального представления этого класса матриц необходимо проявлять особую внимательность, чтобы обеспечить хорошую производительность. Подробное введение в разреженные матрицы в Eigen можно найти в разделе Работа с разреженными матрицами. На этой странице перечислены доступные в Eigen разреженные решатели. Также представлены основные шаги, общие для всех этих линейных решателей. В зависимости от свойств матрицы, желаемой точности пользователь может настроить эти шаги, чтобы улучшить производительность своего кода. Обратите внимание, что глубокое понимание того, что скрывается за этими шагами, не требуется: в последнем разделе представлена процедура бенчмарка, которую можно легко использовать, чтобы получить представление о производительности всех доступных решателей.
Список разреженных решателей
Eigen в настоящее время предоставляет широкий набор встроенных решателей, а также обёртки для внешних библиотек решателей. Они обобщены в следующих таблицах:
Встроенные прямые решатели
| Класс | Тип решателя | Тип матрицы Matrix | Особенности, связанные с производительностью | Лицензия |
Примечания |
|---|---|---|---|---|---|
SimplicialLLT #include<Eigen/SparseCholesky>
|
Прямая факторизация LLt | SPD | Снижение заполнения | LGPL |
SimplicialLDLT зачастую предпочтительнее |
SimplicialLDLT #include<Eigen/SparseCholesky>
|
Прямая факторизация LDLt | SPD | Снижение заполнения | LGPL |
Рекомендуется для очень разреженных и не очень больших задач (например, уравнение Пуассона в 2D) |
SparseLU #include<Eigen/SparseLU> |
Факторизация LU | Квадратная | Снижение заполнения, использование быстрой алгебры плотных матриц | MPL2 |
Оптимизировано для небольших и больших задач с нерегулярными шаблонами |
SparseQR #include<Eigen/SparseQR> |
Факторизация QR | Любая, прямоугольная | Снижение заполнения | MPL2 | рекомендуется для задач наименьших квадратов, имеет базовый признак определения ранга |
Встроенные итеративные решатели
| Класс | Тип решателя | Тип матрицы Matrix | Поддерживаемые прекондиционеры, [по умолчанию] | Лицензия |
Примечания |
|---|---|---|---|---|---|
ConjugateGradient #include<Eigen/IterativeLinearSolvers> |
Классический итеративный CG | SPD | IdentityPreconditioner, [DiagonalPreconditioner], IncompleteCholesky | MPL2 |
Рекомендуется для больших симметричных задач (например, уравнение Пуассона в 3D) |
LeastSquaresConjugateGradient #include<Eigen/IterativeLinearSolvers>
|
CG для прямоугольной задачи наименьших квадратов | Прямоугольная | IdentityPreconditioner, [LeastSquareDiagonalPreconditioner] | MPL2 |
Solve для min |A'Ax-b|^2 без формирования A'A |
BiCGSTAB #include<Eigen/IterativeLinearSolvers>
|
Итеративный стабилизированный бисопряжённый градиент | Квадратная | IdentityPreconditioner, [DiagonalPreconditioner], IncompleteLUT | MPL2 | Для ускорения сходимости попробуйте использовать прекондиционер IncompleteLUT. |
Обёртки для внешних решателей
| Класс | Модуль | Тип решателя | Тип матрицы Matrix | Особенности, связанные с производительностью | Зависимости, лицензия |
Примечания |
|---|---|---|---|---|---|---|
|
PastixLLT PastixLDLT PastixLU |
PaStiXSupport | Прямые факторизации LLt, LDLt, LU | SPD SPD Квадратная |
Снижение заполнения, использование быстрой алгебры плотных матриц, многопоточность | Требуется пакет PaStiX, CeCILL-C | Оптимизировано для сложных задач и симметричных шаблонов |
| CholmodSupernodalLLT | CholmodSupport | Прямая факторизация LLt | SPD | Снижение заполнения, использование быстрой алгебры плотных матриц | Требуется пакет SuiteSparse, GPL | |
| UmfPackLU | UmfPackSupport | Прямая факторизация LU | Квадратная | Снижение заполнения, использование быстрой алгебры плотных матриц | Требуется пакет SuiteSparse, GPL | |
| KLU | KLUSupport | Прямая факторизация LU | Квадратная | Снижение заполнения, подходит для моделирования схем | Требуется пакет SuiteSparse, GPL | |
| SuperLU | SuperLUSupport | Прямая факторизация LU | Квадратная | Снижение заполнения, использование быстрой алгебры плотных матриц | Требуется библиотека SuperLU, (BSD-подобная) | |
| SPQR | SPQRSupport | Факторизация QR | Любая, прямоугольная | снижение заполнения, многопоточность, быстрая алгебра плотных матриц | требуется пакет SuiteSparse, GPL | рекомендуется для задач линейных наименьших квадратов, имеет функцию определения ранга |
|
PardisoLLT PardisoLDLT PardisoLU |
PardisoSupport | Прямые факторизации LLt, LDLt, LU | SPD SPD Квадратная |
Снижение заполнения, использование быстрой алгебры плотных матриц, многопоточность | Требуется пакет Intel MKL, Проприетарная | Оптимизировано для сложных задач, шаблонов, см. также использование MKL с Eigen |
Здесь SPD означает симметрично-положительно определённую.
Концепция разреженного решателя
Все эти решатели следуют одной общей концепции. Вот типичный и общий пример:
#include <Eigen/RequiredModuleName> // ... SparseMatrix<double> A; // fill A VectorXd b, x; // fill b // solve Ax = b SolverClassName<SparseMatrix<double> > solver; solver.compute(A); if(solver.info()!=Success) { // decomposition failed return; } x = solver.solve(b); if(solver.info()!=Success) { // solving failed return; } // solve for another right hand side: x1 = solver.solve(b1);
Для SPD решателей второй необязательный шаблонный аргумент позволяет указать, какая треугольная часть должна быть использована, например:
#include <Eigen/IterativeLinearSolvers> ConjugateGradient<SparseMatrix<double>, Eigen::Upper> solver; x = solver.compute(A).solve(b);
В приведенном выше примере для решения учитывается только верхняя треугольная часть входной матрицы A. Противоположная треугольная часть может быть пустой или содержать произвольные значения.
В случае, когда необходимо решить несколько задач с одинаковым шаблоном разреженности, шаг "вычисления" можно разложить следующим образом:
SolverClassName<SparseMatrix<double> > solver; solver.analyzePattern(A); // for this step the numerical values of A are not used solver.factorize(A); x1 = solver.solve(b1); x2 = solver.solve(b2); ... A = ...; // modify the values of the nonzeros of A, the nonzeros pattern must stay unchanged solver.factorize(A); x1 = solver.solve(b1); x2 = solver.solve(b2); ...
Метод compute() эквивалентен вызову как analyzePattern(), так и factorize().
Каждый решатель предоставляет некоторые специфические функции, такие как вычисление определителя, доступ к факторам, контроль итераций и так далее. Более подробная информация доступна в документации соответствующих классов.
Наконец, большинство итеративных решателей также могут быть использованы в контексте без матрицы, см. пример по этой ссылке .
Шаг вычисления
В функции compute() матрица обычно разлагается: LLT для самосопряжённых матриц, LDLT для общих эрмитовых матриц, LU для неэрмитовых матриц и QR для прямоугольных матриц. Это результаты использования прямых решателей. Для этого класса решателей этап compute дополнительно подразделяется на analyzePattern() и factorize().
Цель analyzePattern() — переупорядочить ненулевые элементы матрицы таким образом, чтобы этап факторизации создавал меньше заполнения. Этот этап использует только структуру матрицы. Следовательно, результаты этого этапа могут быть использованы для других систем линейных уравнений, где матрица имеет ту же структуру. Однако обратите внимание, что иногда некоторые внешние решатели (например, SuperLU) требуют, чтобы значения матрицы были заданы на этом этапе, например, для выравнивания строк и столбцов матрицы. В этой ситуации результаты этого этапа не должны использоваться с другими матрицами.
Eigen предоставляет ограниченный набор методов для переупорядочения матрицы на этом этапе, либо встроенные (COLAMD, AMD), либо внешние (METIS). Эти методы задаются в списке параметров шаблона решателя:
DirectSolverClassName<SparseMatrix<double>, OrderingMethod<IndexType> > solver;
См. модуль OrderingMethods для списка доступных методов и связанных опций.
В factorize() вычисляются факторы матрицы коэффициентов. Этот этап должен вызываться каждый раз, когда значения матрицы меняются. Однако структурная структура матрицы не должна меняться между несколькими вызовами.
Для итеративных решателей этап compute используется для создания предварительного условия. Например, с предварительным условием ILUT неполные факторы L и U вычисляются на этом этапе. Помните, что основная цель предварительного условия — ускорить сходимость итерационного метода путём решения модифицированной системы линейных уравнений, где матрица коэффициентов имеет более сгруппированные собственные значения. Для реальных задач итерационный решатель всегда должен использоваться с предварительным условием. В Eigen предварительное условие выбирается просто добавлением его как параметра шаблона к объекту итерационного решателя.
IterativeSolverClassName<SparseMatrix<double>, PreconditionerName<SparseMatrix<double> > solver;
Член-функция preconditioner() возвращает ссылку на предварительное условие для прямого взаимодействия с ним. См. модуль итеративных решателей и документацию каждого класса для списка доступных методов.
Этап решения
Функция solve() вычисляет решение систем линейных уравнений с одним или несколькими правыми частями.
X = solver.solve(B);
Здесь B может быть вектором или матрицей, где столбцы образуют различные правые части. Функция solve() также может вызываться несколько раз, например, когда все правые части недоступны сразу.
x1 = solver.solve(b1); // Get the second right hand side b2 x2 = solver.solve(b2); // ...
Для прямых методов решение вычисляется с точностью до машинной. Иногда решение не должно быть слишком точным. В этом случае итерационные методы более подходят, и желаемая точность может быть установлена перед шагом решения с помощью setTolerance(). Для всех доступных функций см. документацию модуля итеративных решателей.
Процедура тестирования производительности
Большую часть времени всё, что вам нужно знать, это сколько времени займёт решение вашей системы и, надеюсь, какой решатель является наиболее подходящим. В Eigen мы предоставляем процедуру тестирования производительности, которую можно использовать для этой цели. Её очень легко использовать. В каталоге сборки перейдите в bench/spbench и скомпилируйте процедуру, набрав make spbenchsolver. Запустите её с опцией –help, чтобы получить список всех доступных опций. В принципе, тестируемые матрицы должны быть в формате MatrixMarket Coordinate, а процедура возвращает статистику от всех доступных решателей в Eigen.
Для экспорта ваших матриц и векторов правой части в формате MatrixMarket можно использовать неподдерживаемый модуль SparseExtra:
#include <unsupported/Eigen/SparseExtra> ... Eigen::saveMarket(A, "filename.mtx"); Eigen::saveMarket(A, "filename_SPD.mtx", Eigen::Symmetric); // if A is symmetric-positive-definite Eigen::saveMarketVector(B, "filename_b.mtx");
Следующая таблица даёт пример XML-статистики нескольких встроенных и внешних решателей Eigen.
| Matrix | N | NNZ | UMFPACK | SUPERLU | PASTIX LU | BiCGSTAB | BiCGSTAB+ILUT | GMRES+ILUT | LDLT | CHOLMOD LDLT | PASTIX LDLT | LLT | CHOLMOD SP LLT | CHOLMOD LLT | PASTIX LLT | CG | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| vector_graphics | 12855 | 72069 | Время вычисления | 0.0254549 | 0.0215677 | 0.0701827 | 0.000153388 | 0.0140107 | 0.0153709 | 0.0101601 | 0.00930502 | 0.0649689 | |||||
| Solve Time | 0.00337835 | 0.000951826 | 0.00484373 | 0.0374886 | 0.0046445 | 0.00847754 | 0.000541813 | 0.000293696 | 0.00485376 | ||||||||
| Общее время | 0.0288333 | 0.0225195 | 0.0750265 | 0.037642 | 0.0186552 | 0.0238484 | 0.0107019 | 0.00959871 | 0.0698227 | ||||||||
| Ошибка(Итерации) | 1.299e-16 | 2.04207e-16 | 4.83393e-15 | 3.94856e-11 (80) | 1.03861e-12 (3) | 5.81088e-14 (6) | 1.97578e-16 | 1.83927e-16 | 4.24115e-15 | ||||||||
| poisson_SPD | 19788 | 308232 | Время вычисления | 0.425026 | 1.82378 | 0.617367 | 0.000478921 | 1.34001 | 1.33471 | 0.796419 | 0.857573 | 0.473007 | 0.814826 | 0.184719 | 0.861555 | 0.470559 | 0.000458188 |
| Solve Time | 0.0280053 | 0.0194402 | 0.0268747 | 0.249437 | 0.0548444 | 0.0926991 | 0.00850204 | 0.0053171 | 0.0258932 | 0.00874603 | 0.00578155 | 0.00530361 | 0.0248942 | 0.239093 | |||
| Общее время | 0.453031 | 1.84322 | 0.644241 | 0.249916 | 1.39486 | 1.42741 | 0.804921 | 0.862891 | 0.4989 | 0.823572 | 0.190501 | 0.866859 | 0.495453 | 0.239551 | |||
| Ошибка(Итерации) | 4.67146e-16 | 1.068e-15 | 1.3397e-15 | 6.29233e-11 (201) | 3.68527e-11 (6) | 3.3168e-15 (16) | 1.86376e-15 | 1.31518e-16 | 1.42593e-15 | 3.45361e-15 | 3.14575e-16 | 2.21723e-15 | 7.21058e-16 | 9.06435e-12 (261) | |||
| sherman2 | 1080 | 23094 | Время вычисления | 0.00631754 | 0.015052 | 0.0247514 | - | 0.0214425 | 0.0217988 | ||||||||
| Solve Time | 0.000478424 | 0.000337998 | 0.0010291 | - | 0.00243152 | 0.00246152 | |||||||||||
| Общее время | 0.00679597 | 0.01539 | 0.0257805 | - | 0.023874 | 0.0242603 | |||||||||||
| Ошибка(Итерации) | 1.83099e-15 | 8.19351e-15 | 2.625e-14 | 1.3678e+69 (1080) | 4.1911e-12 (7) | 5.0299e-13 (12) | |||||||||||
| bcsstk01_SPD | 48 | 400 | Время вычисления | 0.000169079 | 0.00010789 | 0.000572538 | 1.425e-06 | 9.1612e-05 | 8.3985e-05 | 5.6489e-05 | 7.0913e-05 | 0.000468251 | 5.7389e-05 | 8.0212e-05 | 5.8394e-05 | 0.000463017 | 1.333e-06 |
| Solve Time | 1.2288e-05 | 1.1124e-05 | 0.000286387 | 8.5896e-05 | 1.6381e-05 | 1.6984e-05 | 3.095e-06 | 4.115e-06 | 0.000325438 | 3.504e-06 | 7.369e-06 | 3.454e-06 | 0.000294095 | 6.0516e-05 | |||
| Общее время | 0.000181367 | 0.000119014 | 0.000858925 | 8.7321e-05 | 0.000107993 | 0.000100969 | 5.9584e-05 | 7.5028e-05 | 0.000793689 | 6.0893e-05 | 8.7581e-05 | 6.1848e-05 | 0.000757112 | 6.1849e-05 | |||
| Ошибка(Итерации) | 1.03474e-16 | 2.23046e-16 | 2.01273e-16 | 4.87455e-07 (48) | 1.03553e-16 (2) | 3.55965e-16 (2) | 2.48189e-16 | 1.88808e-16 | 1.97976e-16 | 2.37248e-16 | 1.82701e-16 | 2.71474e-16 | 2.11322e-16 | 3.547e-09 (48) | |||
| sherman1 | 1000 | 3750 | Время вычисления | 0.00228805 | 0.00209231 | 0.00528268 | 9.846e-06 | 0.00163522 | 0.00162155 | 0.000789259 | 0.000804495 | 0.00438269 | |||||
| Solve Time | 0.000213788 | 9.7983e-05 | 0.000938831 | 0.00629835 | 0.000361764 | 0.00078794 | 4.3989e-05 | 2.5331e-05 | 0.000917166 | ||||||||
| Общее время | 0.00250184 | 0.00219029 | 0.00622151 | 0.0063082 | 0.00199698 | 0.00240949 | 0.000833248 | 0.000829826 | 0.00529986 | ||||||||
| Ошибка(Итерации) | 1.16839e-16 | 2.25968e-16 | 2.59116e-16 | 3.76779e-11 (248) | 4.13343e-11 (4) | 2.22347e-14 (10) | 2.05861e-16 | 1.83555e-16 | 1.02917e-15 | ||||||||
| young1c | 841 | 4089 | Время вычисления | 0.00235843 | 0.00217228 | 0.00568075 | 1.2735e-05 | 0.00264866 | 0.00258236 | ||||||||
| Solve Time | 0.000329599 | 0.000168634 | 0.00080118 | 0.0534738 | 0.00187193 | 0.00450211 | |||||||||||
| Общее время | 0.00268803 | 0.00234091 | 0.00648193 | 0.0534865 | 0.00452059 | 0.00708447 | |||||||||||
| Ошибка(Итерации) | 1.27029e-16 | 2.81321e-16 | 5.0492e-15 | 8.0507e-11 (706) | 3.00447e-12 (8) | 1.46532e-12 (16) | |||||||||||
| mhd1280b | 1280 | 22778 | Время вычисления | 0.00234898 | 0.00207079 | 0.00570918 | 2.5976e-05 | 0.00302563 | 0.00298036 | 0.00144525 | 0.000919922 | 0.00426444 | |||||
| Solve Time | 0.00103392 | 0.000211911 | 0.00105 | 0.0110432 | 0.000628287 | 0.00392089 | 0.000138303 | 6.2446e-05 | 0.00097564 | ||||||||
| Общее время | 0.0033829 | 0.0022827 | 0.00675918 | 0.0110692 | 0.00365392 | 0.00690124 | 0.00158355 | 0.000982368 | 0.00524008 | ||||||||
| Ошибка(Итерации) | 1.32953e-16 | 3.08646e-16 | 6.734e-16 | 8.83132e-11 (40) | 1.51153e-16 (1) | 6.08556e-16 (8) | 1.89264e-16 | 1.97477e-16 | 6.68126e-09 | ||||||||
| crashbasis | 160000 | 1750416 | Время вычисления | 3.2019 | 5.7892 | 15.7573 | 0.00383515 | 3.1006 | 3.09921 | ||||||||
| Solve Time | 0.261915 | 0.106225 | 0.402141 | 1.49089 | 0.24888 | 0.443673 | |||||||||||
| Общее время | 3.46381 | 5.89542 | 16.1594 | 1.49473 | 3.34948 | 3.54288 | |||||||||||
| Ошибка(Итерации) | 1.76348e-16 | 4.58395e-16 | 1.67982e-14 | 8.64144e-11 (61) | 8.5996e-12 (2) |
6.04042e-14 (5) |
© Eigen.
Licensed under the MPL2 License.
https://eigen.tuxfamily.org/dox/group__TopicSparseSystems.html