Алгоритм Ланцоша для арифметики с плавающей точкой с выборочной ортогонализацией: различия между версиями

Материал из Алговики
Перейти к навигации Перейти к поиску
[непроверенная версия][непроверенная версия]
Строка 1: Строка 1:
{{algorithm
+
Авторы: Абдулпотиев А.А.
| name              = Разложение Холецкого
 
| serial_complexity = <math>O(n^3)</math>
 
| pf_height        = <math>O(n)</math>
 
| pf_width          = <math>O(n^2)</math>
 
| input_data        = <math>\frac{n (n + 1)}{2}</math>
 
| output_data      = <math>\frac{n (n + 1)}{2}</math>
 
}}
 
  
Автор: [[Участник:Abdulpotiev|А.А. Абдулпотиев]]
 
 
== Свойства и структура алгоритма ==
 
== Свойства и структура алгоритма ==
 +
===Общее описание алгоритма===
 +
Алгоритм Лáнцоша является мощным инструментом для решения некоторого класса больших разреженных симметричных спектральных задач <math> Ax = \Lambda x</math>. Однако любая практическая реализация этого алгоритма страдает от ошибок округления, т.к. векторы Ланцоша теряют взаимную ортогональность. Для того чтобы поддерживать некоторый уровень ортогональности, появились методы полной переортогонализации и выборочной ортогонализации. В этой работе мы рассмотрим последний метод в качестве способа для поддержания ортогональности среди векторов Ланцоша. Он обладает почти столь же высокой точностью, как алгоритм с полной переортогонализацией, и почти столь же низкой стоимостью, как алгоритм без ортогонализации <ref>"The Lanczos Algorithm With Partial Reorthogonalization", Horst D. Simon, Mathematics of computation, volume 42, number 165, pages 115-142  (1984)</ref>.
  
=== Общее описание алгоритма ===
 
  
'''Алгоритм Ланцоша''' впервые предложено французским офицером и математиком Андре-Луи Холецким в конце Первой Мировой войны, незадолго до его гибели в бою в августе 1918 г. Идея этого разложения была опубликована в 1924 г. его сослуживцем<ref>Commandant Benoit, Note sur une méthode de résolution des équations normales provenant de l'application de la méthode des moindres carrés à un système d'équations linéaires en nombre inférieur à celui des inconnues (Procédé du Commandant Cholesky), Bulletin Géodésique 2 (1924), 67-77.</ref>. Потом оно было использовано поляком Т. Банашевичем<ref>Banachiewicz T. Principles d'une nouvelle technique de la méthode des moindres carrês. Bull. Intern. Acad. Polon. Sci. A., 1938,  134-135.</ref><ref>Banachiewicz T. Méthode de résoltution numérique des équations linéaires, du calcul des déterminants et des inverses et de réduction des formes quardatiques. Bull. Intern. Acad. Polon. Sci. A., 1938, 393-401.</ref> в 1938 г. В советской математической литературе называется также методом квадратного корня<ref>Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.</ref><ref>Воеводин В.В., Кузнецов Ю.А. Матрицы и вычисления. М.: Наука, 1984.</ref><ref>Фаддеев Д.К., Фаддева В.Н. Вычислительные основы линейной алгебры. М.-Л.: Физматгиз, 1963.</ref>; название связано с характерными операциями, отсутствующими в родственном разложении Гаусса.  
+
Дается вещественная симметричная матрица <math>A = A^T</math>,
 +
{{Шаблон:ASymmetric}} <math> \, \;  (1), </math>
 +
случайный вектор <math>b </math>, являющийся первым приближением собственного вектора матрицы, <math>k </math> - количество собственных значений и собственных векторов, которые мы хотим найти, т.е. количество итераций.
  
Первоначально разложение Холецкого использовалось исключительно для плотных симметричных положительно определенных матриц. В настоящее время его использование гораздо шире. Оно может быть применено также, например, к эрмитовым матрицам. Для  повышения производительности вычислений часто применяется блочная версия разложения.
 
  
Для разреженных матриц разложение Холецкого также широко применяется в качестве основного этапа прямого метода решения линейных систем. В этом случае используют специальные упорядочивания для уменьшения ширины профиля исключения, а следовательно и уменьшения количества арифметических операций. Другие упорядочивания используются для выделения независимых блоков вычислений при работе на системах с параллельной организацией.
+
На каждой итерации строится матрица <math>Q_j = [q_1, q_2, \dots, q_j]</math> размерности <math>n \times j</math>, состоящая из ортонормированных векторов Ланцоша <math>z</math>. В качестве приближенных собственных значений берутся числа Ритца <math>\theta_i </math>, т.е. собственные значения симметричной трехдиагональной матрицы <math>T_j = Q^T_j A Q_j</math> размерности <math>j \times j</math>.
  
Варианты разложения Холецкого нашли успешные применения и в итерационных методах для построения переобусловливателей разреженных симметричных положительно определенных матриц. В неполном треугольном разложении («по позициям») элементы переобусловливателя вычисляются только в заранее заданных позициях, например, в позициях ненулевых элементов исходной матрицы (так называемое разложение IC0). Для получения же более точного разложения применяется приближение, в котором фильтрация малых элементов производится «по значениям». В зависимости от используемого порога фильтрации можно получить более точное, но и более заполненное разложение. Существует и алгоритм разложения второго порядка точности<ref>Kaporin I.E. High quality preconditioning of a general symmetric positive definite matrix based on its UTU + UTR + RTU-decomposition. Numer. Lin. Algebra Appl. (1998) Vol. 5, No. 6, 483-509.</ref>. В нём при таком же заполнении множителей разложения удается улучшить точность. Для такого разложения в параллельном режиме используется специальный вариант аддитивного переобуславливания на основе разложения второго порядка<ref>Капорин И.Е., Коньшин И.Н. Параллельное решение симметричных положительно-определенных систем на основе перекрывающегося разбиения на блоки. Ж. вычисл. матем. и матем. физ.,  2001, Т, 41, N. 4, C. 515–528.</ref>.
+
:<math>
 
+
T_j = \begin{pmatrix}
На этой странице представлено исходное разложение Холецкого с новых позиций нашего суперкомпьютерного века. Приведено описание конкретной версии разложения Холецкого — для плотных вещественных симметричных положительно определённых матриц, но структура для ряда других версий, например, для комплексного случая, почти такая же, различия состоят в замене большинства вещественных операций на комплексные. Список остальных основных вариантов разложения можно посмотреть на странице [[Метод Холецкого (нахождение симметричного треугольного разложения)]].
+
\alpha_1 & \beta_1 \\
 
+
\beta_1 & \alpha_2 & \beta_2 \\
Используется для разложения положительно определённых эрмитовых (''в вещественном случае - симметрических'') матриц в виде <math>A = L L^*</math>,  <math>L</math> — нижняя треугольная матрица,
+
& \beta_2 & \ddots & \ddots \\
{{Шаблон:LCommon}}
+
& & \ddots & \ddots & \beta_{j-1} \\
или в виде <math>A = U^* U</math>, <math>U</math> — верхняя треугольная матрица,
+
& & & \beta_{j-1} & \alpha_j
{{Шаблон:UCommon}}
+
\end{pmatrix}\; (2).
Эти разложения совершенно эквивалентны друг другу по вычислениям и различаются только способом представления данных). Он заключается в реализации формул для элементов матрицы <math>L</math>, получающихся из вышеприведённого равенства единственным образом. Получило широкое распространение благодаря следующим особенностям.
+
</math>
  
==== Симметричность и положительная определённость матрицы ====
+
Однако, векторы <math>q_j </math> теряют ортогональность вследствие приобретения больших компонент в направлениях векторов Ритца <math>y_{i,j} = Q_j v_i </math>, отвечающих сошедшимся числам Ритца <math> \theta_i </math>. Поэтому чтобы построить <math>q_j </math>, предлагается на каждом шаге следить за оценками погрешностей  <math>\beta_{t}|v_i(t)|, i = 1 \dots t, t = j - 1 </math>, где <math>v_i(t) </math> - <math>t</math>-я компонента собственного вектора <math>v_i </math>. И когда какая-то оценка становится слишком малой, проводить ортогонализацию вектора Ланцоша <math>z </math>. Величина <math>\beta_{t}|v_i(t)| </math> считается малой, если она меньше, чем <math>\sqrt{\varepsilon}||T_{t}|| </math>, где <math>\varepsilon</math> - доступная машинная точность чисел.
  
Симметричность матрицы позволяет хранить и вычислять только чуть больше половины её элементов, что почти вдвое экономит как необходимые для вычислений объёмы памяти, так и количество операций в сравнении с, например, разложением по методу Гаусса. При этом альтернативное (без вычисления квадратных корней) LU-разложение, использующее симметрию матрицы, всё же несколько быстрее метода Холецкого (не использует извлечение квадратных корней), но требует хранения всей матрицы.
 
Благодаря тому, что разлагаемая матрица не только симметрична, но и положительно определена, её LU-разложения, в том числе и разложение методом Холецкого, имеют наименьшее ''[[Глоссарий#Эквивалентное возмущение|эквивалентное возмущение]]'' из всех известных разложений матриц.
 
 
==== Режим накопления ====
 
 
Алгоритм позволяет использовать так называемый ''режим накопления'', обусловленный тем, что значительную часть вычислений составляют ''вычисления скалярных произведений''.
 
  
 +
После следует вычисление собственных значений  <math> \theta_j </math> и собственных векторов <math>v_j </math> полученной трехдиагональной матрицы <math>T_j</math>, для чего существует, например, метод "разделяй и властвуй"<ref>Дж. Деммель «Вычислительная линейная алгебра», c. 232, алгоритм 5.2</ref>
 
=== Математическое описание алгоритма ===
 
=== Математическое описание алгоритма ===
 
+
<math> \beta_0=0,q_0=0</math>
Исходные данные: положительно определённая симметрическая матрица <math>A</math> (элементы <math>a_{ij}</math>).
+
<math> q_{1} = \frac{b_{j}}{\|b\|_2}</math>, где <math> \|b\|_2 = \sqrt{\sum\limits_{j=1}^{n} b_j^2}</math>
 
+
<math> for\, j=1\,\, to\, \, k\, \, do:</math>
Вычисляемые данные: нижняя треугольная матрица <math>L</math> (элементы <math>l_{ij}</math>).
+
    <math>z=Aq_j,  </math>
 
+
    <math>\alpha_j=q_j^Tz, </math>
Формулы метода:
+
    <math>z=z-\alpha_jq_j-\beta_{j-1}q_{j-1},  </math>
:<math>
+
    <math>t = j - 1,  </math>
\begin{align}
+
    <math>for\, i=1\,\, to\, \, t\, \, do: </math>
l_{11} & = \sqrt{a_{11}}, \\
+
        <math>if\\beta_{t}|v_i(t)| \leqslant \sqrt{\varepsilon}\|T_{t}\| \, \, then:</math>
l_{j1} & = \frac{a_{j1}}{l_{11}}, \quad j \in [2, n], \\
+
            <math>z = z-(y^T_{i,t},z)y_{i,t} </math>, где <math>y_{i,t} = Q_{t}v_i</math>
l_{ii} & = \sqrt{a_{ii} - \sum_{p = 1}^{i - 1} l_{ip}^2}, \quad i \in [2, n], \\
+
    <math>\beta_{j}=\|z\|_2 </math>
l_{ji} & = \left (a_{ji} - \sum_{p = 1}^{i - 1} l_{ip} l_{jp} \right ) / l_{ii}, \quad i \in [2, n - 1], j \in [i + 1, n].
+
    <math>q_{j+1}=z/\beta_{j}, </math>
\end{align}
+
    Строим матрицу  <math> T_j</math> (2), вычисляем собственные значения  <math> \theta_j </math> и собственные векторы <math>v_j </math> полученной матрицы <math>T_j</math>.
</math>
 
 
 
Существует также блочная версия метода, однако в данном описании разобран только точечный метод.
 
 
 
В ряде реализаций деление на диагональный элемент выполняется в два этапа: вычисление <math>\frac{1}{l_{ii}}</math> и затем умножение на него всех (видоизменённых) <math>a_{ji}</math> . Здесь мы этот вариант алгоритма не рассматриваем. Заметим только, что он имеет худшие параллельные характеристики, чем представленный.
 
  
 
=== Вычислительное ядро алгоритма ===
 
=== Вычислительное ядро алгоритма ===
 +
Выделены следующие вычислительные ядра:
  
Вычислительное ядро последовательной версии метода Холецкого можно составить из множественных (всего их <math>\frac{n (n - 1)}{2}</math>) вычислений скалярных произведений строк матрицы:
+
* Умножение матрицы на вектор, <math>z=Aq_j,  </math>.
  
:<math>\sum_{p = 1}^{i - 1} l_{ip} l_{jp}</math>
 
  
в режиме накопления или без него, в зависимости от требований задачи. Во многих последовательных реализациях упомянутый способ представления не используется. Дело в том, что в них вычисление сумм типа
+
* Ортогонализация по отношению к сошедшимся векторам  <math>z = z-(y^T_{i,t},z)y_{i,t} </math> для <math>i  = 1 \dots t</math>
  
:<math>a_{ji} - \sum_{p = 1}^{i - 1} l_{ip} l_{jp}</math><nowiki/>
 
  
в которых и встречаются скалярные произведения, ведутся не в порядке «вычислили скалярное произведение, а потом вычли его из элемента», а путём вычитания из элемента покомпонентных произведений, являющихся частями скалярных произведений. Поэтому следует считать вычислительным ядром метода не вычисления скалярных произведений, а вычисления выражений
+
===Макроструктура алгоритма===
 +
Были выделены следующие макрооперации:
  
:<math>a_{ji} - \sum_{p = 1}^{i - 1} l_{ip} l_{jp}</math>
+
1. Умножение матрицы на вектор,
  
в ''режиме накопления'' или без него.
+
<math>z=Aq_j,  </math>.  
  
Тем не менее, в популярных зарубежных реализациях точечного метода Холецкого, в частности, в библиотеках LINPACK и LAPACK, основанных на BLAS, используются именно вычисления скалярных произведений в виде вызова соответствующих подпрограмм BLAS (конкретно — функции SDOT). На последовательном уровне это влечёт за собой дополнительную операцию суммирования на каждый из <math>\frac{n (n + 1)}{2}</math> вычисляемый элемент матрицы <math>L</math> и некоторое замедление работы программы (о других следствиях рассказано ниже в разделе «[[#Существующие реализации алгоритма|Существующие реализации алгоритма]]»). Поэтому в данных вариантах ядром метода Холецкого будут вычисления этих скалярных произведений.
+
2. Вычисление вектора <math>q_{j+1} </math> с помощью линейной комбинации других векторов:
  
=== Макроструктура алгоритма ===
+
<math>\alpha_j=q_j^Tz, </math>
  
Как записано и в [[#Вычислительное ядро алгоритма|описании ядра алгоритма]], основную часть метода составляют множественные (всего <math>\frac{n (n - 1)}{2}</math>) вычисления сумм
+
<math>z=z-\alpha_jq_j-\beta_{j-1}q_{j-1}</math>
  
:<math>a_{ji}-\sum_{p=1}^{i-1}l_{ip} l_{jp}</math>
+
<math>q_{j+1}=z/\|z\|_2</math>
  
в режиме накопления или без него.
+
3. Ортогонализация вектора Ланцоша с помощью скалярного произведения: 
  
=== Схема реализации последовательного алгоритма ===
+
<math>z = z-(y^T_{i,t},z)y_{i,t} </math>
  
Последовательность исполнения метода следующая:
+
4. Для вычисления собственных значений матрицы будет использоваться алгоритм "разделяй и властвуй".
  
1. <math>l_{11}= \sqrt{a_{11}}</math>
+
===Схема реализации последовательного алгоритма===
  
2. <math>l_{j1}= \frac{a_{j1}}{l_{11}}</math> (при <math>j</math> от <math>2</math> до <math>n</math>).
+
<math>1)</math> Инициализируются векторы <math> \beta_0=0,q_0=0,</math>
 +
<math>2)</math> Считается норма вектора <math> b: \; \;  \|b\|_2 = \sqrt{\sum\limits_{j=1}^{n} b_j^2},</math>
 +
<math>3)</math> Находится первый вектор матрицы <math> Q: \; \; q_{1} = \frac{b_{j}}{\|b\|_2},</math>
 +
<math>4)</math> Начинается цикл, повторяющийся <math> k </math> раз по переменной <math> j: </math>
 +
    <math>4.1)</math> Считается произведение матрицы на вектор: <math> z_d = \sum\limits_{y=1}^{n} a_{dy} q_{j_y}, \; d = 1,\,\dots\,, n, </math> где
 +
    <math>z_d - </math> компоненты вектора Ланцоша <math>z, </math>
 +
    <math>4.2)</math> Скалярно умножается <math>q_j^T</math>  и <math> z: \; \; \alpha_j = \sum\limits_{d=1}^{n}q_{j_d} z_d,</math>
 +
    <math>4.3)</math> Вычисляется линейная комбинация векторов <math>z=z-\alpha_jq_j-\beta_{j-1}q_{j-1},  </math>
 +
    <math>4.4)</math> <math>t</math> Присваивается <math>j - 1.  </math> В цикле по <math> i </math> от первого до посчитанного <math> t</math>-го собственного вектора проводится
 +
    выборочная ортогонализация к сошедшимся векторам Ритца. То есть:
 +
      <math>4.4.1)</math> Считается норма матрицы <math>\|T_{t}\|_2, </math> <math> \| T \|_2 =  \sup\limits_{\| x \|_2 = 1} \| T x \|_2 = \sup\limits_{(x, x) = 1} \sqrt{(Tx, Tx)}</math>, подчиненная векторной норме <math> \| x \|_2 = \sqrt{\sum_{i = 1}^n |x_i|^2} </math>. Т.е.
 +
      <math> \|    T_{t} \|_2 =  \max\limits_{i=1 \dots t} \theta_i</math>, где <math> \theta_i</math> - собственные значения матрицы <math> T,</math>
 +
      <math>4.4.2)</math> Берется <math>t</math>-ю координата вектора <math> v_i</math>  и проверяется, выполняется ли равенство:<math>\beta_{t}|v_i(t)| \leqslant \sqrt{\varepsilon}\|T_{t}\| \, \, ,</math>
 +
      Если выполняется, то
 +
          <math>4.4.2.1)</math> Ищется вектор Ритца <math>y_{i,t_d} = \sum\limits_{x=1}^{t} q_{x_d} v_{i_x}, \; d = 1,\,\dots\,, n,</math>, где  <math>y_{i,t_d}</math> -  <math>d</math>-я компонента вектора Ритца <math>y,</math>
 +
          <math>4.4.2.2)</math> Скалярно умножается <math>y_{i,t}^T</math>  и <math> z: \; \; \gamma = \sum\limits_{d=1}^{n}y_{i,t_d} z_d,</math>
 +
          <math>4.4.2.3)</math> Вычисляется линейная комбинация векторов <math>z = z-\gamma\,  y_{i,t}, </math>
 +
          <math>4.4.2.4)</math> Увеличивается <math>i </math>. Производится возврат к пункту <math>4.4.2,</math>
 +
    <math>4.5)</math> Ищется норма вектора <math> z: \; \; \beta_{j}=\|z\|_2, </math>
 +
    <math>4.6)</math> Ищется вектор Ланцоша <math>q_{j+1}=z/\beta_{j}, </math>
 +
    <math>4.7)</math> Вычисляется новое собственное значение <math> \theta_j </math> и собственный вектор <math> v_j </math> для полученной матрицы <math> T_j,</math>
 +
    <math>4.8)</math> Увеличивается <math> j </math>, производится возврат к шагу <math>4.1.</math>
  
Далее для всех <math>i</math> от <math>2</math> до <math>n</math> по нарастанию выполняются
+
===Последовательная сложность алгоритма===
 
+
Рассмотрим последовательную сложность алгоритма.
3. <math>l_{ii} = \sqrt{a_{ii} - \sum_{p = 1}^{i - 1} l_{ip}^2}</math> и
+
<math>1)</math> Инициализируются векторы,
 
+
<math>2)</math> Считается норма вектора: <math> n </math> сложений и умножений, одно вычисление корня,
4. (кроме <math>i = n</math>): <nowiki/><math>l_{ji} = \left (a_{ji} - \sum_{p = 1}^{i - 1} l_{ip} l_{jp} \right ) / l_{ii}</math> (для всех <math>j</math> от <math>i + 1</math> до <math>n</math>).
+
<math>3)</math> Находится первый вектор матрицы: <math> n </math> делений,
 
+
<math>4)</math> Начинается цикл, повторяющийся <math> k </math> раз по переменной : <math>  j </math>:
После этого (если <math>i < n</math>) происходит переход к шагу 3 с бо́льшим <math>i</math>.
+
    <math>4.1)</math> Считается произведение матрицы на вектор: <math>   n </math> сложений и умножений,
 
+
    <math>4.2)</math> Скалярно умножается два вектора : <math>   n </math> сложений и умножений,
Особо отметим, что вычисления сумм вида <math>a_{ji} - \sum_{p = 1}^{i - 1} l_{ip} l_{jp}</math> в обеих формулах производят в режиме накопления вычитанием из <math>a_{ji}</math> произведений <math>l_{ip} l_{jp}</math> для <math>p</math> от <math>1</math> до <math>i - 1</math>, c нарастанием <math>p</math>.
+
    <math>4.3)</math> Вычисляется линейная комбинация векторов : <math>  2n </math> вычитаний и умножений,
 
+
    <math>4.4)</math> Повторяется цикл : <math>   j-1 </math> раз. На каждом из этапов:
=== Последовательная сложность алгоритма ===
+
      <math>4.4.1)</math> Считается норма матрицы: : <math>  j-1 </math> сравнений,
 
+
      <math>4.4.2)</math> Проверяется равенство::  2 умножения, одно сравнение
Для разложения матрицы порядка n методом Холецкого в последовательном (наиболее быстром) варианте требуется:
+
          <math>4.4.2.1)</math> Ищется вектор Ритца : <math>   n*(j-1) </math> сложений и умножений,
+
          <math>4.4.2.2)</math> Скалярно умножаются два вектора:  <math>   n </math> сложений и умножений,
* <math>n</math> вычислений квадратного корня,
+
          <math>4.4.2.3)</math> Вычисляется линейная комбинация векторов : <math>  n </math> умножений и вычитаний,
* <math>\frac{n(n-1)}{2}</math> делений,
+
          <math>4.4.2.4)</math> Увеличивается <math>i </math>. Одно сложение.
* <math>\frac{n^3-n}{6}</math> сложений (вычитаний),
+
      Таким образом, т.к. внутренний цикл повторяется <math>  j-1 </math> раз, а <math>  j </math> в свою очередь пробегает от <math>1 \dots k </math>, значит, полное количество операций в пункте <math>4.4)</math>, которое будет занимать весь внутренний цикл будет: сравнений <math> \frac{(k-1)k(k+1)}{3} + \frac{k(k-1)}{2} </math>, умножений: <math> 2\frac{k(k-1)}{2} + \frac{(k-1)k(k+1)}{3} + \frac{(2n)k(k-1)}{2} </math>, сложений <math>\frac{(k-1)k(k+1)}{3} + \frac{(n+1)k(k-1)}{2}</math> и вычитаний <math> \frac{(n)k(k-1)}{2}</math>
* <math>\frac{n^3-n}{6}</math> умножений.
+
    <math>4.5)</math> Ищется норма вектора: <math> n </math> сложений и умножений
 
+
    <math>4.6)</math> Ищется вектор Ланцоша <math> n </math> делений
Умножения и сложения (вычитания) составляют ''основную часть алгоритма''.
+
    <math>4.7)</math> Увеличивается <math> j </math>, одно сложение.
 +
   
 +
    Так, пункты  <math>4.1 - 4.3, 4.5 - 4.7</math> занимают <math> k*(n+n+n+1)</math> сложений, <math>k(n+n+2n+n)</math> умножений, <math>2nk</math> вычитаний и <math>n</math> делений.
 +
 +
 
   
 
   
При этом использование режима накопления требует совершения умножений и вычитаний в режиме двойной точности (или использования функции вроде DPROD в Фортране), что ещё больше увеличивает долю умножений и сложений/вычитаний во времени, требуемом для выполнения метода Холецкого.
+
Подводя итог, общее количество операций умножения и деления <math>n + n + 2\frac{k(k-1)}{2}  + \frac{(k-1)k(k+1)}{3} + \frac{(2n)k(k-1)}{2} +  k(n+n+2n+n) + n</math>, т.е. <math>O( k^3+nk^2+k^2+kn+n )</math>
 +
общее количество сложений и вычитаний: <math> n + \frac{(k-1)k(k+1)}{3} + \frac{(n+1)k(k-1)}{2}+ \frac{(n)k(k-1)}{2} + k*(n+n+n+1) + 2nk</math>, т.е.  <math>O( k^3+nk^2+kn+n )</math>
 +
И <math> \frac{(k-1)k(k+1)}{3} + \frac{k(k-1)}{2} </math> сравнений ( <math>O( k^3+k^2)</math>).
  
При классификации по последовательной сложности, таким образом, метод Холецкого относится к алгоритмам ''с кубической сложностью''.
 
  
=== Информационный граф ===
 
  
Опишем [[глоссарий#Граф алгоритма|граф алгоритма]]<ref>Воеводин В.В.  Математические основы параллельных вычислений// М.: Изд. Моск. ун-та, 1991. 345 с.</ref><ref>Воеводин В.В., Воеводин Вл.В. Параллельные вычисления. – СПб.: БХВ - Петербург, 2002. – 608 с.</ref><ref>Фролов А.В.. Принципы построения и описание языка Сигма. Препринт ОВМ АН N 236. М.: ОВМ АН СССР, 1989.</ref> как аналитически, так и в виде рисунка.
+
[[File:Graph.jpg|200px|right| frame |рис. 1: Информационный граф]]
  
Граф алгоритма состоит из трёх групп вершин, расположенных в целочисленных узлах трёх областей разной размерности.
+
=== Информационный граф===
 +
На рисунке (рис. 1) изображен информационный граф. Ниже приведено его описание:
  
'''Первая''' группа вершин расположена в одномерной области, соответствующая ей операция вычисляет функцию SQRT.  
+
* Вершины 1 соответствуют операции  <math>Aq_j,</math>.
Единственная координата каждой из вершин <math>i</math> меняется в диапазоне от <math>1</math> до <math>n</math>, принимая все целочисленные значения.
+
* Вершины 2 соответствуют операции скалярного произведения <math>q_j^Tz</math>.
 +
* Вершины 3 соответствуют операции <math>z-\sum\nolimits_{i=1}^{j-1}(z^Tq_i)q_i</math>.
 +
* Вершины 4 соответствуют операции <math>z-\alpha_jq_j-\beta_{j-1}q_{j-1}</math>.
 +
* Вершины 5 соответствуют операции <math>||z||</math>.
 +
* Вершины 6 соответствуют операции <math>z/\beta_j</math>.
  
Аргумент этой функции
+
=== Ресурс параллелизма алгоритма===
 
* при <math>i = 1</math> — элемент ''входных данных'', а именно  <math>a_{11}</math>;
 
* при <math>i > 1</math> — результат срабатывания операции, соответствующей вершине из третьей группы, с координатами <math>i - 1</math>, <math>i</math>, <math>i - 1</math>.
 
Результат срабатывания операции является ''выходным данным'' <math>l_{ii}</math>.
 
  
'''Вторая''' группа вершин расположена в двумерной области, соответствующая ей операция <math>a / b</math>.
+
Хотя алгоритм является итерационным, возможно распараллелить внутри каждой итерации умножение матрицы на вектор и процесс переортогонализации Грамма-Шмидта.
Естественно введённые координаты области таковы:
 
* <math>i</math> — меняется в диапазоне от <math>1</math> до <math>n-1</math>, принимая все целочисленные значения;
 
* <math>j</math> — меняется в диапазоне от <math>i+1</math> до <math>n</math>, принимая все целочисленные значения.
 
  
Аргументы операции следующие:
+
Процесс умножения матриц matvec можно распараллелить несколькими способами[https://eprint.iacr.org/2011/416.pdf].
*<math>a</math>:
 
** при <math>i = 1</math> — элементы ''входных данных'', а именно <math>a_{j1}</math>;
 
** при <math>i > 1</math> — результат срабатывания операции, соответствующей вершине из третьей группы, с координатами <math>i - 1, j, i - 1</math>;
 
* <math>b</math> — результат срабатывания операции, соответствующей вершине из первой группы, с координатой <math>i</math>.
 
  
Результат срабатывания операции является ''выходным данным'' <math>l_{ji}</math>.
+
Как видно из графа, для выполнения j-й операции, необходимо выполнить следующие ярусы:
 +
# <math>n</math> ярусов сложений с <math>n</math> операциями умножения в каждом (вычисление <math>z</math>);
 +
# <math>n</math> ярусов сложений с <math>1</math> операцией умножения в каждом (вычисление <math>\alpha_j</math>);
 +
# <math>j</math> ярусов сложений с <math>n</math> операциями умножения в каждом, <math>n</math> ярусов сложений с <math>j</math> операциями умножения в каждом, <math>1</math> ярус вычитаний размера <math>n</math> (первая переортогонализация);
 +
# <math>j</math> ярусов сложений с <math>n</math> операциями умножения в каждом, <math>n</math> ярусов сложений с <math>j</math> операциями умножения в каждом, <math>1</math> ярус вычитаний размера <math>n</math> (вторая переортогонализация);
 +
# <math>n</math> ярусов сложений с <math>1</math> операцией умножения в каждом (вычисление <math>\beta_j</math>);
 +
# <math>1</math> ярус делений размера <math>n</math> (вычисление <math>q_{j + 1}</math>).
  
'''Третья''' группа вершин расположена в трёхмерной области, соответствующая ей операция  <math>a - b * c</math>.
+
Если учесть весь ресурс параллелизма, можно получить мультипликативную вычислительную сложность всего алгоритма: <math>O(n^2k+nk^2)/p)</math>.
Естественно введённые координаты области таковы:  
 
* <math>i</math> — меняется в диапазоне от <math>2</math> до <math>n</math>, принимая все целочисленные значения;
 
* <math>j</math> — меняется в диапазоне от <math>i</math> до <math>n</math>, принимая все целочисленные значения;
 
* <math>p</math> — меняется в диапазоне  от <math>1</math> до <math>i - 1</math>, принимая все целочисленные значения.
 
  
Аргументы операции следующие:
+
Вычислительная мощность данного алгоритма, как отношение числа операций к суммарному объему входных и выходных, данных равняется  <math>O(k)</math>.
*<math>a</math>:
 
** при <math>p = 1</math> элемент входных данных <math>a_{ji}</math>;
 
** при <math>p > 1</math> — результат срабатывания операции, соответствующей вершине из третьей группы, с координатами <math>i, j, p - 1</math>;
 
*<math>b</math> — результат срабатывания операции, соответствующей вершине из второй группы, с координатами <math>p, i</math>;
 
*<math>c</math> — результат срабатывания операции, соответствующей вершине из второй группы, с координатами <math>p, j</math>;
 
  
Результат срабатывания операции является ''промежуточным данным'' алгоритма.
+
=== Входные и выходные данные алгоритма===
 +
'''Входные данные'''
  
Описанный граф можно посмотреть на рис.1 и рис.2, выполненных для случая <math>n = 4</math>. Здесь вершины первой группы обозначены жёлтым цветом и буквосочетанием sq, вершины второй — зелёным цветом и знаком деления, третьей — красным цветом и буквой f. Вершины, соответствующие операциям, производящим выходные данные алгоритма, выполнены более крупно. Дублирующие друг друга дуги даны как одна. На рис.1 показан граф алгоритма согласно классическому определению , на рис.2 к графу алгоритма добавлены вершины , соответствующие входным (обозначены синим цветом) и выходным (обозначены розовым цветом) данным.
+
Матрица  <math> A </math>, начальный вектор  <math> b </math> и число собственных значений, которые мы хотим найти  <math> k </math>.
  
[[file:Cholesky full.png|thumb|center|1400px|Рисунок 1. Граф алгоритма без отображения входных и выходных данных. SQ - вычисление квадратного корня, F - операция a-bc, Div - деление.]]
+
Ввиду симметричности из матрицы <math> A </math> достаточно хранить только ее диагональ и элементы, лежащие выше ее. Таким образом, объем входных данных:
[[file:Cholesky full_in_out.png|thumb|center|1400px|Рисунок 2. Граф алгоритма с отображением входных и выходных данных. SQ - вычисление квадратного корня, F - операция a-bc, Div - деление, In - входные данные, Out - результаты.]]
 
  
<center>
+
<math>\frac{n (n + 1)}{2} + n + 1 = \frac{n^2+3n+2}{2}.</math>
{{#widget:Iframe
 
|url=https://algowiki-project.org/html/Algo_viewer_json/cholesky/Algo_viewer_example_2.html
 
|width=800
 
|height=600
 
|border=1
 
}}
 
<br/>
 
Интерактивное изображение графа алгоритма без входных и выходных данных для n = 4.
 
</center>
 
  
=== Ресурс параллелизма алгоритма ===
+
'''Выходные данные'''
  
Для разложения матрицы порядка <math>n</math> методом Холецкого в параллельном варианте требуется последовательно выполнить следующие ярусы:
+
Матрица собственных значений <math> \Lambda = diag(\theta_1 \dots \theta_k) </math>, матрица собственных векторов <math> V = [v_1 \dots v_k] </math> размера <math> n \times k </math>
* <math>n</math> ярусов с вычислением квадратного корня (единичные вычисления в каждом из ярусов),
 
* <math>n - 1</math> ярус делений (в каждом из ярусов линейное количество делений, в зависимости от яруса — от <math>1</math> до <math>n - 1</math>),
 
* по <math>n - 1</math> ярусов умножений и сложений/вычитаний (в каждом из ярусов — квадратичное количество операций, от <math>1</math> до <math>\frac{n^2 - n}{2}</math>.
 
 
Таким образом, в параллельном варианте, в отличие от последовательного, вычисления квадратных корней и делений будут определять довольно значительную долю требуемого времени. При реализации на конкретных архитектурах наличие в отдельных ярусах [[глоссарий#Ярусно-параллельная форма графа алгоритма|ЯПФ]] отдельных вычислений квадратных корней может породить и другие проблемы. Например, при реализации на ПЛИСах остальные вычисления (деления и тем более умножения и сложения/вычитания) могут быть конвейеризованы, что даёт экономию и по ресурсам на программируемых платах; вычисления же квадратных корней из-за их изолированности приведут к занятию ресурсов на платах, которые будут простаивать большую часть времени. Таким образом, общая экономия в 2 раза, из-за которой метод Холецкого предпочитают в случае симметричных задач тому же методу Гаусса, в параллельном случае уже имеет место вовсе не по всем ресурсам, и главное - не по требуемому времени.
 
 
 
При этом использование режима накопления требует совершения умножений и вычитаний в режиме двойной точности, а в параллельном варианте это означает, что практически все промежуточные вычисления для выполнения метода Холецкого в режиме накопления должны быть двойной точности. В отличие от последовательного варианта это означает увеличение требуемой памяти почти в 2 раза.
 
 
 
При классификации по высоте ЯПФ, таким образом, метод Холецкого относится к алгоритмам со сложностью <math>O(n)</math>. При классификации по ширине ЯПФ его сложность будет <math>O(n^2)</math>.
 
 
 
=== Входные и выходные данные алгоритма ===
 
  
'''Входные данные''': плотная матрица <math>A</math> (элементы <math>a_{ij}</math>).
+
Так, объем выходных данных:  <math> k + kn </math>
Дополнительные ограничения:
 
* <math>A</math> – симметрическая матрица, т. е. <math>a_{ij}= a_{ji}, i, j = 1, \ldots, n</math>.
 
* <math>A</math> – положительно определённая матрица, т. е. для любых ненулевых векторов <math>\vec{x}</math> выполняется <math>\vec{x}^T A \vec{x} > 0</math>.
 
 
 
'''Объём входных данных''': <math>\frac{n (n + 1)}{2}</math> (в силу симметричности достаточно хранить только диагональ и над/поддиагональные элементы). В разных реализациях эта экономия хранения может быть выполнена разным образом. Например, в библиотеке, реализованной в НИВЦ МГУ, матрица A хранилась в одномерном массиве длины <math>\frac{n (n + 1)}{2}</math> по строкам своего нижнего треугольника.
 
 
 
'''Выходные данные''': нижняя треугольная матрица <math>L</math> (элементы <math>l_{ij}</math>).
 
 
 
'''Объём выходных данных''': <math>\frac{n (n + 1)}{2}</math> (в силу треугольности достаточно хранить только ненулевые элементы). В разных реализациях эта экономия хранения может быть выполнена разным образом. Например, в той же  библиотеке, созданной в НИВЦ МГУ, матрица <math>L</math> хранилась в одномерном массиве длины <math>\frac{n (n + 1)}{2}</math> по строкам своей нижней части.
 
  
 
=== Свойства алгоритма ===
 
=== Свойства алгоритма ===
 +
Если <math>A</math> эрмитова матрица, то алгоритм Ланцоша и Bi-Lanczos сходятся к одинаковым трехдиагональным матрицам Ритца[http://ta.twi.tudelft.nl/nw/users/vuik/papers/DUT-TWI-96-44.pdf].
  
Соотношение последовательной и параллельной сложности в случае неограниченных ресурсов, как хорошо видно, является ''квадратичным'' (отношение кубической к линейной).
+
При реализации классического алгоритма Ланцоша возникает большая погрешность при округлении. Вариант с полной переортогонализацией позволяет избегать больших погрешностей, однако является более ресурсоемкимВариант с частичной переортогонализацией является промежуточным.  
 
 
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных – всего лишь ''линейна''.
 
 
 
При этом алгоритм почти полностью детерминирован, это гарантируется теоремой о единственности разложения. Использование другого порядка выполнения ассоциативных операций может привести к накоплению ошибок округления, однако это влияние в используемых вариантах алгоритма не так велико, как, скажем, отказ от использования режима накопления.
 
 
 
Дуги информационного графа, исходящие из вершин, соответствующих операциям квадратного корня и деления, образуют пучки т. н. рассылок линейной мощности (то есть степень исхода этих вершин и мощность работы с этими данными — линейная функция от порядка матрицы и координат этих вершин). При этом естественно наличие в этих пучках «длинных» дуг. Остальные дуги локальны.
 
 
 
Наиболее известной является компактная укладка графа — его проекция на треугольник матрицы, который перевычисляется укладываемыми операциями. При этом «длинные» дуги можно убрать, заменив более дальнюю пересылку комбинацией нескольких ближних (к соседям).
 
 
 
[[Глоссарий#Эквивалентное возмущение|Эквивалентное возмущение]] <math>M</math> у метода Холецкого всего вдвое больше, чем возмущение <math>\delta A</math>, вносимое в матрицу при вводе чисел в компьютер:
 
<math>
 
||M||_{E} \leq 2||\delta A||_{E}
 
</math>
 
 
 
Это явление обусловлено положительной определённостью матрицы. Среди всех используемых разложений матриц это наименьшее из эквивалентных возмущений.
 
 
 
== Программная реализация алгоритма ==
 
 
 
=== Особенности реализации последовательного алгоритма ===
 
 
 
В простейшем (без перестановок суммирования) варианте метод Холецкого на Фортране можно записать так:
 
<source lang="fortran">
 
DO  I = 1, N
 
S = A(I,I)
 
DO  IP=1, I-1
 
S = S - DPROD(A(I,IP), A(I,IP))
 
END DO
 
A(I,I) = SQRT (S)
 
DO  J = I+1, N
 
S = A(J,I)
 
DO  IP=1, I-1
 
S = S - DPROD(A(I,IP), A(J,IP))
 
END DO
 
A(J,I) = S/A(I,I)
 
END DO
 
END DO
 
</source>
 
При этом для реализации режима накопления переменная <math>S</math> должна быть двойной точности.
 
 
 
Отдельно следует обратить внимание на используемую в такой реализации функцию DPROD. Её появление как раз связано с тем, как математики могли использовать режим накопления вычислений. Дело в том, что, как правило, при выполнении умножения двух чисел с плавающей запятой сначала результат получается с удвоенными длинами мантиссы и порядка, и только при выполнении присваивания переменной одинарной точности результат округляется. Эта возможность даёт выполнять умножение действительных чисел с двойной точностью без предварительного приведения их к типу двойной точности. На ранних этапах развития вычислительных библиотек снятие результата без округление реализовали вставками специального кода в фортран-программы, но уже в 70-х гг. XX века в ряде трансляторов Фортрана появилась функция DPROD, реализующая это без обращения программиста к машинным кодам. Она была закреплена среди стандартных функций в стандарте Фортран 77, и реализована во всех современных трансляторах Фортрана.
 
 
 
Для метода Холецкого существует блочная версия, которая отличается от точечной не тем, что операции над числами заменены на аналоги этих операций над блоками; её построение основано на том, что практически все циклы точечной версии имеют тип SchedDo в терминах методологии, основанной на исследовании информационного графа и, следовательно, могут быть развёрнуты. Тем не менее, обычно блочную версию метода Холецкого записывают не в виде программы с развёрнутыми и переставленными циклами, а в виде программы, подобной реализации точечного метода, в которой вместо соответствующих скалярных операций присутствуют операции над блоками.
 
 
 
Особенностью размещения массивов в Фортране является хранение их "по столбцам" (быстрее всего меняется первый индекс). Поэтому для обеспечения локальности работы с памятью представляется более эффективной такая схема метода Холецкого (полностью эквивалентная описанной), когда исходная матрица и её разложение хранятся не в нижнем, а в верхнем треугольнике. Тогда при вычислениях скалярных произведений программа будет "идти" по последовательным ячейкам памяти компьютера.
 
 
 
Есть и другой вариант точечной схемы: использовать вычисляемые элементы матрицы <math>L</math> в качестве аргументов непосредственно «сразу после» их вычисления. Такая программа будет выглядеть так:
 
<source lang="fortran">
 
DO  I = 1, N
 
A(I,I) = SQRT (A(I, I))
 
DO  J = I+1, N
 
A(J,I) = A(J,I)/A(I,I)
 
END DO
 
DO K=I+1,N
 
DO J = K, N
 
A(J,K) = A(J,K) - A(J,I)*A(K,I)
 
END DO
 
END DO
 
END DO
 
</source>
 
Как видно, в этом варианте для реализации режима накопления одинарной точности мы должны будем объявить двойную точность для массива, хранящего исходные данные и результат. Подчеркнём, что [[глоссарий#Граф алгоритма|граф алгоритма]] обеих схем - один и тот же (из п.1.7), если не считать изменением замену умножения на функцию DPROD!
 
 
 
=== Локальность данных и вычислений ===
 
 
 
==== Локальность реализации алгоритма ====
 
 
 
===== Структура обращений в память и качественная оценка локальности =====
 
 
 
[[file:Cholesky_locality1.jpg|thumb|center|700px|Рисунок 3. Реализация метода Холецкого. Общий профиль обращений в память]]
 
 
 
На рис.3 представлен профиль обращений в память<ref>Воеводин Вад. В. Визуализация и анализ профиля обращений в память // Вестник Южно-Уральского государственного университета. Серия Математическое моделирование и про-граммирование. — 2011. — Т. 17, № 234. — С. 76–84.</ref><ref>Воеводин Вл. В., Воеводин Вад. В. Спасительная локальность суперкомпьютеров // Открытые системы. — 2013. — № 9. — С. 12–15.</ref> для реализации метода Холецкого. В программе задействован только 1 массив, поэтому в данном случае обращения в профиле происходят только к элементам этого массива. Программа состоит из одного основного этапа, который, в свою очередь, состоит из последовательности подобных итераций. Пример одной итерации выделен зеленым цветом.
 
 
 
Видно, что на каждой <math>i</math>-й итерации используются все адреса, начиная с некоторого, при этом адрес первого обрабатываемого элемента увеличивается. Также можно заметить, что число обращений в память на каждой итерации растет примерно до середины работы программы, после чего уменьшается вплоть до завершения работы. Это позволяет говорить о том, что данные в программе используются неравномерно, при этом многие итерации, особенно в начале выполнения программы, задействуют большой объем данных, что приводит к ухудшению локальности.
 
 
 
Однако в данном случае основным фактором, влияющим на локальность работы с памятью, является строение итерации. Рассмотрим фрагмент профиля, соответствующий нескольким первым итерациям.
 
 
 
[[file:Cholesky_locality2.jpg|thumb|center|700px|Рисунок 4. Реализация метода Холецкого. Фрагмент профиля (несколько первых итераций)]]
 
 
 
Исходя из рис.4 видно, что каждая итерация состоит из двух различных фрагментов. Фрагмент 1 – последовательный перебор (с некоторым шагом) всех адресов, начиная с некоторого начального. При этом к каждому адресу происходит мало обращений. Такой фрагмент обладает достаточно неплохой пространственной локальностью, так как шаг по памяти между соседними обращениями невелик, но плохой временно́й локальностью, поскольку данные редко используются повторно.
 
 
 
Фрагмент 2 устроен гораздо лучше с точки зрения локальности. В рамках этого фрагмента выполняется большое число обращений подряд к одним и тем же данным, что обеспечивает гораздо более высокую степень как пространственной, так и временно́й локальности по сравнению с фрагментом 1.
 
 
 
После рассмотрения фрагмента профиля на рис.4 можно оценить общую локальность двух фрагментов на каждой итерации. Однако стоит рассмотреть более подробно, как устроен каждый из фрагментов.
 
 
 
[[file:Cholesky_locality3.jpg|thumb|center|700px|Рисунок 5. Реализация метода Холецкого. Фрагмент профиля (часть одной итерации)]]
 
 
 
Рис.5, на котором представлена часть одной итерации общего профиля (см. рис.3), позволяет отметить достаточно интересный факт: строение каждого из фрагментов на самом деле заметно сложнее, чем это выглядит на рис.4. В частности, каждый шаг фрагмента 1 состоит из нескольких обращений к соседним адресам, причем выполняется не последовательный перебор. Также можно увидеть, что фрагмент 2 на самом деле в свою очередь состоит из повторяющихся итераций, при этом видно, что каждый шаг фрагмента 1 соответствует одной итерации фрагмента 2 (выделено зеленым на рис.5). Это лишний раз говорит о том, что для точного понимания локальной структуры профиля необходимо его рассмотреть на уровне отдельных обращений.
 
 
 
Стоит отметить, что выводы на основе рис.5 просто дополняют общее представлении о строении профиля обращений; сделанные на основе рис.4 выводы относительно общей локальности двух фрагментов остаются верны.
 
 
 
===== Количественная оценка локальности =====
 
  
Основной фрагмент реализации, на основе которого были получены количественные оценки, приведен [http://git.algowiki-project.org/Voevodin/locality/blob/master/benchmarks/holecky/holecky.h здесь] (функция Kernel). Условия запуска описаны [http://git.algowiki-project.org/Voevodin/locality/blob/master/README.md здесь].
+
Алгоритм может завершить свою работу досрочно, когда найденные собственные значения будут достаточно близки к целевым.
  
Первая оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.
+
== Программная реализация алгоритма==
 
+
=== Особенности реализации последовательного алгоритма===
[[file:Cholesky_locality4.jpg|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]
+
=== Локальность данных и вычислений===
 
+
=== Возможные способы и особенности параллельной реализации алгоритма===
На рис.6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что реализация метода Холецкого характеризуется достаточно высокой скоростью взаимодействия с памятью, однако ниже, чем, например, у теста Линпак или реализации метода Якоби.
 
 
 
Вторая характеристика – cvg – предназначена для получения более машинно-независимой оценки локальности. Она определяет, насколько часто в программе необходимо подтягивать данные в кэш-память. Соответственно, чем меньше значение cvg, тем реже это нужно делать, тем лучше локальность.
 
 
 
[[file:Cholesky_locality5.jpg|thumb|center|700px|Рисунок 7. Сравнение значений оценки cvg]]
 
 
 
На рис.7 приведены значения cvg для того же набора реализаций, отсортированные по убыванию (чем меньше cvg, тем в общем случае выше локальность). Можно увидеть, что, согласно данной оценке, реализация метода Холецкого оказалась ниже в списке по сравнению с оценкой daps.
 
 
 
=== Возможные способы и особенности параллельной реализации алгоритма ===
 
 
 
Как нетрудно видеть по структуре графа алгоритма, вариантов распараллеливания алгоритма довольно много. Например, во втором варианте (см. раздел «[[#Особенности реализации последовательного алгоритма|Особенности реализации последовательного алгоритма]]») все внутренние циклы параллельны, в первом — параллелен цикл по <math>J</math>. Тем не менее, простое распараллеливание таким способом «в лоб» вызовет такое количество пересылок между процессорами с каждым шагом по внешнему циклу, которое почти сопоставимо с количеством арифметических операций. Поэтому перед размещением операций и данных между процессорами вычислительной системы предпочтительно разбиение всего пространства вычислений на блоки, с сопутствующим разбиением обрабатываемого массива.
 
 
 
Многое зависит от конкретного типа вычислительной системы. Присутствие конвейеров на узлах многопроцессорной системы делает рентабельным параллельное вычисление нескольких скалярных произведений сразу. Подобная возможность есть и на программировании ПЛИСов, но там быстродействие будет ограничено медленным последовательным выполнением операции извлечения квадратного корня.
 
 
В принципе, возможно и использование т. н. «скошенного» параллелизма. Однако его на практике никто не использует, из-за усложнения управляющей структуры программы.
 
  
 
=== Масштабируемость алгоритма и его реализации ===
 
=== Масштабируемость алгоритма и его реализации ===
 +
Исследование масштабируемости реализации алгоритма проводилось с использованием стандарта MPI на суперкомпьютере "Ломоносов".
  
==== Масштабируемость алгоритма ====
+
Набор изменяемых параметров запуска реализации алгоритма и границы значений параметров алгоритма:
 
+
*Число процессоров 4, 8, 16, 32, 64, 128.
==== Масштабируемость реализации алгоритма ====
+
*Размерность матрицы от 1000 до 50000
Проведём исследование масштабируемости параллельной реализации разложения Холецкого согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере "Ломоносов"<ref name="Lom">Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.</ref> [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].
+
Сборка осуществлялась с параметрами:  
 
+
*openmpi/1.5.5-icc
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма:  
+
*intel/13.1.0
 
 
* число процессоров [4 : 256] с шагом 4;
 
* размер матрицы [1024 : 5120].
 
 
 
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:
 
 
 
* минимальная эффективность реализации 0,11%;
 
* максимальная эффективность реализации 2,65%.
 
 
 
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации разложения Холецкого в зависимости от изменяемых параметров запуска.
 
 
 
[[file:Масштабируемость Параллельной реализации метода Холецкого Производительность3.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Холецкого. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]
 
[[file:Холецкий масштабируемость эффективность2.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Холецкого. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]
 
 
 
Построим оценки масштабируемости выбранной реализации разложения Холецкого:
 
* По числу процессов: -0,000593. При увеличении числа процессов эффективность на рассмотренной области изменений параметров запуска уменьшается, однако в целом уменьшение не очень быстрое. Малая интенсивность изменения объясняется крайне низкой общей эффективностью работы приложения с максимумом в 2,65%, и значение эффективности на рассмотренной области значений быстро доходит до десятых долей процента. Это свидетельствует о том, что на большей части области значений нет интенсивного снижения эффективности. Это объясняется также тем, что с ростом [[Глоссарий#Вычислительная сложность|вычислительной сложности]] падение эффективности становится не таким быстрым. Уменьшение эффективности на рассмотренной области работы параллельной программы объясняется быстрым ростом накладных расходов на организацию параллельного выполнения. С ростом вычислительной сложности задачи эффективность снижается так же быстро, но при больших значениях числа процессов. Это подтверждает предположение о том, что накладные расходы начинают сильно превалировать над вычислениями.
 
* По размеру задачи: 0,06017. При увеличении размера задачи эффективность возрастает. Эффективность возрастает тем быстрее, чем большее число процессов используется для выполнения. Это подтверждает предположение о том, что размер задачи сильно влияет на эффективность выполнения приложения. Оценка показывает, что с ростом размера задачи эффективность на рассмотренной области значений параметров запуска сильно увеличивается. Также, учитывая разницу максимальной и минимальной эффективности в 2,5%, можно сделать вывод, что рост эффективности при увеличении размера задачи наблюдается на большей части рассмотренной области значений.
 
* По двум направлениям: 0,000403. При рассмотрении увеличения как вычислительной сложности, так и числа процессов на всей рассмотренной области значений эффективность увеличивается, однако скорость увеличения эффективности небольшая. В совокупности с тем фактом, что разница между максимальной и минимальной эффективностью на рассмотренной области значений параметров небольшая, эффективность с увеличением масштабов возрастает, но медленно и с небольшими перепадами.
 
 
 
[http://git.algowiki-project.org/Teplov/Scalability/tree/master/cholesky-decomposition-master Исследованная параллельная реализация на языке C]
 
 
 
=== Динамические характеристики и эффективность реализации алгоритма ===
 
 
 
Для проведения экспериментов использовалась реализация разложения Холецкого, представленная в пакете SCALAPACK библиотеки Intel MKL (метод pdpotrf).  Все результаты получены на суперкомпьютере «Ломоносов»<ref name="Lom" />. Использовались процессоры Intel Xeon X5570 с пиковой производительностью в 94 Гфлопс, а также компилятор Intel с опцией –O2.  
 
На рисунках показана эффективность реализации разложения Холецкого (случай использования нижних треугольников матриц) для разного числа процессов и размерности матрицы 80000, запуск проводился на 256 процессах.
 
 
 
[[file:Cholesky CPU.png|thumb|center|700px|Рисунок 10. График загрузки CPU при выполнении разложения Холецкого]]
 
 
 
На графике загрузки процессора видно, что почти все время работы программы уровень загрузки составляет около 50%. Это хорошая картина для программ, запущенных без использования технологии Hyper Threading.
 
 
 
[[file:Cholesky FLOPS.png|thumb|center|700px|Рисунок 11. График операций с плавающей точкой в секунду при выполнении разложения Холецкого]]
 
 
 
На Рисунке 11 показан график количества операций с плавающей точкой в секунду. Видно, что к концу каждой итерации число операций возрастает.
 
[[file:Cholesky L1.png|thumb|center|700px|Рисунок 12. График кэш-промахов L1 в секунду при работе разложения Холецкого]]
 
 
На графике кэш-промахов первого уровня видно, что число промахов достаточно большое и находится на уровне 25 млн/сек в среднем по всем узлам.
 
[[file:Cholesky L3.png|thumb|center|700px|Рисунок 13. График кэш-промахов L3 в секунду при работе разложения Холецкого]]
 
 
 
На графике кэш-промахов третьего уровня видно, что число промахов все еще достаточно большое и находится на уровне 1,5 млн/сек в среднем по всем узлам. Это указывает на то, что задача достаточно большая, и данные плохо укладываются в кэш-память.
 
[[file:Cholesky MemRead.png|thumb|center|700px|Рисунок 14. График количества чтений из оперативной памяти при работе разложения Холецкого]]
 
 
 
На графике чтений из памяти на протяжении всего времени работы программы наблюдается достаточно интенсивная и не сильно изменяющаяся работа с памятью.
 
[[file:Cholesky MemWrite.png|thumb|center|700px|Рисунок 15. График количества записей в оперативную память при работе разложения Холецкого]]
 
 
 
На графике записей в память видна периодичность: на каждой итерации к концу выполнения число записей в память достаточно сильно падает. Это коррелирует с возрастанием числа операций с плавающей точкой и может объясняться тем, что при меньшем числе записей в память программа уменьшает накладные расходы и увеличивает эффективность.
 
[[file:Cholesky Inf Bps.png|thumb|center|700px|Рисунок 16. График скорости передачи по сети Infiniband в байт/сек при работе разложения Холецкого]]
 
 
 
На графике скорости передачи данных по сети Infiniband наблюдается достаточно интенсивное использование коммуникационной сети на каждой итерации. Причем к концу каждой итерации интенсивность передачи данных сильно возрастает. Это указывает на большую необходимость в обмене данными между процессами к концу итерации.
 
[[file:Cholesky Inf Pps.png|thumb|center|700px|Рисунок 17. График скорости передачи по сети Infiniband в пакетах/сек при работе разложения Холецкого]]
 
  
На графике скорости передачи данных в пакетах в секунду наблюдается большая «кучность» показаний максимального минимального и среднего значений в сравнении с графиком скорости передачи в байт/сек. Это говорит о том, что, вероятно, процессы обмениваются сообщениями различной длины, что указывает на неравномерное распределение данных. Также наблюдается рост интенсивности использования сети к концу каждой итерации.
+
[[File:time_dependance.jpg| center|border |800px]]
[[file:Cholesky LoadAVG.png|thumb|center|700px|Рисунок 18. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе разложения Холецкого]]
+
[http://pastebin.com/SbNrh7nz  Используемая параллельная реализация алгоритма на языке C++]
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно и приблизительно равняется 8. Это свидетельствует о стабильной работе программы с восьмью процессами на каждом узле. Это указывает на рациональную и статичную загрузку аппаратных ресурсов процессами.
 
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала достаточно эффективно и стабильно. Использование памяти и коммуникационной среды достаточно интенсивное, что может стать фактором снижения эффективности при существенном росте размера задачи или же числа процессоров.
 
Для существующих параллельных реализаций характерно отнесение всего ресурса параллелизма на блочный уровень. Относительно низкая эффективность работы связана с проблемами внутри одного узла, следующим фактором является неоптимальное соотношение между «арифметикой» и обменами. Видно, что при некотором (довольно большом) оптимальном размере блока обмены влияют не так уж сильно.
 
  
=== Выводы для классов архитектур ===
+
На данном графике отчетливо видно, что время выполнения алгоритма сильно уменьшается с увеличением количества процессов при расчете матрицы любой размерности. При этом наблюдается некоторое снижение производительности в случае наибольших размеров матриц. Данный факт является следствием большого количества данных, необходимого для обмена между процессами. Отсюда опытным путем можно установить, что наибольшая производительность алгоритма будет достигнута, если размерность матрицы не будет превышать 30000-35000.
  
Как видно по показателям SCALAPACK на суперкомпьютерах, обмены при большом n хоть и уменьшают эффективность расчётов, но слабее, чем неоптимальность организации расчётов внутри одного узла. Поэтому, видимо, следует сначала оптимизировать не блочный алгоритм, а подпрограммы, используемые на отдельных процессорах: точечный метод Холецкого, перемножения матриц и др. подпрограммы. [[#Существующие реализации алгоритма|Ниже]] содержится информация о возможном направлении такой оптимизации.
+
=== Динамические характеристики и эффективность реализации алгоритма===
 +
=== Выводы для классов архитектур===
 +
=== Существующие реализации алгоритма===
 +
Алгоритм Ланцоша реализован в различных пакетах, библиотеках и проектах
  
Вообще эффективность метода Холецкого для параллельных архитектур не может быть высокой. Это связано со следующим свойством информационной структуры алгоритма: если операции деления или вычисления выражений <math>a - bc</math> являются не только массовыми, но и параллельными, и потому их вычисления сравнительно легко выстраивать в конвейеры или распределять по устройствам, то операции извлечения квадратных корней являются узким местом алгоритма. Поэтому для эффективной реализации  алгоритмов, столь же хороших по вычислительным характеристикам, как и метод квадратного корня, следует использовать не метод Холецкого, а его давно известную модификацию без извлечения квадратных корней — [[%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%BE%D0%BB%D0%B5%D1%86%D0%BA%D0%BE%D0%B3%D0%BE_(%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D0%BE%D0%B3%D0%BE_%D1%82%D1%80%D0%B5%D1%83%D0%B3%D0%BE%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F)#.5C.28LDL.5ET.5C.29_.D1.80.D0.B0.D0.B7.D0.BB.D0.BE.D0.B6.D0.B5.D0.BD.D0.B8.D0.B5|разложение матрицы в произведение <math>L D L^T</math>]]<ref>Krishnamoorthy A., Menon D. Matrix Inversion Using Cholesky Decomposition. 2013. eprint arXiv:1111.4144</ref>.
+
NAG Library http://www.nag.com/content/nag-library C, C++, Fortran, C#, MATLAB, R
  
=== Существующие реализации алгоритма ===
+
ARPACK https://people.sc.fsu.edu/~jburkardt/m_src/arpack/arpack.html MATLAB
  
Точечный метод Холецкого реализован как в основных библиотеках программ отечественных организаций, так и в западных пакетах LINPACK, LAPACK, SCALAPACK и др.  
+
GrapLab https://turi.com/products/create/open_source.html C++
 
При этом в отечественных реализациях, как правило, выполнены стандартные требования к методу с точки зрения ошибок округления, то есть, реализован режим накопления, и обычно нет лишних операций. Ряд старых отечественных реализаций использует для экономии памяти упаковку матриц <math>A</math> и <math>L</math> в одномерный массив.
 
 
Реализация точечного метода Холецкого в современных западных пакетах обычно происходит из одной и той же реализации метода в LINPACK, а та использует пакет BLAS. В BLAS скалярное произведение реализовано без режима накопления, но это легко исправляется при желании.  
 
  
Интересно, что в крупнейших библиотеках алгоритмов до сих пор предлагается именно разложение Холецкого, а более быстрый алгоритм LU-разложения без извлечения квадратных корней используется только в особых случаях (например, для трёхдиагональных матриц), в которых количество диагональных элементов уже сравнимо с количеством внедиагональных.
+
Gaussian Belief Propagation Matlab Package http://www.cs.cmu.edu/~bickson/gabp/ MATLAB
  
== Литература ==
+
The IETL Project http://www.comp-phys.org/software/ietl/ C++
  
<references \>
+
LANSO/PLANSO http://web.cs.ucdavis.edu/~bai/ET/lanczos_methods/overview_PLANSO.html Fortran
  
[[en:Cholesky decomposition]]
+
Julia Math  https://github.com/JuliaMath/IterativeSolvers.jl Julia
  
[[Категория:Законченные статьи]]
+
==Литература ==
[[Категория:Разложения матриц]]
 

Версия 01:25, 24 января 2017

Авторы: Абдулпотиев А.А.

1 Свойства и структура алгоритма

1.1 Общее описание алгоритма

Алгоритм Лáнцоша является мощным инструментом для решения некоторого класса больших разреженных симметричных спектральных задач [math] Ax = \Lambda x[/math]. Однако любая практическая реализация этого алгоритма страдает от ошибок округления, т.к. векторы Ланцоша теряют взаимную ортогональность. Для того чтобы поддерживать некоторый уровень ортогональности, появились методы полной переортогонализации и выборочной ортогонализации. В этой работе мы рассмотрим последний метод в качестве способа для поддержания ортогональности среди векторов Ланцоша. Он обладает почти столь же высокой точностью, как алгоритм с полной переортогонализацией, и почти столь же низкой стоимостью, как алгоритм без ортогонализации [1].


Дается вещественная симметричная матрица [math]A = A^T[/math],

[math] A = \begin{pmatrix} a_{11} & a_{12} & a_{13} & \cdots & a_{1\ n-1} & a_{1\ n} \\ a_{12} & a_{22} & a_{23} & \cdots & a_{2\ n-1} & a_{2\ n} \\ a_{13} & a_{23} & a_{33} & \cdots & a_{3\ n-1} & a_{3\ n} \\ \vdots & \vdots & \ddots & \ddots & \ddots & \vdots \\ a_{1\ n-1} & \cdots & \cdots & a_{n-2\ n-1} & a_{n-1\ n-1} & a_{n-1\ n} \\ a_{1\ n} & \cdots & \cdots & a_{n-2\ n} & a_{n-1\ n} & a_{n\ n} \\ \end{pmatrix} [/math] [math] \, \; (1), [/math]

случайный вектор [math]b [/math], являющийся первым приближением собственного вектора матрицы, [math]k [/math] - количество собственных значений и собственных векторов, которые мы хотим найти, т.е. количество итераций.


На каждой итерации строится матрица [math]Q_j = [q_1, q_2, \dots, q_j][/math] размерности [math]n \times j[/math], состоящая из ортонормированных векторов Ланцоша [math]z[/math]. В качестве приближенных собственных значений берутся числа Ритца [math]\theta_i [/math], т.е. собственные значения симметричной трехдиагональной матрицы [math]T_j = Q^T_j A Q_j[/math] размерности [math]j \times j[/math].

[math] T_j = \begin{pmatrix} \alpha_1 & \beta_1 \\ \beta_1 & \alpha_2 & \beta_2 \\ & \beta_2 & \ddots & \ddots \\ & & \ddots & \ddots & \beta_{j-1} \\ & & & \beta_{j-1} & \alpha_j \end{pmatrix}\; (2). [/math]

Однако, векторы [math]q_j [/math] теряют ортогональность вследствие приобретения больших компонент в направлениях векторов Ритца [math]y_{i,j} = Q_j v_i [/math], отвечающих сошедшимся числам Ритца [math] \theta_i [/math]. Поэтому чтобы построить [math]q_j [/math], предлагается на каждом шаге следить за оценками погрешностей [math]\beta_{t}|v_i(t)|, i = 1 \dots t, t = j - 1 [/math], где [math]v_i(t) [/math] - [math]t[/math]-я компонента собственного вектора [math]v_i [/math]. И когда какая-то оценка становится слишком малой, проводить ортогонализацию вектора Ланцоша [math]z [/math]. Величина [math]\beta_{t}|v_i(t)| [/math] считается малой, если она меньше, чем [math]\sqrt{\varepsilon}||T_{t}|| [/math], где [math]\varepsilon[/math] - доступная машинная точность чисел.


После следует вычисление собственных значений [math] \theta_j [/math] и собственных векторов [math]v_j [/math] полученной трехдиагональной матрицы [math]T_j[/math], для чего существует, например, метод "разделяй и властвуй"[2]

1.2 Математическое описание алгоритма

[math] \beta_0=0,q_0=0[/math]
[math] q_{1} = \frac{b_{j}}{\|b\|_2}[/math], где [math] \|b\|_2 = \sqrt{\sum\limits_{j=1}^{n} b_j^2}[/math]
[math] for\, j=1\,\, to\, \, k\, \, do:[/math]
    [math]z=Aq_j,  [/math]
    [math]\alpha_j=q_j^Tz, [/math]
    [math]z=z-\alpha_jq_j-\beta_{j-1}q_{j-1},  [/math]
    [math]t = j - 1,  [/math]
    [math]for\, i=1\,\, to\, \, t\, \, do: [/math]
        [math]if\,  \beta_{t}|v_i(t)| \leqslant \sqrt{\varepsilon}\|T_{t}\| \, \,  then:[/math]
            [math]z = z-(y^T_{i,t},z)y_{i,t} [/math], где [math]y_{i,t} = Q_{t}v_i[/math] 
    [math]\beta_{j}=\|z\|_2 [/math]
    [math]q_{j+1}=z/\beta_{j}, [/math]
    Строим матрицу  [math] T_j[/math] (2), вычисляем собственные значения  [math] \theta_j [/math] и собственные векторы [math]v_j [/math] полученной матрицы [math]T_j[/math].

1.3 Вычислительное ядро алгоритма

Выделены следующие вычислительные ядра:

  • Умножение матрицы на вектор, [math]z=Aq_j, [/math].


  • Ортогонализация по отношению к сошедшимся векторам [math]z = z-(y^T_{i,t},z)y_{i,t} [/math] для [math]i = 1 \dots t[/math]


1.4 Макроструктура алгоритма

Были выделены следующие макрооперации:

1. Умножение матрицы на вектор,

[math]z=Aq_j, [/math].

2. Вычисление вектора [math]q_{j+1} [/math] с помощью линейной комбинации других векторов:

[math]\alpha_j=q_j^Tz, [/math]

[math]z=z-\alpha_jq_j-\beta_{j-1}q_{j-1}, [/math]

[math]q_{j+1}=z/\|z\|_2[/math]

3. Ортогонализация вектора Ланцоша с помощью скалярного произведения:

[math]z = z-(y^T_{i,t},z)y_{i,t} [/math]

4. Для вычисления собственных значений матрицы будет использоваться алгоритм "разделяй и властвуй".

1.5 Схема реализации последовательного алгоритма

[math]1)[/math] Инициализируются векторы [math] \beta_0=0,q_0=0,[/math]
[math]2)[/math] Считается норма вектора [math] b: \; \;  \|b\|_2 = \sqrt{\sum\limits_{j=1}^{n} b_j^2},[/math]
[math]3)[/math] Находится первый вектор матрицы [math] Q: \; \; q_{1} = \frac{b_{j}}{\|b\|_2},[/math]
[math]4)[/math] Начинается цикл, повторяющийся [math] k [/math] раз по переменной [math] j: [/math]
   [math]4.1)[/math] Считается произведение матрицы на вектор: [math] z_d = \sum\limits_{y=1}^{n} a_{dy} q_{j_y}, \; d = 1,\,\dots\,, n, [/math] где 
   [math]z_d - [/math] компоненты вектора Ланцоша [math]z, [/math]
   [math]4.2)[/math] Скалярно умножается [math]q_j^T[/math]  и [math] z: \; \; \alpha_j = \sum\limits_{d=1}^{n}q_{j_d} z_d,[/math]
   [math]4.3)[/math] Вычисляется линейная комбинация векторов [math]z=z-\alpha_jq_j-\beta_{j-1}q_{j-1},  [/math]
   [math]4.4)[/math] [math]t[/math] Присваивается [math]j - 1.  [/math] В цикле по [math] i [/math] от первого до посчитанного [math] t[/math]-го собственного вектора проводится
   выборочная ортогонализация к сошедшимся векторам Ритца. То есть:
      [math]4.4.1)[/math] Считается норма матрицы [math]\|T_{t}\|_2, [/math] [math] \| T \|_2 =  \sup\limits_{\| x \|_2 = 1} \| T x \|_2 = \sup\limits_{(x, x) = 1} \sqrt{(Tx, Tx)}[/math], подчиненная векторной норме [math] \| x \|_2 = \sqrt{\sum_{i = 1}^n |x_i|^2} [/math]. Т.е. 
      [math] \|    T_{t} \|_2 =  \max\limits_{i=1 \dots t} \theta_i[/math], где [math] \theta_i[/math] - собственные значения матрицы [math] T,[/math]
      [math]4.4.2)[/math] Берется [math]t[/math]-ю координата вектора [math] v_i[/math]  и проверяется, выполняется ли равенство:[math]\beta_{t}|v_i(t)| \leqslant \sqrt{\varepsilon}\|T_{t}\| \, \, ,[/math]
      Если выполняется, то
          [math]4.4.2.1)[/math] Ищется вектор Ритца [math]y_{i,t_d} = \sum\limits_{x=1}^{t} q_{x_d} v_{i_x}, \; d = 1,\,\dots\,, n,[/math], где  [math]y_{i,t_d}[/math] -  [math]d[/math]-я компонента вектора Ритца [math]y,[/math]
          [math]4.4.2.2)[/math] Скалярно умножается [math]y_{i,t}^T[/math]  и [math] z: \; \; \gamma = \sum\limits_{d=1}^{n}y_{i,t_d} z_d,[/math]
          [math]4.4.2.3)[/math] Вычисляется линейная комбинация векторов [math]z = z-\gamma\,  y_{i,t}, [/math]
          [math]4.4.2.4)[/math] Увеличивается [math]i [/math]. Производится возврат к пункту [math]4.4.2,[/math]
   [math]4.5)[/math] Ищется норма вектора [math] z: \; \; \beta_{j}=\|z\|_2, [/math]
   [math]4.6)[/math] Ищется вектор Ланцоша [math]q_{j+1}=z/\beta_{j}, [/math]
   [math]4.7)[/math] Вычисляется новое собственное значение [math] \theta_j [/math] и собственный вектор [math] v_j [/math] для полученной матрицы [math] T_j,[/math]
   [math]4.8)[/math] Увеличивается [math] j [/math], производится возврат к шагу [math]4.1.[/math]

1.6 Последовательная сложность алгоритма

Рассмотрим последовательную сложность алгоритма.

[math]1)[/math] Инициализируются векторы,
[math]2)[/math] Считается норма вектора: [math] n [/math] сложений и умножений, одно вычисление корня,
[math]3)[/math] Находится первый вектор матрицы: [math] n [/math] делений,
[math]4)[/math] Начинается цикл, повторяющийся [math] k [/math] раз по переменной : [math]   j [/math]:
   [math]4.1)[/math] Считается произведение матрицы на вектор: [math]   n [/math] сложений и умножений,
   [math]4.2)[/math] Скалярно умножается два вектора : [math]   n [/math] сложений и умножений,
   [math]4.3)[/math] Вычисляется линейная комбинация векторов : [math]   2n [/math] вычитаний и умножений,
   [math]4.4)[/math] Повторяется цикл : [math]   j-1 [/math] раз. На каждом из этапов:
      [math]4.4.1)[/math] Считается норма матрицы: : [math]   j-1 [/math] сравнений,
      [math]4.4.2)[/math] Проверяется равенство::  2 умножения, одно сравнение
          [math]4.4.2.1)[/math] Ищется вектор Ритца : [math]   n*(j-1) [/math] сложений и умножений,
          [math]4.4.2.2)[/math] Скалярно умножаются два вектора:  [math]   n [/math] сложений и умножений,
          [math]4.4.2.3)[/math] Вычисляется линейная комбинация векторов : [math]   n [/math] умножений и вычитаний,
          [math]4.4.2.4)[/math] Увеличивается [math]i [/math]. Одно сложение.
     Таким образом, т.к. внутренний цикл повторяется [math]   j-1 [/math] раз, а [math]   j [/math] в свою очередь пробегает от [math]1 \dots k [/math], значит, полное количество операций в пункте [math]4.4)[/math], которое будет занимать весь внутренний цикл будет: сравнений [math] \frac{(k-1)k(k+1)}{3} + \frac{k(k-1)}{2} [/math], умножений: [math] 2\frac{k(k-1)}{2}  + \frac{(k-1)k(k+1)}{3} + \frac{(2n)k(k-1)}{2} [/math], сложений [math]\frac{(k-1)k(k+1)}{3} + \frac{(n+1)k(k-1)}{2}[/math] и вычитаний [math] \frac{(n)k(k-1)}{2}[/math]
   [math]4.5)[/math] Ищется норма вектора: [math] n [/math] сложений и умножений
   [math]4.6)[/math] Ищется вектор Ланцоша [math] n [/math] делений
   [math]4.7)[/math] Увеличивается [math] j [/math], одно сложение. 
   
   Так, пункты  [math]4.1 - 4.3, 4.5 - 4.7[/math] занимают [math] k*(n+n+n+1)[/math] сложений, [math]k(n+n+2n+n)[/math] умножений, [math]2nk[/math] вычитаний и [math]n[/math] делений.



Подводя итог, общее количество операций умножения и деления [math]n + n + 2\frac{k(k-1)}{2}  + \frac{(k-1)k(k+1)}{3} + \frac{(2n)k(k-1)}{2} +  k(n+n+2n+n) + n[/math], т.е. [math]O( k^3+nk^2+k^2+kn+n )[/math]
общее количество сложений и вычитаний: [math] n + \frac{(k-1)k(k+1)}{3} + \frac{(n+1)k(k-1)}{2}+ \frac{(n)k(k-1)}{2} + k*(n+n+n+1) + 2nk[/math], т.е.  [math]O( k^3+nk^2+kn+n )[/math]
И [math] \frac{(k-1)k(k+1)}{3} + \frac{k(k-1)}{2} [/math] сравнений ( [math]O( k^3+k^2)[/math]).


рис. 1: Информационный граф

1.7 Информационный граф

На рисунке (рис. 1) изображен информационный граф. Ниже приведено его описание:

  • Вершины 1 соответствуют операции [math]Aq_j,[/math].
  • Вершины 2 соответствуют операции скалярного произведения [math]q_j^Tz[/math].
  • Вершины 3 соответствуют операции [math]z-\sum\nolimits_{i=1}^{j-1}(z^Tq_i)q_i[/math].
  • Вершины 4 соответствуют операции [math]z-\alpha_jq_j-\beta_{j-1}q_{j-1}[/math].
  • Вершины 5 соответствуют операции [math]||z||[/math].
  • Вершины 6 соответствуют операции [math]z/\beta_j[/math].

1.8 Ресурс параллелизма алгоритма

Хотя алгоритм является итерационным, возможно распараллелить внутри каждой итерации умножение матрицы на вектор и процесс переортогонализации Грамма-Шмидта.

Процесс умножения матриц matvec можно распараллелить несколькими способами[1].

Как видно из графа, для выполнения j-й операции, необходимо выполнить следующие ярусы:

  1. [math]n[/math] ярусов сложений с [math]n[/math] операциями умножения в каждом (вычисление [math]z[/math]);
  2. [math]n[/math] ярусов сложений с [math]1[/math] операцией умножения в каждом (вычисление [math]\alpha_j[/math]);
  3. [math]j[/math] ярусов сложений с [math]n[/math] операциями умножения в каждом, [math]n[/math] ярусов сложений с [math]j[/math] операциями умножения в каждом, [math]1[/math] ярус вычитаний размера [math]n[/math] (первая переортогонализация);
  4. [math]j[/math] ярусов сложений с [math]n[/math] операциями умножения в каждом, [math]n[/math] ярусов сложений с [math]j[/math] операциями умножения в каждом, [math]1[/math] ярус вычитаний размера [math]n[/math] (вторая переортогонализация);
  5. [math]n[/math] ярусов сложений с [math]1[/math] операцией умножения в каждом (вычисление [math]\beta_j[/math]);
  6. [math]1[/math] ярус делений размера [math]n[/math] (вычисление [math]q_{j + 1}[/math]).

Если учесть весь ресурс параллелизма, можно получить мультипликативную вычислительную сложность всего алгоритма: [math]O(n^2k+nk^2)/p)[/math].

Вычислительная мощность данного алгоритма, как отношение числа операций к суммарному объему входных и выходных, данных равняется [math]O(k)[/math].

1.9 Входные и выходные данные алгоритма

Входные данные

Матрица [math] A [/math], начальный вектор [math] b [/math] и число собственных значений, которые мы хотим найти [math] k [/math].

Ввиду симметричности из матрицы [math] A [/math] достаточно хранить только ее диагональ и элементы, лежащие выше ее. Таким образом, объем входных данных:

[math]\frac{n (n + 1)}{2} + n + 1 = \frac{n^2+3n+2}{2}.[/math]

Выходные данные

Матрица собственных значений [math] \Lambda = diag(\theta_1 \dots \theta_k) [/math], матрица собственных векторов [math] V = [v_1 \dots v_k] [/math] размера [math] n \times k [/math]

Так, объем выходных данных: [math] k + kn [/math]

1.10 Свойства алгоритма

Если [math]A[/math] эрмитова матрица, то алгоритм Ланцоша и Bi-Lanczos сходятся к одинаковым трехдиагональным матрицам Ритца[2].

При реализации классического алгоритма Ланцоша возникает большая погрешность при округлении. Вариант с полной переортогонализацией позволяет избегать больших погрешностей, однако является более ресурсоемким. Вариант с частичной переортогонализацией является промежуточным.

Алгоритм может завершить свою работу досрочно, когда найденные собственные значения будут достаточно близки к целевым.

2 Программная реализация алгоритма

2.1 Особенности реализации последовательного алгоритма

2.2 Локальность данных и вычислений

2.3 Возможные способы и особенности параллельной реализации алгоритма

2.4 Масштабируемость алгоритма и его реализации

Исследование масштабируемости реализации алгоритма проводилось с использованием стандарта MPI на суперкомпьютере "Ломоносов".

Набор изменяемых параметров запуска реализации алгоритма и границы значений параметров алгоритма:

  • Число процессоров 4, 8, 16, 32, 64, 128.
  • Размерность матрицы от 1000 до 50000

Сборка осуществлялась с параметрами:

  • openmpi/1.5.5-icc
  • intel/13.1.0
Time dependance.jpg

Используемая параллельная реализация алгоритма на языке C++

На данном графике отчетливо видно, что время выполнения алгоритма сильно уменьшается с увеличением количества процессов при расчете матрицы любой размерности. При этом наблюдается некоторое снижение производительности в случае наибольших размеров матриц. Данный факт является следствием большого количества данных, необходимого для обмена между процессами. Отсюда опытным путем можно установить, что наибольшая производительность алгоритма будет достигнута, если размерность матрицы не будет превышать 30000-35000.

2.5 Динамические характеристики и эффективность реализации алгоритма

2.6 Выводы для классов архитектур

2.7 Существующие реализации алгоритма

Алгоритм Ланцоша реализован в различных пакетах, библиотеках и проектах

NAG Library http://www.nag.com/content/nag-library C, C++, Fortran, C#, MATLAB, R

ARPACK https://people.sc.fsu.edu/~jburkardt/m_src/arpack/arpack.html MATLAB

GrapLab https://turi.com/products/create/open_source.html C++

Gaussian Belief Propagation Matlab Package http://www.cs.cmu.edu/~bickson/gabp/ MATLAB

The IETL Project http://www.comp-phys.org/software/ietl/ C++

LANSO/PLANSO http://web.cs.ucdavis.edu/~bai/ET/lanczos_methods/overview_PLANSO.html Fortran

Julia Math https://github.com/JuliaMath/IterativeSolvers.jl Julia

3 Литература

  1. "The Lanczos Algorithm With Partial Reorthogonalization", Horst D. Simon, Mathematics of computation, volume 42, number 165, pages 115-142 (1984)
  2. Дж. Деммель «Вычислительная линейная алгебра», c. 232, алгоритм 5.2